Kirjautuminen

Haku

Tehtävät

Keskustelu: Ohjelmointikysymykset: C++: Ongelma satunnaislukujen kanssa

Sivun loppuun

Aloittelija3 [03.06.2009 19:47:39]

#

Mulla on siis nyt vaikkapa funktiot N() ja K(), joista N() arpoo numeron välillä 0-9 ja lähettää sen numeron samaan tapaan kuin Visual Basicissa SendKeys, ja K() tekee muuten saman, mutta kirjaimilla. Ongelma on siinä, että

N();
K();
N();
K();
N();
K();

tuottaa jotain tämäntapaista: 6N6N6N. Toi kyllä toimii jos jokaisen väliin noista laittaa jonkun jumalattoman pitkän Sleepin() mutta siinä kestääää.

Metabolix [03.06.2009 19:52:54]

#

Et kertonut mitään arvontatavastasi, mutta kristallipallo sanoo, että kutsut srand-funktiota joka välissä, vaikka sitä kuuluu kutsua vain kerran aluksi.

Grez [03.06.2009 19:53:01]

#

Jees, ja sama ongelma on kaiksisa Delphin, C:n, C++:n ja Pascalin versioissa, ja funktion lähdekoodi on huippusalainen?

Vaikka kristallipalloni onkin pölyssä, niin veikkaan että funktiosi käyttää aikaan satunnaismoottorin siemenlukuna kellonaikaa tms. ja nopeiden kutsujen välillä aika ei ehdi muuttua, joten satunnaisuus alustetaan aina samalla siemenluvulla.

Voisit kokeilla alustaa satunnaisgeneraattorin vain ohjelman alussa etkä ollenkaan itse funktioissa.

(Edit: Metabolix näköjään jo arvaili samaa)

Antti Laaksonen [03.06.2009 19:54:38]

#

Jos siemenluku on kellonaika sekunteina, saman sekunnin aikana arvotut luvut ovat aina samat.

Sama ilmiö näkyy Windowsin pasianssissa, jos painat F2-napin (uusi peli) pohjaan.

Aloittelija3 [03.06.2009 20:16:28]

#

Joo on huippusalainen ;) Mutta kiitos, hyvin teillä kristallipallot toimii :) Pitää vielä vähän parannella tota..

jalski [04.06.2009 17:31:50]

#

Jos haluat kokeilla vaihtoehtoista random-funktio toteutusta, niin alla on OpenWatcom C-kutsuttava random-funktio (toimiva, testattu), sekä todennäköisesti toimiva käännös Delphille (sori, en osaa pascalia. Mahdollisten virheiden korjaus jätetään harjoitukseksi).

Delphi-versio olettaa, että siemenluku muuttujat seed1: integer ja seed2: integer on määritelty jonnekin ohjelmaan globaaleiksi.

Molemmat funktiot tietysti vaativat, että siemenluku muuttujat asetetaan kerran ohjelmassa, ennenkuin funktiota kutsutaan ensimmäisen kerran. Arvot voi ottaa vaikka kellonajasta.

OpenWatcom C-kutsuttava:

.386p

 _DATA SEGMENT DWORD PUBLIC USE32 'DATA'
        public _random_var1
        public _random_var2
        _random_var1    dd 3    ; siemenluku1
        _random_var2    dd 5    ; siemenluku2


 _DATA ENDS

 DGROUP GROUP _DATA


_TEXT SEGMENT PARA PUBLIC USE32 'CODE'
ASSUME CS:_TEXT, DS:_DATA

public Random1_
Random1_ proc near
        push ebx
        push edx

        cmp       eax, 0
        jz @random_exit

        cmp eax, 0xFFFFFFFF
        jz @random_exit

        mov ebx, eax

        mov eax, 0x4019CF16
        mul _random_var1
        add eax, _random_var1[4]
        mov _random_var1, eax
        adc edx, 0
        mov _random_var1[4], edx

        xor edx, edx
        inc ebx
        div  ebx
        xchg edx, eax

@random_exit:
        pop edx
        pop ebx

        ret

Random1_ endp


        _TEXT ends
                end

Delphi asm-funktio:

function Random1(range : integer): integer;assembler
asm
        push ecx
        push edx

        cmp  eax, 0
        jz @random_exit

        cmp eax, 0xFFFFFFFF
        jz @random_exit

        mov ecx, eax

        mov eax, 0x4019CF16
        mul seed1
        add eax, seed2
        mov seed1, eax
        adc edx, 0
        mov seed2, edx

        xor edx, edx
        inc ecx
        div ecx
        xchg edx, eax

@random_exit:
        pop edx
        pop ecx
end;

goala [04.06.2009 17:35:42]

#

Taas kerran, Boost tarjoaa toimivan ratkaisun.

http://www.boost.org/doc/libs/1_39_0/libs/random/index.html

Pieni esimerkki:

boost::mt19937 r; // Mersenne-twister generaattori.
boost::uniform_int<> d(1, 10); // Arvotaan numeroita 1-10 väliltä; distribuutio.
boost::variate_generator<boost::mt19937&, boost::uniform_int<> > arpa(r, d);

int luku = arpa();

os [04.06.2009 18:39:29]

#

Jaahas, jälkimmäisten herrojen kristallipallot sanovatkin siis aloittajan ongelman olleen se, että tämän satunnaislukugeneraattori on liian yksinkertainen ja helppokäyttöinen. Vaikea laji tuo ennustaminen... :)

ByteMan [04.06.2009 18:49:55]

#

itse kyhään satunnaiset lukuni niin, että jos haluan for -silmukassa esimerkiksi sarpoa useita lukuja, teen sen jotakuinkin näin:

