[PHP] for lus met rand herhaald zich na een tijdje.

Pagina: 1
Acties:

Onderwerpen


Acties:
  • 0 Henk 'm!

  • Tha Ertenal
  • Registratie: September 2002
  • Laatst online: 31-05-2022
Voor een website heb ik een programma geschreven die random in verschillende talen events op datum in een database zet. Tot 100 events gaat dit goed, maar vanaf een bepaald getal tussen de 100 en 500 gaat het mis. Dan herhaald hij simpelweg hetzelfde patroon, wat resulteert in precies dezelfde rijen.

Dan krijg ik dus dit:

id: titel: jaar:
5 lorem ipsum 1956
432 lorem ipsum 1956

Dus ik dacht, oke, daar zit een patroon in. Zouden nummertje 6 en 433 dan ook hetzelfde zijn? En ja dat is ook waar..

het id wordt btw door mysql zelf toegevoegd.

Is dit een bug van php?

AMD Phenom II X6 1090T | 2x 4GB Kingston | Geforce GTX 560TI | Creative I-Trigue L3450


Acties:
  • 0 Henk 'm!

  • storeman
  • Registratie: April 2004
  • Laatst online: 00:28
Hier kunnen we niets mee zonder wat relevante code.

"Chaos kan niet uit de hand lopen"


Acties:
  • 0 Henk 'm!

  • Spiked
  • Registratie: Mei 2008
  • Laatst online: 17-09 15:30
Wellicht gebruik je PHP4 (<4.2.0) en moet je de randomizer eerst seeden met srand()

Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 21:27

Creepy

Tactical Espionage Splatterer

Je zou ook de eerste niet zijn die juist srand() aanroept in elke loop iteratie ;) Maar zonder je daadwerkelijke implementatie van het random gebeuren kunnen we daar niet zoveel zinnigs over zeggen zoals storeman al aangeeft.

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • Crayne
  • Registratie: Januari 2002
  • Laatst online: 17-03 13:41

Crayne

Have face, will travel

Mijn ervaring met rand() (zonder te hoeven seeden) is dat de nummers zich na verloop van tijd gaan herhalen, ja.
mt_rand() heeft dat probleem in mindere mate of niet (die discussie ga ik hier niet aan ;)).

rand() vs mt_rand():
http://portfolio.technoized.com/notes/26

Dit noemt men dus overigens anecdotal evidence, dus wellicht praat ik nu poep, maar het is het proberen waard lijkt me.

