[PHP] Rekensommen genereren

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • mcdronkz
  • Registratie: Oktober 2003
  • Laatst online: 16-04 12:44
Ik ben aan 't hobbyen binnen PHP met een methode die uiteindelijk een rekensom + antwoord moet gaan genereren, waarbij de som is verwerkt in een gewone vraag. Een voorbeeld van zo'n vraag kan zijn:

"Jan heeft %d voetbalplaatjes, Piet steelt er %d en de politie geeft er vervolgens %d terug. Hoeveel voetbalplaatjes heeft Jan nu?"

Zoals je ziet is het optellen en aftrekken gemengd in de vragen en dat maakt het best pittig.

Ik zet alle sommen met bijbehorende attributen in een CSV bestand, met de volgende indeling:

code:
1
vraag;maximale waarde gegenereerd getal;sleutel;niveau;geslacht


De vraag spreekt voor zich, hierin zou letterlijk het eerder genoemde voorbeeld kunnen staan.

Er worden random getallen gegenereerd die ik een maximale waarde mee wil geven, zo kan Piet niet ineens 23528 voetbalplaatjes stelen, 20 lijkt me realistischer maar dat verschilt per vraag, vandaar.

De sleutel ziet er in deze vraag als volgt uit: %d-%d+%d. Er wordt eerst een waarde van het begingetal afgetrokken, en vervolgens wordt er iets bij opgeteld. Op deze wijze kan ik vragen zo uitgebreid en ingewikkeld maken als ik zelf wil. Maar hier zit ook meteen m'n probleem.

Laten we zeggen dat Jan begint met 20 voetbalplaatjes. Piet steelt er 18 en Jan krijgt er 2 terug. Uitkomst is 4. 't Kan ook zijn dat Jan begint met 2 voetbalplaatjes, Piet er vervolgens 7 steelt en de politie er 19 terugbrengt. Da's een beetje raar natuurlijk, en daarvoor moet ik een aantal controles doen en de sommen stap voor stap doorlopen.

Goed, logisch denken is niet m'n sterkste kant (ik werk eraan) dus ik zie er een beetje tegenop, maar het is wel een oefening en vooral uitdaging om het voor elkaar te krijgen.

Hoe kan ik dit probleem ondervangen ? Ik dacht zelf hieraan:

- Bij elke stap controleren of de tussenuitkomst niet onder de nul uitkomt.
- In het geval van de voetbalplaatjes controleren of de tussenuitkomst niet boven het begingetal uitstijgt. Maar goed, dit geldt natuurlijk niet voor alle situaties dus dat is een attribuut die ik aan bepaalde vragen moet toevoegen.

Ik zit ook met het punt van het uitrekenen van de sommen. Is de beste methode hiervoor eval() met output buffering? Dit lijkt me één van de weinige situaties waarin eval() wenselijk is.

Ik snap dat dit topic meer met boerenverstand te maken heeft dan met programmeerkennis. Toch wou ik 'm posten omdat ik op dit moment een beetje vastloop en graag wat frisse input wil hebben waardoor ik er wat luchtiger over na kan denken.

Acties:
  • 0 Henk 'm!

  • BtM909
  • Registratie: Juni 2000
  • Niet online

BtM909

Watch out Guys...

"Jan heeft %d voetbalplaatjes, Piet steelt er %d en de politie geeft er vervolgens %d terug. Hoeveel voetbalplaatjes heeft Jan nu?"
Wat dacht je van een formule maken van je probleem ;)

Vervolgens bepaal je min en max marges voor elke onderdeel (sommige gaan relatief worden) en gebruik je een random generator om het voor je in te vullen :)

x - y + z (maar daar was je al achter)

Vervolgens zeg je

x = random tussen 3 en 100
y = random tussen 0 en x (tenzij Piet er altijd minimaal 1 moet stelen)
z = random tussen 0 en y

problem solved :P

Ace of Base vs Charli XCX - All That She Boom Claps (RMT) | Clean Bandit vs Galantis - I'd Rather Be You (RMT)
You've moved up on my notch-list. You have 1 notch
I have a black belt in Kung Flu.