for( int i = 0; i < 10; i++ ){
    srand( time(0) + i);
    //jne..
}

tällöin takaantuu se, että tulos on joka kerralla eri.
Edit:: tai se mitöön takaa, takaapa vain sen että siemenluku on joka kerralla eri

tosin tunnustan että ihan varmuuden varaksi(lue: omaksi iloskeni) teen niistä lausekkeista monimutkaisempia tyyliin:

srand (apu2*time(0) + apu1*i);

saman toki voi tehdä ilmankin silmukoita, mutta silloin apumuuttujaa kuitenkin pitäisi kasvattaa aina välissä

os [04.06.2009 19:13:55]

#

Ja minkäköhän ihmeen takia sinä noin teet? Satunnaislukujen laatu ei automaattisesti parane vaihtamalla siemenlukua mahdollisimman usein, varsinkaan jos tämän uuden siemenluvun satunnaisuus on huono, kuten tuossa koodissasi, jossa tuo srandin kutsuminen käytännössä vain huonontaa lopputulosta.

Jos haluat laadukkaampia satunnaislukuja, vaihda satunnaislukugeneraattoria. Tämä onnistuu esimerkiksi goalan näyttämällä tavalla. Myös siemenluvun satunnaisuutta voi tarvittaessa parantaa, mutta noin se ei kyllä onnistu.

EDIT: Jos et usko, niin kokeile, mitä seuraava ohjelma tulostaa:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void tulosta_satunnaisluvut( int n ) {
	for( int i = 0; i < n; i++ ) {
		srand( time(0) + i);
		printf("%d ", rand() % 10);
	}
	printf("\n");
}

int main() {
	tulosta_satunnaisluvut( 5 );
	tulosta_satunnaisluvut( 6 );
	tulosta_satunnaisluvut( 7 );
	return 0;
}

Teuro [04.06.2009 20:36:18]

#

Satunnaisuus on vaikea laji, eikä deterministisellä koneella ole mahdollista saavuttaa aitoa satunnaisuutta, koska tulos on näillä jokaisella kaavalla tarkasti etukäteen tiedossa. Lisäksi kannattaa miettiä tuottaako siemenluvun muutos oikeasti satunnaisuutta tuloksiin, kuten os edellä jo mainitsikin.

Saattaa nimittäin käydä niinkin, että generaattorin tuottama satunnisuus häviää tuolla tavalla olemattomiin. Hassun hauskassa c++ oppaassa on artikkeli aiheesta, johon kannattaa tutustua ennen omia sävellyksiä. Putkassa on ennenkin keskusteltu omista satunnaisluvuista.

Grez [04.06.2009 20:50:36]

#

Teuro kirjoitti:

Satunnaisuus on vaikea laji, eikä deterministisellä koneella ole mahdollista saavuttaa aitoa satunnaisuutta, koska tulos on näillä jokaisella kaavalla tarkasti etukäteen tiedossa. Lisäksi kannattaa miettiä tuottaako siemenluvun muutos oikeasti satunnaisuutta tuloksiin, kuten os edellä jo mainitsikin.

No tuo nyt meni jo aika pitkälti offtopiciksi alkuperäisen kysyjän kannalta. Mutta jos tarvitsee aitoa satunnaisuutta, niin ainahan voi hankkia jonkin tällaisen ja onhan esimerkiksi Linuxissa kohtuullisen hyvä satunnaisuuslähde ilman erillistä rautaakin, kun se kerää satunnaisuutta altaaseen erilaisista koneen jossain määrin satunnaisista tapahtumista.

ByteMan [05.06.2009 01:15:06]

#

no okei, myöönän virheeni - tässä tapauksessa - koska tässä ilmiselvästi virhe on. joskus kuitenkin onnistuin siemenlukua muuttamalla ehkäisemään saman luvun jatkuvan toistumisen sekuntin aikana, ja se oli mielestäni riittävä keino, vaikka kumpikaan noista edellisessä viestissäni mainituista "kaavoista" nyt ei toimikkaan aivan niinkuin oli tarkoitus

eq [05.06.2009 05:41:08]

#

ByteMan kirjoitti:

no okei, myöönän virheeni - tässä tapauksessa - koska tässä ilmiselvästi virhe on. joskus kuitenkin onnistuin siemenlukua muuttamalla ehkäisemään saman luvun jatkuvan toistumisen sekuntin aikana, ja se oli mielestäni riittävä keino, vaikka kumpikaan noista edellisessä viestissäni mainituista "kaavoista" nyt ei toimikkaan aivan niinkuin oli tarkoitus

Välttäisit sen muutenkin, jos alustaisit satunnaislukugeneraattorisi vain kerran (kuten on tarkoitus). (Käsittääkseni) useimmat C:n standardikirjaston satunnaislukugeneraattorien toteutukset muodostavat näennäisen satunnaisen lukunsa aina edellisen luvun perusteella (tai ensimmäisen luvun tapauksessa siemenluvun avulla) yksinkertaisen kerto-yhteenlasku-jakojäännös-laskun avulla, eikä laskun tuloksen pitäisi olla n millään luvulla n (ts. jäädä "jumiin").

ByteMan [05.06.2009 12:07:49]

#

@eq: ....öööh just :D oli pakko tarkistaa toi nii niihä se sit näköjään toimiiki
sillon ku sain käsityksen että se luku vaihtuu vain sekuntin välein olin ilmeisesti vahingossa laittanut srand( time(0) ); -rivin for silmukkaan sisälle...
ja koska ohjelma aina kääntyi ilman virheitä en vaivautunut ikänä menemään referencestä tarkistamaan toimiiko se oikeasti niin -.-


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta