Hvordan å generere et tilfeldig tall i C Stack Overflow

@trusktr for en enkel lineær kongruentrekursjonssekvens generator (som er hva rand () vanligvis er) seeding med rand () ville i beste fall ha noen effekt i det hele tatt, og i verste fall ville bryte generator kjente kvaliteter. Dette er et vanskelig tema. Begynn med å lese Knuth Vol 2 kapittel 3 om tilfeldige tall som den beste introduksjonen til matematikk og fallgruver. – RBerteig 3 oktober ’12 på 21:15

Husk at dette er fortsatt en svak måte å se PRNG. Bare i fjor, en cryptolocker-type virus på Linux har gjort feil i seeding med tiden, og dette dramatisk redusert leteområdet. Alt du måtte gjøre var å få en anstendig idé om når infeksjonen oppstod, og deretter prøve frø fra rundt den tiden. Sist jeg hørte, den beste kilden til tilfeldigheten er / dev / urandom, som er, tilsynelatende, seeded fra en mashup av kaotisk-ish kilder som temperaturer på maskinvaren. Hvis alt du virkelig ønsker, men er for programmet å handle annerledes på hvert løp, er løsningen ovenfor fine. – Jemenake 7 februar ’16 kl 20:15

Den rand () -funksjonen i lt; stdlib.hgt; returnerer en pseudo-tilfeldig tall mellom 0 og RAND_MAX. Du kan bruke srand (unsigned int frø) for å stille et frø.

Det er vanlig praksis å bruke% operatøren i forbindelse med rand () for å få et annet utvalg (men husk at dette kaster av ensartethet noe). For eksempel:

Hvis du virkelig bryr deg om ensartethet kan du gjøre noe som dette:

Lar gå gjennom dette. Først bruker vi srand () -funksjonen til frø randomizeren. I utgangspunktet kan datamaskinen generere tilfeldige tall basert på antall som er matet til srand (). Hvis du ga den samme startverdi, da de samme tilfeldige tall vil bli generert hver gang.

Derfor har vi i frø randomizeren med en verdi som er alltid i endring. Vi gjør dette ved å mate den verdien av gjeldende tid med tiden () -funksjonen.

Nå, når vi kaller rand (), et nytt tilfeldig tall vil bli produsert hver gang.

Standarden C funksjonen er rand (). Det er godt nok til å håndtere kort for kabal, men det er forferdelig. Mange implementeringer av rand () bla gjennom en kort liste med tall, og de lave bitene har kortere sykluser. Måten noen programmer kaller rand () er forferdelig, og beregne et godt frø for å gå til srand () er vanskelig.

Den beste måten å generere tilfeldige tall i C er å bruke en tredjeparts bibliotek som OpenSSL. For eksempel,

Hvorfor så mye kode? Andre språk som Java og Ruby har funksjoner for tilfeldige heltall eller flyter. OpenSSL gir bare tilfeldige bytes, så jeg prøver å etterligne hvordan Java eller Ruby ville forvandle dem til heltall eller flyter.

For heltall, ønsker vi å unngå modulo bias. Anta at vi fikk noen tilfeldige 4-sifret tall fra rand ()% 10000. men rand () kan bare returnere 0-32767 (som det gjør i Microsoft Windows). Hvert nummer 0-2767 vil vises oftere enn hvert nummer fra 2768 til 9999. For å fjerne bias, kan vi forsøke rand (), mens verdien er under 2768, fordi de 30000 verdier fra 2768 til 32767 kart jevnt på de 10000 verdier 0-9999.

For flyter, vi ønsker 53 tilfeldige biter, fordi en dobbel holder 53 bits presisjon (forutsatt at det er en IEEE dobbel). Hvis vi bruker mer enn 53 biter, får vi avrunding bias. Noen programmerere skrive kode som rand () / (dobbel) RAND_MAX. men rand () kan returnere bare 31 biter, eller bare 15 bits Windows.

OpenSSL sin RAND_bytes () frø seg, kanskje ved å lese / dev / urandom i Linux. Hvis vi trenger mange tilfeldige tall, ville det være for treg til å lese dem alle fra / dev / urandom. fordi de må kopieres fra kjernen. Det er raskere å tillate OpenSSL å generere flere tilfeldige tall fra et frø.

Mer om tilfeldige tall:

  • Perl Perl_seed () er et eksempel på hvordan du beregner et frø i C for srand (). Det blander biter fra det nåværende tidspunkt, prosess ID, og ​​noen tips, hvis den ikke kan lese / dev / urandom.
  • OpenBSD er arc4random_uniform () forklarer modulo bias.
  • API Java for java.util.Random beskriver algoritmer for å fjerne fordommer fra tilfeldige heltall, og pakking 53 bits inn tilfeldige flyter.

svarte 8 juli ’15 på 01:34

STL eksisterer ikke for C. Du må ringe rand. eller enda bedre, tilfeldig. Disse er deklarert i standard bibliotek header stdlib.h. rand er POSIX, er tilfeldig en BSD spec funksjon.

Forskjellen er at tilfeldige returnerer en mye mer brukervennlig 32-bit tilfeldig tall, og rand returnerer vanligvis en 16-biters tall. BSD manpages viser at de lavere biter av rand er syklisk og forutsigbart, så rand er potensielt ubrukelig for små tall.

svarte 4 mai ’09 på 22:10

Hvem nevnte STL? – anon 4 mai ’09 på 22:13

@Neil – Siden alle svar så langt nevne STL, jeg mistenker at spørsmålet ble raskt redigert for å fjerne anunecessary referanse. – Michael Burr 4 mai ’09 på 22:16

rand () er ikke ubrukelig for små tall – du kan bitshift dem ut og bruke bare de mer tilfeldige høye biter hvis du virkelig trenger det. – Chris Lutz 4 mai ’09 på 22:20

Hvis systemet støtter arc4random familie av funksjoner jeg vil anbefale å bruke disse i stedet standard rand funksjonen.

Den arc4random familien omfatter:

  • arc4random (void). uint32_t
  • arc4random_buf (void * buf, size_t bytes). tomrom
  • arc4random_uniform (uint32_t grense). uint32_t
  • arc4random_stir (void). tomrom
  • arc4random_addrandom (unsigned char * dat, int datlen). tomrom

arc4random returnerer en tilfeldig 32-bit usignert heltall.

arc4random_buf setter tilfeldig innhold i det para buf. tomrom *. Mengden av innholdet bestemmes av byte. size_t parameter.

arc4random_uniform returnerer en tilfeldig 32-bit usignert heltall som følger regelen: 0 lt; = arc4random_uniform (grense) lt; grense. hvor grensen er også en usignert 32-bits heltall.

arc4random_stir leser data fra / dev / urandom og sender dataene til arc4random_addrandom i tillegg random den interne tilfeldig tall bassenget.

arc4random_addrandom brukes av arc4random_stir å fylle den interne tilfeldig tall basseng i henhold til de data som sendes til den.

Hvis du ikke har disse funksjonene, men du er på Unix, så kan du bruke denne koden:

Den urandom_init funksjonen åpner / dev / urandom enhet, og legger filen descriptor i urandom_fd.

Den urandom funksjon er i utgangspunktet det samme som en oppfordring til rand. bortsett sikrere, og den returnerer en lang (lett utskiftbare).

Men / dev / urandom kan være litt treg, så det anbefales at du bruker den som et frø for et annet tilfeldig tall generator.

Hvis systemet ikke har en / dev / urandom. men har en / dev / tilfeldig eller lignende fil, så kan du bare endre banen vedtatt å åpne i urandom_init. Samtalene og APIer som brukes i urandom_init og urandom er (tror jeg) POSIX-kompatibel, og som sådan, bør fungere på de fleste, om ikke alle POSIX-kompatible systemer.

Merknader: En lese fra / dev / urandom vil ikke blokkere hvis det ikke er nok entropi tilgjengelig, så verdiene generert under slike omstendigheter kan være kryptografisk usikker. Hvis du er bekymret for det, og bruk deretter / dev / tilfeldig. som alltid vil blokkere hvis det er for lite entropi.