Edit: heb zelf even wat tests gedaan en het hangt natuurlijk helemaal af van de range van getallen die je door de functie laat genereren (een kleiner bereik met een hoger aantal iteraties levert natuurlijk veel meer dubbele resultaten op), maar als ik de volgende code gebruik, krijg ik weinig verschillen te zien wanneer ik de verschillende combinaties probeer (hoog aantal iteraties/grote range, hoog aantal iteraties/kleine range, klein aantal iteraties/kleine range, klein aantal iteraties/grote range.

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
$iterations = 10000;

$knownA = array();
$countDoublesA = 0;

for ($i = 0; $i < $iterations; $i++)
{

    $tmpNumA = rand(1, 10000000);
    
    if (!in_array($tmpNumA, $knownA))
    {
    
        $knownA[] = $tmpNumA;
        
    }
    else
    {
    
        $countDoublesA++;
        
    }
    
}
        
$knownB = array();
$countDoublesB = 0;

for ($x = 0; $x < $iterations; $x++)
{

    $tmpNumB = mt_rand(1, 10000000);
    
    if (!in_array($tmpNumB, $knownB))
    {
    
        $knownB[] = $tmpNumB;
        
    }
    else
    {
    
        $countDoublesB++;
        
    }
    
}

echo "<b>rand()</b>";
echo "Aantal unieke waarden gegenereerd over ".$iterations." iteraties met rand(): ".count($knownA)."<br>";
echo "Aantal dubbele waarden gegenereerd over ".$iterations." iteraties met rand(): ".$countDoublesA."</p>";

echo "<p><b>mt_rand()</b>";
echo "Aantal unieke waarden gegenereerd over ".$iterations." iteraties met mt_rand(): ".count($knownB)."<br>";
echo "Aantal dubbele waarden gegenereerd over ".$iterations." iteraties met mt_rand(): ".$countDoublesB."</p>";

[ Voor 66% gewijzigd door Crayne op 10-11-2009 19:54 ]

Mijn Library Thing catalogus


Acties:
  • 0 Henk 'm!

  • storeman
  • Registratie: April 2004
  • Laatst online: 00:28
@crayne:

Je vergelijking is de huidige code niet erg eerlijk omdat je in de bovenste loop tot 1000000 doet en in de onderste tot 1000.

Bij beide methoden zit ik op ongeveer 64% unieke getallen als je dezelfde range gebruikt.

Door iets vies te doen, kreeg ik bijna allemaal unieke >99%:

PHP:
1
2
3
<?php
$tmpNumA = round( rand(1, $iterations) * rand() );
?>

"Chaos kan niet uit de hand lopen"


Acties:
  • 0 Henk 'm!

  • Tha Ertenal
  • Registratie: September 2002
  • Laatst online: 31-05-2022
Hmm, daar heb ik nog nooit van gehoort.. wel raar eigenlijk dat zo'n standaard rand functie toch een patroon opleverd.

Crayne wat ik uit jou code begrijp is dat je een simpel getal wil hebben en kijkt of deze al eerder is voorgekomen. Dat is opzich wel mogelijk want je zou elke keer dezelfde procentuele kans moeten hebben. (oftewel, altijd hetzelfde getal 4 moet in theorie mogelijk zijn).
Wat mijn probleem alleen was dat hij een bepaald patroon volgde. Dus bijvoorbeeld eerst 3 dan 5 dan 6, dan weer 3 dan 5 dan 6 om het makkelijk te zeggen.

De code zag er alsvolgt uit:

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
for($i=0; $i<1000; $i++) {

    $title_en = '';
    $title_fr = '';
    $title_de = '';
    $title_es = '';
    $title_nl = '';
    
    // Generate random titles in multilanguage
    $forlus = rand(8, 20);
    for($j=0; $j<$forlus; $j++) {
        $title_en .= $lorem_en[rand(0,count($lorem_en))]." ";
        $title_fr .= $lorem_fr[rand(0,count($lorem_fr))]." ";
        $title_de .= $lorem_de[rand(0,count($lorem_de))]." ";
        $title_es .= $lorem_es[rand(0,count($lorem_es))]." ";
        $title_nl .= $lorem_nl[rand(0,count($lorem_nl))]." ";
    }
    
    $geonameId = rand(2050000, 3050405);

    $randommaand = rand(1,12);
    $month = sprintf("%02d", $randommaand );
    $randomdag = rand(1,28);
    $day = sprintf("%02d", $randomdag );
    $year = rand(1200,2009);
    
    
    
    $historycat = rand(0,6);
    
    $getal++;
    $importid = sprintf("%05d", $getal );
    $importid = "historytestdata".$importid.";";
    
    echo "'".$title_en."', '".$title_fr."', '".$title_de."', '".$title_es."', '".$title_nl."', '".$geonameId."', '".$day."', '".$month."', '".$year."', '".$historycat."', '".$importid."'<br />";
}


Op deze manier kreeg ik na een tijdje precies hetzelfde patroon als in het begin. Bij 1000x had elke rij ongeveer wel een dubbelganger.

Ik heb nu alle functie's vervangen door mt_rand, en dit geeft geen dubbelgangers. Maar ik blijf het raar vinden waarom php dit dan toch als standaard functie houdt.

AMD Phenom II X6 1090T | 2x 4GB Kingston | Geforce GTX 560TI | Creative I-Trigue L3450


Acties:
  • 0 Henk 'm!

  • Laurens-R
  • Registratie: December 2002
  • Laatst online: 29-12-2024
wel raar eigenlijk dat zo'n standaard rand functie toch een patroon opleverd
Dat is niet zo raar, omdat geen enkele random number generator echt random is. Er zit altijd een vorm van structuur in, waarbij zaken als seeds etc gebruikt worden om zo random mogelijk te laten lijken.

De kunst bij random nummer generatie is om het herhalingspatroon zoveel mogelijk te voorkomen en het bereik van potentiele 'random resultaten' zo breed mogelijk te maken en ook gelijkmatig te verspreiden.

Een goed (klassiek) boek wat dit zeer diepgaand behandeld is The Art Of Programming Volume 2 door Knuth. Hoewel sommige punten in dit boek niet even actueel zijn, staat het verhaal algemeen genomen nog steeds als een huis.

[ Voor 19% gewijzigd door Laurens-R op 10-11-2009 15:24 ]


Acties:
  • 0 Henk 'm!

  • Creepy
  • Registratie: Juni 2001
  • Laatst online: 21:27

Creepy

Tactical Espionage Splatterer

De kunst bij random nummer generatie is om het herhalingspatroon zoveel mogelijk te voorkomen en het bereik van potentiele 'random resultaten' zo breed mogelijk te maken en ook gelijkmatig te verspreiden.
En gezien de comments bij de rand() functie (http://php.net/manual/en/function.rand.php) gebeurt dat niet goed als je een min en max opgeeft.

[ Voor 6% gewijzigd door Creepy op 10-11-2009 15:41 ]

"I had a problem, I solved it with regular expressions. Now I have two problems". That's shows a lack of appreciation for regular expressions: "I know have _star_ problems" --Kevlin Henney


Acties:
  • 0 Henk 'm!

  • storeman
  • Registratie: April 2004
  • Laatst online: 00:28
Creepy schreef op dinsdag 10 november 2009 @ 15:40:
[...]

En gezien de comments bij de rand() functie (http://php.net/manual/en/function.rand.php) gebeurt dat niet goed als je een min en max opgeeft.
Dat zou ook verklaren waarom mijn lelijke hack eigenlijk bijna geen dubbele teruggeeft.

"Chaos kan niet uit de hand lopen"


Acties:
  • 0 Henk 'm!

  • Crayne
  • Registratie: Januari 2002
  • Laatst online: 17-03 13:41

Crayne

Have face, will travel

storeman schreef op dinsdag 10 november 2009 @ 14:12:
@crayne:

Je vergelijking is de huidige code niet erg eerlijk omdat je in de bovenste loop tot 1000000 doet en in de onderste tot 1000.
Denk je? ;)

Ik heb ze gelukkig wel met dezelfde parameters getest, de code hier bevat blijkbaar een fout inderdaad, zal het even bijstellen.

Mijn Library Thing catalogus

Pagina: 1