Acties:
  • 0 Henk 'm!

Anoniem: 76287

Database:

1. "Jan heeft $a voetbalplaatjes, Piet steelt er $b en de politie geeft er vervolgens $c terug. Hoeveel voetbalplaatjes heeft Jan nu?"
2. $a = rand(1, 20)
3. $b = rand(0, $a)
4. $c = rand(0, 20)
5. $totaal = $a - $b + $c

Wat je met 'sleutel', 'niveau' en 'geslacht' in je CSV / database bedoelt, begrijp ik niet.

Acties:
  • 0 Henk 'm!

  • Woutrrr
  • Registratie: Februari 2007
  • Laatst online: 05-06 16:23
Is het niet beter om:

$c = rand(0, $b);

te doen?
anders kan de politie er meer teruggeven dan er gestolen zijn.

Acties:
  • 0 Henk 'm!

  • stefanmarchal
  • Registratie: Februari 2005
  • Laatst online: 19:20
te laat

[ Voor 97% gewijzigd door stefanmarchal op 26-03-2009 23:52 . Reden: Woutrrr was eerder ]

||


Acties:
  • 0 Henk 'm!

  • mcdronkz
  • Registratie: Oktober 2003
  • Laatst online: 16-04 12:44
Niveau en geslacht mag je voor het gemak even vergeten, de sleutel is de formule zoals BtM909 al verklapt.

Nu ik er zo overna zit te denken, zou het niet veel makkelijker zijn om de vragen als volgt in het CSV bestand te zetten? :

"Jan heeft {1,20} voetbalplaatjes, Piet steelt er {1,(0)} en de politie geeft er vervolgens {1,(1)} terug. Hoeveel voetbalplaatjes heeft Jan nu?"

(0) en (1) zijn dan referenties naar eerder gegenereerde getallen. Hoe ik dat precies ga aanpakken weet ik ook nog niet, maar dat klinkt in elk geval een stuk makkelijker dan stap voor stap de sommen doorlopen op een achterlijk complexe manier zoals ik dat eerst voor ogen had.

Zo'n forum helpt echt geloof ik, ik heb er weer een beetje vertrouwen in.

Als iemand een handigere oplossing ziet, graag.

Acties:
  • 0 Henk 'm!

  • ReLight
  • Registratie: Augustus 2001
  • Laatst online: 13:56

ReLight

echo("What Now ? !")

Simpeler imho.
Je verteld de Random reneratie dat y = Random(0,x) en z = Random(0,y)

Is bijna net als door tygetjuh hierboven, maar ik ga ervan uit dat de politie niet meer plaatjes terugbrengt dan dat Piet gejat heeft, wellicht wel minder.

Mijn zoon & dochter zijn de toekomst, de rest is tijdsvermaak. Home assistant & & Nibe S2125-12/SMO-S40, RMU-s40 & Tado - Volvo C40 ER, SE


Acties:
  • 0 Henk 'm!

  • Bergen
  • Registratie: Maart 2001
  • Laatst online: 28-05 17:02

Bergen

Spellingscontroleur

In de zin komen 3 getallen te staan: a, b en c. Het lijkt mij dat geen enkele 0 mag zijn, anders wordt de som te gemakkelijk. Dus laten we zeggen dat elk getal minimaal 1 moet zijn.

PHP:
1
2
3
4
5
6
7
$maximum = 20;

$a = rand(1, $maximum); // bijvoorbeeld $a = 8
$b = rand(1, $a);       // wordt dan tussen 1 en 8, bijvoorbeeld 5
$c = rand(1, $b);       // wordt dan tussen 1 en 5

echo "Jan heeft $a voetbalplaatjes, Piet steelt er $b en de politie geeft er vervolgens $c terug. Hoeveel voetbalplaatjes heeft Jan nu?";

Zo bedoel je?

Acties:
  • 0 Henk 'm!

  • mcdronkz
  • Registratie: Oktober 2003
  • Laatst online: 16-04 12:44
