Ik heb nog altijd het vermoeden dat rand() niet random is, iig, niet volledig random.

Na mijn vorige experiment (let vooral op de totalen onderaan) heb ik nu een nieuw experiment bedacht. Een plaatje.

Ik geef de opdracht (source) om met x = rand(0, 500) en y= rand(0, 500) een plaatje pixel voor pixel in te kleuren. Daarna tel ik de witte pixels. Nu valt het me op dat het aantal witte pixels altijd 36% is. Nooit meer, nooit minder, op een paar getallen achter de komma na.

Nog erger, op mijn eigen computer had ik laatst een plaatje met een volledig raster erin. Je zag gewoon de knooppunten. Helaas had ik het niet opgeslagen.

Altijd 36%, wat voor conclusie mag ik daaruit trekken? Dat het aantal keer dat x en y al eerder samen zijn voorgekomen dus 36% is. Maar waarom nooit 37% laat staan 45%?

Maar dit is een van mijn gemiddelde resultaten:
Jan Koehoorn schreef op 07.04.2006 19:52
in de kansbereking worden uitkomsten voorspelbaarder naarmate het aantal pogingen toeneemt.


Dat was ook mijn conclusie.
Heb ook even een programmaatje om mijn grafische rekenmachine gemaakt. Ook met pixels enz. Ik maak een veld van 30x30 en laat die ook random vullen met pixels en te het aantal witten. Nu kom ik met mijn rekenmachine telkens tussen de 35-40. Dus daar is de rand functie beter?


ps. over een forlus van 30*30=900 doet mijn rekenmachine al 52 seconden :( en dan heb ik het over een TI84+
@ Steffan: Je aantal pogingen is veel en veel kleiner dan wat Jelmer gedaan heeft, dus is je gemiddelde afwijking ook groter.
Laten we het eens wiskundig gaan bekijken: de Chi-kwadraatverdeling (ken je hem nog? ;-) )

Om de Chi-kwadraat te bepalen heb ik een klein script geschreven dat 100.000 getallen tussen 0 en 99 genereert met zowel mt_rand() als rand().

In een array wordt bijgehouden hoe vaak elke random-waarde voorkomt (frequentie). Hiervan wordt een Chi-kwadraatverdeling berekend.

De waarde die daaruit voortkomt is een indicatie hoe goed de randomizer werkt. Zoek de waarde op in onderstaande distributietabel:

99% 69.22986
90% 81.44925
70% 91.16627
50% 98.33414
40% 101.9279
10% 117.4069
1% 134.6415

Deze tabel geeft aan dat 99% van de keren dat je dit script runt, de chi-kwadraat meer moet zijn dan 69.22986, 90% van de keren groter dan 81.44925, etc.

Een eenvoudige vuistregel is te kijken naar de waardes tussen 40% en 70%; de chi-kwadraat zou dus ruwweg tussen 91 en 102 moeten liggen.

Een voorzichtige conclusie die ik trek na het script een aantal malen te hebben uitgevoerd is dat mt_rand() inderdaad beter is dan rand(), maar dat ook mt_rand() niet echt waanzinnig goed is.

De source van het script kun je hier vinden, en het script zelf hier.
Arjan Kapteijn schreef op 07.04.2006 14:08
Als random random zou moeten zijn, dan betekend dat toch dat er uiteindelijk dezelfde waardes uit moeten komen als je maar genoeg keer 'kop of munt' uitvraagt?
Als het daadwerkelijk zo zou zijn dat elke waarde even vaak voorkomt, dan is dat niet random ;-)
Als ik het plaatje 1000x1000 maak, kom ik met rand() zelfs op 98% wit uit :s

mt_rand ook op 36%

Dit is lokaal Windows XP Home PHP 5.1.2 Apache 2.0.55

Ik zal het zo ook nog even online testen...

Edit
Online kom ik op dezelfde resultaten uit (Windows 2000 bak met PHP 4.4.1 als ik me niet vergis.) Wat me wel opvalt, als er maar 1 mt_rand is (x of y maakt niet uit), dat hij dan wel altijd 36% blijft. En niet er tussen gaat zitten oid. (Zal vast weer te verklaren zijn, maar het viel mij gewoon op :) )

Edit 2
Ik vergiste me dus in de PHP versie :)
@ Willem vp: chi-kwadraat is inderdaad lang geleden :-) De voorkeur gaat dus uit naar het nog steeds niet geweldige mt_rand.
Heb ik Elwin's conclusie van een eerdere test dus weerlegd. rand() is sneller, maar op windows vrij onwillekeurig. (een raster krijgen vind ik geen willekeur!) mt_rand() is langzamer, maar 'betrouwbaarder' :)
In een boek dat ik heb (Dynamische websites met PHP) meen ik te hebben gelezen dat mt_rand() juist sneller is, zal het nog wel even nakijken..

edit: Ik citeer:
...
De functie mt_rand() werkt gemiddeld ongeveer vier keer sneller dan rand() en gebruikt een speciale generator, de Mersenne Twister (MT), die beter geschikt zou zijn voor cryptografie.

Bron: Dynamische website's met PHP

Daaronder staat nog een hoofdstukje over srand() en mt_srand(). Daar staat ook een stukje over wat er hier allemaal wordt getest. Ik denk niet dat de schrijver dit leuk vind, maargoed:
srand() en mt_rand()
Een generator voor aselecte getallen maakt vaak niet echt een aselect getal, maar een pseudo-aselect getal door willekeurig een getal te trekken uit een vaste reeks van standaardgetallen. Daardoor is de statische kans dat een specifiek getal vaker voorkomt dan andere getallen groot, waardoor het gegenereerde getal niet echt willekeurig is en uiteindelijk dus veel minder veilig.

U kunt dit voorkomen door de generator te initialiseren met een seed of 'zaadje'. Vanaf PHP 4.2.0 gebeurt dat automatisch, maar in oudere versies moet u hiervoor de functie srand() of mt_srand() gebruiken:

void srand ( int seed )
void mt_srand ( int seed )

De functie srand() en mt_srand() husselen of dobbelen de reeks standaardgetallen als het ware door elkaar met de integer in de parameter seed. Om echter te voorkomen dat u toch weer steeds dezelfde reekst getallen krijgt, moet u voor seed elke keer een andere integer gebruiken. Een standaardtechniek daarvoor is uitgaan van de tijd in seconden, milliseconden of zelfd microseveonden volgens de systeemklok van de server, omdat de tijd voortdurend verandert. Hiervoor kunt u de functie microtime() gebruiken, die een timestamp of tijdstempel teruggeeft met de huidige tijd als het aantal milliseconden en seconden dat is verstreken sinds het begin van het UNIX-tijdperk (1 januarie 1970 om 0:00:00). Het resultaat van microtime() is een string met milliseconden en daarna seconden. Als u dit resultaat vermenigvuldigt met een groot getal, bijvoorbeeld 1000000 (één miljoen) en daarop de cast (int) toepast, krijgt u een lange integer die u kunt gebruiken voor de parameter seed van srand() en mt_srand(), bijvoorbeeld:

mt_srand((int) (microtime() * 1000000));

Bron: Dynamische website's met PHP

Reageren