Hvis du er på et annet system (dvs. Windows), og deretter bruke rand eller noen interne Windows bestemt plattform avhengig ikke-bærbar API.

Wrapper funksjon for urandom. rand. eller arc4random samtaler:

Høre en god forklaring på hvorfor du bruker rand () til å produsere jevnt fordelt tilfeldige tall i et gitt område er en dårlig idé, bestemte jeg meg for å ta en titt på hvordan skjevt produksjonen faktisk er. Min test var rettferdig terning kaster. Her er C-kode:

og her er det utgang:

Jeg vet ikke hvordan uniform du trenger dine tilfeldige tall for å være, men ovenfor vises uniform nok for de fleste behov.

Edit: det ville være en god idé å initialisere PRNG med noe bedre enn tid (NULL).

svarte 20 oktober ’14 på 03:39

rand () kan mislykkes andre tilfeldigheten tester, slik som diehard tester. rand () skiller seg fra plattform til plattform; rand () verdier fra GNU / Linux kan være bedre enn verdier fra BSD eller Windows. – George Koehler 8 juli ’15 på 01:37

Dette er ikke en gyldig måte å teste med hensyn til tilfeldighet. – Veedrac 14 august ’16 på 22:59

Avhenger av hensikten og trusselen / risikomodell. For kryptografisk sterk RNG – sikker, bruker RDRAND (eller RDSEED). For en enkel terning thrower (ikke casino-nivå) IMHO den ovenfor burde holde. Nøkkelordet er "flink nok ". – Mus 29 august ’16 på 03:23

Jeg hadde et alvorlig problem med pseudo random number generator i mitt siste søknaden: Jeg repeatidly ringte min C-program via en pyhton manus og jeg var med som frø følgende kode:

  • rand vil generere samme pseudo tilfeldig sekvens gi samme frø i srand (se mennesket srand);
  • Som allerede nevnt, endrer tidsfunksjonen bare andre fra andre: hvis søknaden blir kjørt flere ganger i samme sekund, vil tiden returnere samme verdi hver gang.

Mitt program generert samme sekvens av tall. Du kan gjøre 3 ting for å løse dette problemet:

bland tid utgang med annen informasjon endrer på løp (i min søknad, output navn):

Jeg brukte djb2 som min hash-funksjon.

Øk tidsoppløsning. På min plattform, clock_gettime var tilgjengelig, så jeg bruker det:

Bruk begge metodene sammen:

Alternativ 3 sikrer deg (så vidt jeg vet) det beste frøet randomitet, men det kan skape en forskjell bare på svært rask søknad. Etter min mening alternativ 2 er et sikkert kort.

Prøv dette, jeg satte det sammen fra noen av begrepene som allerede referert ovenfor:

Kopiere all teksten under dette avsnittet – det vil si fra / * til det siste gt; – Inn i en header fil kalt random.h. Referanse det enten ved #include "(filnavn)" i samme mappe som C kildefilen, eller – hva jeg liker å gjøre – slippe dette i kompilatoren "inkludere" mappen og du kan referere til den i løpet av noen fremtidig samling med #include lt; (filnavn) gt; – Merk lt; gt; i stedet for "" – I den standardkatalog. Riktignok hvis du vil arbeidsfilene å være bærbar, vil du ønsker å inkludere dette blant dem og referere til den i anførselstegn i stedet – men hvis du kompilere ofte på samme maskin og bare ønsker å alltid være i stand til å ringe tilfeldig (n), den lt; gt; Metoden ovenfor er en fin måte å behandle det som en "standard" header fil.

svarte 14 september ’13 på 20:40

Formater svaret, er det vanskelig å lese. – Beryllium 14 september ’13 på 20:58

Denne koden er ikke bra. Calling srand () hver gang du ønsker å ringe rand () er en forferdelig idé. Siden tid () returnerer vanligvis en verdi i sekunder kalle denne funksjonen raskt vil returnere den samme "tilfeldig" verdi. – Masovn 14 september ’13 på 21:08

Kilde: stackoverflow.com

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *

10 − 3 =