Bergen schreef op vrijdag 27 maart 2009 @ 00:04:
In de zin komen 3 getallen te staan: a, b en c. Het lijkt mij dat geen enkele 0 mag zijn, anders wordt de som te gemakkelijk. Dus laten we zeggen dat elk getal minimaal 1 moet zijn.

PHP:
1
2
3
4
5
6
7
$maximum = 20;

$a = rand(1, $maximum); // bijvoorbeeld $a = 8
$b = rand(1, $a);       // wordt dan tussen 1 en 8, bijvoorbeeld 5
$c = rand(1, $b);       // wordt dan tussen 1 en 5

echo "Jan heeft $a voetbalplaatjes, Piet steelt er $b en de politie geeft er vervolgens $c terug. Hoeveel voetbalplaatjes heeft Jan nu?";

Zo bedoel je?
Dat klopt inderdaad. Methodiek is helemaal juist en met dat inzicht ben ik op deze manier ook wel geholpen. Het enige waar ik mee zit is dat er net zo goed zestien getallen in een som kunnen zitten en het niet vast staat hoe hoog een getal moet zijn en of er opgeteld of afgetrokken moet worden. Ik moet dus een soort van logisch systeem bedenken zodat ik het niet allemaal hoef te hardcoden. Ik denk dat het maken van referenties naar eerder gegenereerde getallen goed gaat werken, zo kun je alles best compact en overzichtelijk houden in het CSV bestand.

Acties:
  • 0 Henk 'm!

Anoniem: 76287

mcdronkz schreef op vrijdag 27 maart 2009 @ 00:08:
[...]


Dat klopt inderdaad. Methodiek is helemaal juist en met dat inzicht ben ik op deze manier ook wel geholpen. Het enige waar ik mee zit is dat er net zo goed zestien getallen in een som kunnen zitten en het niet vast staat hoe hoog een getal moet zijn en of er opgeteld of afgetrokken moet worden. Ik moet dus een soort van logisch systeem bedenken zodat ik het niet allemaal hoef te hardcoden. Ik denk dat het maken van referenties naar eerder gegenereerde getallen goed gaat werken, zo kun je alles best compact en overzichtelijk houden in het CSV bestand.
Gewoon zo:
PHP:
1
2
3
4
5
<?php
$maximum = 20; //elke som heeft een maximum
echo "Jan heeft $a = rand(1, $maximum) voetbalplaatjes, Piet steelt er $b = rand(1, $a) en de politie geeft er vervolgens $c = rand(1, $b) terug. Hoeveel voetbalplaatjes heeft Jan nu?"; //elke som heeft een tekst
echo $totaal = $a - $b + $c //elke som heeft een correct antwoord.
?>


Oftewel: Het maakt niet uit hoeveel variabelen er in een som zitten. Elke som heeft een maximum waarde, een tekst waarin de variabelen gegenereerd worden en een uitkomst.

Acties:
  • 0 Henk 'm!

  • Bergen
  • Registratie: Maart 2001
  • Laatst online: 28-05 17:02

Bergen

Spellingscontroleur

Zo'n soort template achtig iets in een csv bestand is dan ideaal inderdaad en goed te doen ook. Ik zou echter wel eerst een mooie flowchart maken en niet in het wilde weg gaan zitten coden. Anders raak je heel snel het overzicht kwijt en ben je veel te veel tijd kwijt aan onnodig debuggen.

Iemand met jarenlange programmeerervaring schudt het misschien zo uit zijn mouw, maar als je nog een beetje wegwijs moet worden in het programmeren, is dit een pittige opgave.

Acties:
  • 0 Henk 'm!

  • masq
  • Registratie: September 2004
  • Laatst online: 18-04 00:18
Ik kon het niet laten :P

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php

define("MIN", "-");
define("PLUS", "+");

$formule = "a - b + c - d";
$vraag = "Jan heeft %d voetbalplaatjes, Piet steelt er %d en de politie geeft er vervolgens %d terug. De volgende dag jat Piet er nog eens %d. Hoeveel voetbalplaatjes heeft Jan nu?";
$max = 30; // Maximale startgetal
$min = 1;  // Minimaal optellen/aftrekken

$operators = str_split(ereg_replace("[^+-]", "", $formule));
$start = rand($min, $max);
$getallen = array($start);
$uitkomst = $start;

foreach ($operators as $operator) {
  if ($operator == MIN) {
    $getal = ($uitkomst > 0) ? rand($min, $uitkomst) : 0;
    $uitkomst -= $getal;
  } elseif ($operator == PLUS) {
    $getal = ($uitkomst < $start) ? rand($min, $start - $uitkomst) : 0; // nooit meer optellen dan verschil startgetal en tussentijdse uitkomst
    $uitkomst += $getal;
  }
  $getallen[] = $getal;
}

vprintf($vraag, $getallen);
print("\nAntwoord: $uitkomst");

?>


Redelijk naïeve methode maar zo hoef je iig geen evileval te gebruiken.

Acties:
  • 0 Henk 'm!

Anoniem: 76287

Het is leuk dat je een lap met code plaatst, maar kan je misschien ook uitleggen wat de meerwaarde is? Volgens mij wordt het foreach gedeelte véél te uitgebreid als je een verscheidenheid aan formules wilt gebruiken.

Acties:
  • 0 Henk 'm!

  • Woy
  • Registratie: April 2000
  • Niet online

Woy

Moderator Devschuur®
Ik denk dat je eerst moet bedenken welke gegevens je bij alle sommen hebt.

Je hebt bij elke som die je wilt genereren:
  • De Formule ( met variabelen )
  • Het Verhaaltje ( met placeholders voor de variabelen )
  • Constraints waar de variabelen aan moeten voldoen
Nu je dus weet welke gegevens je hebt, ga je dat omzetten naar code om die data te verwerken.

Voor het parsen en uitrekenen van de som kun je zelf een expression tree bouwen aan de hand van je input.

Het verhaal zou je kunnen opslaan in de vorm van een string die je aan printf kan voeren.

Voor je constraints zul je eerst goed moeten bedenken wat voor type constraints je allemaal hebt, en hoe je die makkelijk kunt representeren zodat je die in je programm kunt gebruiken.

[ Voor 34% gewijzigd door Woy op 27-03-2009 11:48 ]

“Build a man a fire, and he'll be warm for a day. Set a man on fire, and he'll be warm for the rest of his life.”


Acties:
  • 0 Henk 'm!

  • masq
  • Registratie: September 2004
  • Laatst online: 18-04 00:18
Anoniem: 76287 schreef op vrijdag 27 maart 2009 @ 11:26:
Het is leuk dat je een lap met code plaatst, maar kan je misschien ook uitleggen wat de meerwaarde is?
Ik had nog niemand een concrete oplossing zien geven voor het probleem van het variabele aantal getallen in de formule. En een illustratie van het feit dat je geen eval nodig hebt voor het uitrekenen van de uitkomst dus.

Verder heeft Woy gelijk, de beste oplossing hangt af van de complexiteit van de formules en de constraints. Interessante variatie op het gegeven verhaaltje: wat nu als Jan 1 kaartje ruilt tegen 2 of meer kaartjes van een ander? In dat geval krijgt hij er wel meer terug dan hij kwijt is.

Acties:
  • 0 Henk 'm!

  • mcdronkz
  • Registratie: Oktober 2003
  • Laatst online: 16-04 12:44
Zo dan, 't werkt :D.

Hieronder mijn oplossing. Sommige dingen pak ik misschien een beetje lelijk aan, commentaar klopt soms niet helemaal maar ik heb er veel van geleerd en het werkt prima.

PHP:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
<?php
function genereerVraag_OptellenAftrekken($niveau, $geslacht)
{   
    /*
    CSV bestand met templates voor vragen uitlezen en elke regel in een array plaatsen.
    */
    $bestand = file("./vragen.csv");
    /*
    Elke regel van de array doorlopen.
    */
    $aantal_vragen = count($bestand);
    for($i = 0; $i < $aantal_vragen; $i++)
    {
        /*
        Elk stuk van de regel dat met een puntkomma gescheiden is wordt gesepareerd.
        */
        $vraag = explode(";", $bestand[$i]);
        /*
        Controleren of de vraag voldoet aan het niveau en het geslacht
        */
        if(trim($vraag[2]) == $niveau && trim($vraag[3]) == $geslacht)
        {
            /*
            Elke vraag met alle eigenschappen in een tweedimensionale array plaatsen.
            */
            $vragen[$i]['vraag'] = trim($vraag[0]);
            $vragen[$i]['formule'] = trim($vraag[1]);
            $vragen[$i]['niveau'] = trim($vraag[2]);
            $vragen[$i]['geslacht'] = trim($vraag[3]);
        }
    }
    
    /*
    Een willekeurige vraag uit de tweedimensionale array selecteren.
    */
    $vraag = $vragen[rand(0, count($vragen) - 1)];
    
    /*
    Alle te vervangen placeholders uit de vraag halen.
    */
    global $getallen;
    preg_match_all("/\((.*),(.*)\)/U",$vraag['vraag'],$getallen);
        
    $aantal_getallen = count($getallen[0]);
    
    /*
    De placeholders zijn uit de vraag gehaald, nu moeten ze vervangen worden door een willekeurig getal.
    Dit getal kan ook een verwijzing hebben naar een eerder gegenereerd getal. Bijvoorbeeld: (1,[0]), dit
    genereert een getal tussen de 1 en de waarde die het eerst gegeneerde getal heeft.
    */
    for($i = 0; $i < $aantal_getallen; $i++)
    {   
        $patronen = array("/\(([0-9]+),([0-9]+)\)/Ue","/\(([0-9]+),\[(.*)\]\)/Ue");
        $vervangingen = array("rand(\"\\1\",\"\\2\")","rand(\"\\1\",\$getallen[0][\\2])");
        $getallen[0][$i] = preg_replace($patronen,$vervangingen,$getallen[0][$i]);
    }
        
    /*
    De getallen moeten stap voor stap vervangen worden omdat er soms ook getallen op een latere positie gebruikt moeten worden
    die al eerder gegenereerd zijn. Het is dus niet mogelijk om alles met één reguliere expressie op te lossen. Om ervoor te zorgen
    dat het mogelijk is om stap voor stap te kunnen vervangen met een reguliere expressie wordt er van onderstaande callback-functie
    gebruik gemaakt.
    */
    $teller = 0;
    $vraag['vraag'] = preg_replace_callback("/\((.*)\)/U",create_function("\$matches", "global \$teller; global \$getallen; \$teller++; return \$getallen[0][\$teller - 1];"),$vraag['vraag']);
    
    /*
    Getallen in formule vervangen
    */
    $vraag['formule'] = preg_replace("/\[(.*)\]/Ue","\$getallen[0][\\1]",$vraag['formule']);    
    eval("\$vraag['antwoord'] = ".$vraag['formule'].";");
    
    return $vraag;      
}

print "<pre>";
print_r(genereerVraag_OptellenAftrekken(3,"man"));
?>

Acties:
  • 0 Henk 'm!

  • HaTe
  • Registratie: Mei 2007
  • Laatst online: 20:57

HaTe

haat niet

Gewoon een functie maken:
function maak_vraag($vraag, $formule)
{
//verwerking
}

//output:
echo maak_vraag("Jan heeft %a voetbalplaatjes, Piet steelt er %b en de politie geeft er vervolgens %c terug. Hoeveel voetbalplaatjes heeft Jan nu?", "%a-%b+%c");

//edit
Ow, ik zie dat je al zoiets had :p

[ Voor 8% gewijzigd door HaTe op 27-03-2009 23:41 ]

WP: ME PUHZ-SW75YAA + ERST30D-VM2ED | Solar: 17x TSM-340-DE06M.08 (5780Wp ~6200kWh), Azimuth 179°, Hellingshoek: 34° | PC specs


Acties:
  • 0 Henk 'm!

  • TvdW
  • Registratie: Juli 2007
  • Laatst online: 30-08-2021
code:
1
global $getallen;

waarom maak je deze global?


code:
1
$vraag = $vragen[rand(0, count($vragen) - 1)];

beter :
code:
1
$vraag = $vragen[array_rand($vragen)];


Tom

Acties:
  • 0 Henk 'm!

  • Patriot
  • Registratie: December 2004
  • Laatst online: 14:43

Patriot

Fulltime #whatpulsert

TvdW schreef op zaterdag 28 maart 2009 @ 00:51:
code:
1
global $getallen;

waarom maak je deze global?
Misschien omdat het een global betreft, die niet zonder meer beschikbaar is in de scope van de functie?

Acties:
  • 0 Henk 'm!

  • mcdronkz
  • Registratie: Oktober 2003
  • Laatst online: 16-04 12:44
TvdW schreef op zaterdag 28 maart 2009 @ 00:51:
code:
1
global $getallen;

waarom maak je deze global?


code:
1
$vraag = $vragen[rand(0, count($vragen) - 1)];

beter :
code:
1
$vraag = $vragen[array_rand($vragen)];


Tom
Ik maak $getallen global omdat 'ie anders buiten de scope valt van de callbackfunctie. Ik vind het alleen wel een beetje raar dat ik 'm in de callbackfunctie zelf ook nog als global moet definiëren, wie kan me vertellen hoe dat komt ?

array_rand() kende ik niet, bedankt!

Acties:
  • 0 Henk 'm!

  • Patriot
  • Registratie: December 2004
  • Laatst online: 14:43

Patriot

Fulltime #whatpulsert

mcdronkz schreef op zaterdag 28 maart 2009 @ 01:03:
[...]


Ik maak $getallen global omdat 'ie anders buiten de scope valt van de callbackfunctie. Ik vind het alleen wel een beetje raar dat ik 'm in de callbackfunctie zelf ook nog als global moet definiëren, wie kan me vertellen hoe dat komt ?
Omdat je hem niet meegeeft als parameter aan de anonieme functie.

EDIT:
Klopt niet helemaal wat ik zeg. Je moet hem daar in ieder geval nog een keer global maken omdat je in de anonieme functie in een andere scope terecht bent gekomen.

[ Voor 47% gewijzigd door Patriot op 28-03-2009 01:13 ]


Acties:
  • 0 Henk 'm!

  • TvdW
  • Registratie: Juli 2007
  • Laatst online: 30-08-2021
Oeps, over het hoofd gezien.

Acties:
  • 0 Henk 'm!

  • mcdronkz
  • Registratie: Oktober 2003
  • Laatst online: 16-04 12:44
Patriot schreef op zaterdag 28 maart 2009 @ 01:10:
[...]


Omdat je hem niet meegeeft als parameter aan de anonieme functie.

EDIT:
Klopt niet helemaal wat ik zeg. Je moet hem daar in ieder geval nog een keer global maken omdat je in de anonieme functie in een andere scope terecht bent gekomen.
Maar 't heeft er volgens mij niet zoveel mee te maken of 't al dan niet een anonieme functie is. Ik heb namelijk even nog een functie geschreven die alleen maar print_r($getallen) doet, en dan krijg ik ook een notice undefined variable. Het is dus blijkbaar zo dat wanneer je een variabele global maakt, deze dan weer niet binnen andere functies beschikbaar is. Buiten alle functies kan ik $getallen wél gewoon bereiken namelijk.

Kortweg: als je een variabele wilt gebruiken in een method die je in een andere method hebt aangemaakt zul je 'm global moeten maken in de method waar de variabele oorspronkelijk werd gedefinieerd, en óók in de andere method. Snap je het nog ? Ik wel.

[ Voor 13% gewijzigd door mcdronkz op 28-03-2009 01:21 ]

Pagina: 1