Jak wygenerować losową liczbę całkowitą w C?


Odpowiedzi:


662

Uwaga : Nie używaj rand()dla bezpieczeństwa. Jeśli potrzebujesz numeru zabezpieczonego kryptograficznie, zapoznaj się z tą odpowiedzią .

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

srand(time(NULL));   // Initialization, should only be called once.
int r = rand();      // Returns a pseudo-random integer between 0 and RAND_MAX.

Edycja : w systemie Linux możesz preferować losowe i losowe .


194
+1 dla uproszczenia, ale prawdopodobnie dobrym pomysłem jest podkreślenie, że srand () należy wywoływać tylko raz . Ponadto w aplikacji wątkowej możesz chcieć upewnić się, że stan generatora jest zapisany dla każdego wątku, i zaszczepić generator raz dla każdego wątku.
RBerteig,

41
@trusktr, to skomplikowane. Oto powód: time()zmienia się tylko raz na sekundę. Jeśli zaczniesz od time(), dla każdego połączenia do rand(), otrzymasz tę samą wartość dla każdego połączenia w ciągu jednej sekundy. Ale głównym powodem jest to, że właściwości rand()i funkcje tego rodzaju są najlepiej znane w przypadku użycia, w którym są one inicjowane dokładnie raz na uruchomienie, a nie przy każdym wywołaniu. W zależności od „losowości” z niesprawdzonymi lub niesprawdzonymi właściwościami prowadzi do problemów.
RBerteig

9
@trusktr dla prostego liniowego generatora kongruencjalnego (którym rand()zwykle jest) zaszczepienie przy pomocy rand()w najlepszym wypadku nie miałoby żadnego efektu, aw najgorszym przypadku złamałoby znane cechy generatora. To głęboki temat. Zacznij od przeczytania Knuth Vol 2 Rozdział 3 o liczbach losowych jako najlepszego wprowadzenia do matematyki i pułapek.
RBerteig

21
Unikaj ostrzeżenia kompilatora z obsadą:srand((unsigned int)time(NULL));
GiovaMaster

9
Należy pamiętać, że jest to wciąż słaby sposób postrzegania PRNG. Tylko w zeszłym roku wirus typu cryptolocker w Linuksie popełnił błąd zaszczepienia się czasem, co znacznie zmniejszyło przestrzeń wyszukiwania. Wystarczyło zorientować się, kiedy nastąpiła infekcja, a następnie wypróbować nasiona z tego okresu. Ostatnio słyszałem, że najlepszym źródłem losowości jest / dev / urandom, który rzekomo pochodzi z mieszanki chaotycznych źródeł, takich jak temperatura na sprzęcie. Jeśli jednak tak naprawdę chcesz, aby Twój program działał inaczej przy każdym uruchomieniu, powyższe rozwiązanie jest w porządku.
Jemenake,

238

rand()Działają w <stdlib.h>Zwraca pseudo losową liczbę całkowitą między 0 a RAND_MAX. Możesz użyć, srand(unsigned int seed)aby ustawić ziarno.

Powszechną praktyką jest używanie %operatora w połączeniu z, rand()aby uzyskać inny zasięg (choć należy pamiętać, że to nieco obniża jednolitość). Na przykład:

/* random int between 0 and 19 */
int r = rand() % 20;

Jeśli naprawdę zależy Ci na jednolitości, możesz zrobić coś takiego:

/* Returns an integer in the range [0, n).
 *
 * Uses rand(), and so is affected-by/affects the same seed.
 */
int randint(int n) {
  if ((n - 1) == RAND_MAX) {
    return rand();
  } else {
    // Supporting larger values for n would requires an even more
    // elaborate implementation that combines multiple calls to rand()
    assert (n <= RAND_MAX)

    // Chop off all of the values that would cause skew...
    int end = RAND_MAX / n; // truncate skew
    assert (end > 0);
    end *= n;

    // ... and ignore results from rand() that fall above that limit.
    // (Worst case the loop condition should succeed 50% of the time,
    // so we can expect to bail out of this loop pretty quickly.)
    int r;
    while ((r = rand()) >= end);

    return r % n;
  }
}

18
Jest to powszechna praktyka , ale nie poprawna. Zobacz to i to .
Lazer

35
@Lazer: Dlatego powiedziałem „choć pamiętajcie, że to trochę zrzuca z jednolitości”.
Laurence Gonsalves

3
@AbhimanyuAryan Jest %to operator modułu. Daje ci resztę podziału na liczby całkowite, więc x % nzawsze da ci liczbę pomiędzy 0i n - 1(o ilex i nsą zarówno pozytywne). Jeśli nadal uważasz to za mylące, spróbuj napisać program, który iliczy od 0 do 100, i wydrukuje i % ndla niektórych nwybranych mniejszych niż 100.
Laurence Gonsalves

2
@necromancer Poszedłem i dodałem idealnie jednolite rozwiązanie.
Laurence Gonsalves

2
@Lazer drugi link, który opublikowałeś, nadal nie jest idealnie jednolity. Rzucanie na podwójne i tylne plecy nie pomaga. Pierwszy opublikowany link ma idealnie jednolite rozwiązanie, choć będzie dużo pętli dla małych górnych granic. Dodałem do tej odpowiedzi idealnie jednolite rozwiązanie, które nie powinno zapętlać się tak bardzo nawet w przypadku małych górnych granic.
Laurence Gonsalves

53

Jeśli potrzebujesz bezpiecznych losowych znaków lub liczb całkowitych:

Jak rozwiązano w kwestii bezpiecznego generowania liczb losowych w różnych językach programowania , należy wykonać jedną z następujących czynności:

Na przykład:

#include "sodium.h"

int foo()
{
    char myString[32];
    uint32_t myInt;

    if (sodium_init() < 0) {
        /* panic! the library couldn't be initialized, it is not safe to use */
        return 1; 
    }


    /* myString will be an array of 32 random bytes, not null-terminated */        
    randombytes_buf(myString, 32);

    /* myInt will be a random number between 0 and 9 */
    myInt = randombytes_uniform(10);
}

randombytes_uniform() jest kryptograficznie bezpieczny i bezstronny.


czy należy wywołać libsodium RNG przed wywołaniem randombytes_buf?
user2199593,

Po prostu zadzwoń sodium_init()w pewnym momencie. Nie martw się o RNG, używa jądra.
Scott Arciszewski

Uwaga: Zatwierdziłem ostatnią edycję, sodium_init()chociaż niekoniecznie jest to część mojego przykładu, ponieważ jest to ważny szczegół.
Scott Arciszewski

29

Przejdźmy przez to. Najpierw używamy funkcji srand () do inicjowania randomizatora. Zasadniczo komputer może generować losowe liczby na podstawie liczby podawanej do srand (). Jeśli podasz tę samą wartość początkową, za każdym razem będą generowane te same liczby losowe.

Dlatego musimy zaszczepić randomizator wartością, która zawsze się zmienia. Robimy to, podając mu wartość bieżącego czasu za pomocą funkcji time ().

Teraz, gdy wywołamy rand (), za każdym razem będzie generowany nowy losowy numer.

#include <stdio.h>

int random_number(int min_num, int max_num);

int main(void)
{
    printf("Min : 1 Max : 40 %d\n", random_number(1,40));
    printf("Min : 100 Max : 1000 %d\n",random_number(100,1000));
    return 0;
}

int random_number(int min_num, int max_num)
{
    int result = 0, low_num = 0, hi_num = 0;

    if (min_num < max_num)
    {
        low_num = min_num;
        hi_num = max_num + 1; // include max_num in output
    } else {
        low_num = max_num + 1; // include max_num in output
        hi_num = min_num;
    }

    srand(time(NULL));
    result = (rand() % (hi_num - low_num)) + low_num;
    return result;
}

12
Ładny kod, ale nie jest dobrym pomysłem nazywanie „srand (czas (NULL));”. ta metoda generuje ten sam numer, gdy jest wywoływana w pętli for.
RayOldProf,

1
Sugerowane zmiany dotyczące kodu często są odrzucane. Ktoś tu zrobił jeden z komentarzem „algorytm był niepoprawny. Może produkować więcej niż maksimum”. Sam nie oceniłem roszczenia.
Martin Smith

1
@Martin Smith Problemy: 1) powinno być else{ low_num=max_num; hi_num=min_num+1;2) kończy się niepowodzeniem, kiedy hi_num - low_num > INT_MAX. 3) Pomija wartości w rzadkiej sytuacji INT_MAX > hi_num - low_num > RAND_MAX.
chux - Przywróć Monikę

1
Ponowne posadzenie go w ten sposób spowoduje, że ta funkcja wygeneruje ten sam numer, jeśli zostanie wywołany wiele razy w tej samej sekundzie. Jeśli naprawdę chcesz to zresetować, zresetuj je tylko raz na sekundę.
Élektra

1
Drobne: hi_num = max_num + 1;brak ochrony przed przepełnieniem.
chux - Przywróć Monikę

25

Jeśli potrzebujesz pseudolosowych liczb lepszej jakości niż to stdlib, co oferuje, sprawdź Mersenne Twister . Jest też szybszy. Przykładowe wdrożenia są obfite, na przykład tutaj .


2
+1: Wygląda świetnie, ale właśnie tworzyłem grę w zgadywanie. Gdybym miał użyć generatora liczb losowych w aplikacji biznesowej, na pewno bym tego użył.
Kredns

4
Nie używaj Mersenne Twister, użyj czegoś dobrego, takiego jak xoroshiro128 + lub PCG. (Odpowiedni link.)
Veedrac

17

Standardowa funkcja C to rand(). Wystarczająco dobrze jest rozdawać karty w pasjansa, ale jest okropny. Wiele implementacji rand()cyklu poprzez krótką listę liczb, a małe bity mają krótsze cykle. Sposób, w jaki niektóre programy wywołują, rand()jest okropny, a obliczenie dobrego ziarna do przekazania srand()jest trudne.

Najlepszym sposobem na generowanie liczb losowych w C jest użycie biblioteki innej firmy, takiej jak OpenSSL. Na przykład,

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rand.h>

/* Random integer in [0, limit) */
unsigned int random_uint(unsigned int limit) {
    union {
        unsigned int i;
        unsigned char c[sizeof(unsigned int)];
    } u;

    do {
        if (!RAND_bytes(u.c, sizeof(u.c))) {
            fprintf(stderr, "Can't get random bytes!\n");
            exit(1);
        }
    } while (u.i < (-limit % limit)); /* u.i < (2**size % limit) */
    return u.i % limit;
}

/* Random double in [0.0, 1.0) */
double random_double() {
    union {
        uint64_t i;
        unsigned char c[sizeof(uint64_t)];
    } u;

    if (!RAND_bytes(u.c, sizeof(u.c))) {
        fprintf(stderr, "Can't get random bytes!\n");
        exit(1);
    }
    /* 53 bits / 2**53 */
    return (u.i >> 11) * (1.0/9007199254740992.0);
}

int main() {
    printf("Dice: %d\n", (int)(random_uint(6) + 1));
    printf("Double: %f\n", random_double());
    return 0;
}

Dlaczego tyle kodu? Inne języki, takie jak Java i Ruby, mają funkcje losowych liczb całkowitych lub liczb zmiennoprzecinkowych. OpenSSL podaje tylko losowe bajty, więc staram się naśladować, w jaki sposób Java lub Ruby przekształciłyby je w liczby całkowite lub zmiennoprzecinkowe.

W przypadku liczb całkowitych chcemy uniknąć błędu modulo . Załóżmy, że otrzymaliśmy losowe 4-cyfrowe liczby całkowite rand() % 10000, ale rand()możemy zwrócić tylko 0 do 32767 (jak ma to miejsce w systemie Microsoft Windows). Każda liczba od 0 do 2767 pojawiałaby się częściej niż każda liczba od 2768 do 9999. Aby usunąć błąd, możemy spróbować ponownierand() gdy wartość jest mniejsza niż 2768, ponieważ wartości 30000 od 2768 do 32767 odwzorowują jednolicie na wartości 10000 od 0 do 9999.

W przypadku liczb zmiennoprzecinkowych chcemy 53 losowe bity, ponieważ doublezawierają 53 bity precyzji (zakładając, że jest to podwójna liczba IEEE). Jeśli użyjemy więcej niż 53 bity, otrzymujemy błąd zaokrąglania. Niektórzy programiści piszą kod podobny rand() / (double)RAND_MAX, ale rand()mogą zwrócić tylko 31 bitów lub tylko 15 bitów w systemie Windows.

RAND_bytes()Ziarna OpenSSL same w sobie, być może poprzez czytanie /dev/urandomw Linuksie. Jeśli potrzebujemy wielu liczb losowych, odczytanie ich wszystkich byłoby zbyt wolne /dev/urandom, ponieważ należy je skopiować z jądra. Szybciej pozwala OpenSSL generować więcej liczb losowych z nasion.

Więcej informacji o liczbach losowych:

  • Perl's Perl_seed () jest przykładem tego, jak obliczyć ziarno w C dla srand(). Miksuje bity z bieżącego czasu, identyfikatora procesu i niektórych wskaźników, jeśli nie może odczytać /dev/urandom.
  • Arc4random_uniform () w OpenBSD wyjaśnia modulo stronniczość.
  • Java API dla java.util.Random opisuje algorytmy do usuwania odchyleń od losowych liczb całkowitych i upakowywania 53 bitów w losowe liczby zmiennoprzecinkowe.

Dziękuję za tę rozszerzoną odpowiedź. Należy zauważyć, że spośród 24 obecnych odpowiedzi na to pytanie, byłeś jedynym z dodatkową interpretacją do czynienia z float/ double, więc już wyjaśniono kwestię trzymać się intliczb, aby uniknąć zbyt szerokie. Istnieją inne pytania C zajmujące konkretnie float/ doublelosowych wartości, więc może chcesz odśwież swoją drugą połowę swojej odpowiedzi na pytania takie jak stackoverflow.com/questions/13408990/...
Coeur

11

Jeśli twój system obsługuje arc4randomrodzinę funkcji, polecam korzystanie z nich zamiast randfunkcji standardowej .

arc4randomRodzina obejmuje:

uint32_t arc4random(void)
void arc4random_buf(void *buf, size_t bytes)
uint32_t arc4random_uniform(uint32_t limit)
void arc4random_stir(void)
void arc4random_addrandom(unsigned char *dat, int datlen)

arc4random zwraca losową 32-bitową liczbę całkowitą bez znaku.

arc4random_bufumieszcza losową zawartość w swoim parametrze buf : void *. Ilość treści zależy od bytes : size_tparametru.

arc4random_uniformzwraca losową 32-bitową liczbę całkowitą bez znaku, która jest zgodna z regułą: 0 <= arc4random_uniform(limit) < limitgdzie limit jest również 32-bitową liczbą całkowitą bez znaku.

arc4random_stirodczytuje dane /dev/urandomi przekazuje je, arc4random_addrandomaby dodatkowo randomizować wewnętrzną pulę liczb losowych.

arc4random_addrandomsłuży arc4random_stirdo wypełnienia wewnętrznej puli liczb losowych zgodnie z przekazanymi do niej danymi.

Jeśli nie masz tych funkcji, ale korzystasz z Uniksa, możesz użyć tego kodu:

/* This is C, not C++ */
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h> /* exit */
#include <stdio.h> /* printf */

int urandom_fd = -2;

void urandom_init() {
  urandom_fd = open("/dev/urandom", O_RDONLY);

  if (urandom_fd == -1) {
    int errsv = urandom_fd;
    printf("Error opening [/dev/urandom]: %i\n", errsv);
    exit(1);
  }
}

unsigned long urandom() {
  unsigned long buf_impl;
  unsigned long *buf = &buf_impl;

  if (urandom_fd == -2) {
    urandom_init();
  }

  /* Read 4 bytes, or 32 bits into *buf, which points to buf_impl */
  read(urandom_fd, buf, sizeof(long));
  return buf_impl;
}

urandom_initFunkcja otwiera /dev/urandomurządzenie i umieszcza deskryptor pliku w urandom_fd.

urandomFunkcja jest w zasadzie taka sama jak wywołania rand, z wyjątkiem bardziej bezpieczny, a to zwraca long(łatwo zmienić).

Jednak /dev/urandommoże być trochę powolny, dlatego zaleca się, aby użyć go jako źródła dla innego generatora liczb losowych.

Jeśli system nie posiada /dev/urandom, ale nie ma /dev/randompliku lub podobnego, wtedy można po prostu zmienić ścieżkę przekazanej openw urandom_init. Połączenia i API wykorzystywane w urandom_initi urandomsą (wierzę) POSIX, i jako taki powinien działać na większości, jeśli nie we wszystkich systemach zgodnych z POSIX.

Uwagi: Odczyt /dev/urandomNIE zablokuje się, jeśli dostępna jest niewystarczająca entropia, więc wartości generowane w takich okolicznościach mogą być niepewne kryptograficznie. Jeśli martwisz się tym, użyj /dev/random, który zawsze będzie blokował, jeśli nie będzie wystarczającej entropii.

Jeśli korzystasz z innego systemu (np. Windows), użyj randlub jakiegoś wewnętrznego, zależnego od platformy, nieprzenośnego interfejsu API.

Wrapper funkcji dla urandom, rand, lub arc4randompołączeń:

#define RAND_IMPL /* urandom(see large code block) | rand | arc4random */

int myRandom(int bottom, int top){
    return (RAND_IMPL() % (top - bottom)) + bottom;
}

8

STL nie istnieje dla C. Musisz zadzwonić randlub jeszcze lepiej random. Są one zadeklarowane w standardowym nagłówku biblioteki stdlib.h. randjest POSIX, randomjest funkcją specyfikacji BSD.

Różnica między randi randompolega na tym, że randomzwraca o wiele bardziej użyteczną 32-bitową liczbę losową i randzwykle zwraca liczbę 16-bitową. Strony BSD pokazują, że niższe bity randsą cykliczne i przewidywalne, więc randpotencjalnie są bezużyteczne dla małych liczb.


3
@ Neil - ponieważ wszystkie dotychczasowe odpowiedzi wspominają o STL, podejrzewam, że pytanie zostało szybko zredagowane w celu usunięcia niepotrzebnych odniesień.
Michael Burr

rand () nie jest bezużyteczny dla małych liczb - możesz je przesunąć w bity i użyć tylko bardziej losowych wysokich bitów, jeśli naprawdę potrzebujesz.
Chris Lutz

@Chris, możesz, jeśli rozmiar liczby losowej jest znany, ale jeśli wymagany rozmiar liczby losowej zmienia się podczas działania (np. Tasowanie dynamicznej tablicy itp.), Trudno byłoby obejść takie zastrzeżenie.
dreamlax

Nie mogę znaleźć żadnej funkcji losowego tutaj :-(
kasia.b

@ kasia.b w tym łączu znajduje się extern int rand(void);i extern void srand(unsigned int);.
RastaJedi

7

Spójrz na ISAAC (Indirection, Shift, Accumulate, Add, and Count). Jest równomiernie rozmieszczony i ma średnią długość cyklu 2 ^ 8295.


2
ISAAC jest interesującym RNG ze względu na jego szybkość, ale nie otrzymał jeszcze poważnej uwagi kryptograficznej.
user2398029

4

To dobry sposób na uzyskanie losowej liczby między dwiema wybranymi liczbami.

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

    #define randnum(min, max) \
        ((rand() % (int)(((max) + 1) - (min))) + (min))

int main()
{
    srand(time(NULL));

    printf("%d\n", randnum(1, 70));
}

Wyjście za pierwszym razem: 39

Wyjście za drugim razem: 61

Wyjście po raz trzeci: 65

Możesz zmienić wartości randnumna dowolne liczby, które wybierzesz, i wygeneruje dla ciebie losową liczbę między tymi dwiema liczbami.


4

Chcesz użyć rand(). Uwaga ( BARDZO WAŻNE ): pamiętaj, aby ustawić ziarno dla funkcji rand. Jeśli tego nie zrobisz, twoje losowe liczby nietak naprawdę losowe . To jest bardzo, bardzo, bardzo ważne. Na szczęście zazwyczaj można użyć kombinacji systemowego timera tykania i daty, aby uzyskać dobry początek.


6
Dwa punkty a) twoje losowe liczby nie są „prawdziwie” losowe, bez względu na to, jak zainicjujesz generator. Oraz b) bardzo wygodne jest, aby sekwencja pseudolosowa była zawsze taka sama w wielu okolicznościach - na przykład w celu przetestowania.

16
jeśli BARDZO WAŻNE jest, aby Twój numer był naprawdę losowy, nie powinieneś używać funkcji rand ().
tylerl

1
Wartości z rand nie są wcale „prawdziwie” losowe, bez względu na to, czy ustawisz ziarno, czy nie. Biorąc pod uwagę znane nasiona, sekwencja jest przewidywalna. „Prawdziwe” generowanie liczb losowych jest trudne. Rand nie wiąże się z entropią.
dreamlax

2
Oczywiście, że tak - generator zostanie zaszczepiony dla ciebie przez bibliotekę (prawdopodobnie do zera, ale to jest prawidłowe ziarno).

4
Ach, ale znany algorytm / znane ziarno jest niezbędne do debugowania dowolnego programu, który używa liczb losowych. Nie jest niczym niezwykłym rejestrowanie użytego materiału siewnego wraz z przebiegiem symulacji, aby można go było odtworzyć w celu przeprowadzenia bardziej szczegółowej analizy. Brak wywołania srand () jest w ogóle równoważny wywołaniu srand (1).
RBerteig

4

FWIW, odpowiedź brzmi: tak, istnieje stdlib.hfunkcja o nazwie rand; ta funkcja jest dostosowana przede wszystkim do prędkości i rozkładu, a nie do nieprzewidywalności. Prawie wszystkie wbudowane losowe funkcje dla różnych języków i frameworków domyślnie korzystają z tej funkcji. Istnieją również „kryptograficzne” generatory liczb losowych, które są znacznie mniej przewidywalne, ale działają znacznie wolniej. Powinny być używane w aplikacjach związanych z bezpieczeństwem.


4

Mam nadzieję, że jest to nieco bardziej losowe niż zwykłe używanie srand(time(NULL)).

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

int main(int argc, char **argv)
{
    srand((unsigned int)**main + (unsigned int)&argc + (unsigned int)time(NULL));
    srand(rand());

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

1
dodanie srand (rand ()) nie zwiększa losowości sekwencji, jeśli ten program jest wykonywany wielokrotnie w ciągu 1 sekundy. time (NULL) nadal zwróci tę samą wartość dla każdego z nich, pierwszy rand () zwróci tę samą długą wartość, a drugie wywołanie srand () będzie miało tę samą wartość, co spowoduje, że nadal będzie mieć tę samą losową sekwencję. Użycie adresu argc może pomóc, tylko jeśli jest gwarantowane, że adres ten będzie inny przy każdym uruchomieniu programu, co nie zawsze jest prawdziwe.
theferrit32

3

Cóż, STL to C ++, a nie C, więc nie wiem, czego chcesz. Jeśli jednak chcesz C, dostępne są funkcje rand()i srand():

int rand(void);

void srand(unsigned seed);

Obie są częścią ANSI C. Istnieje również random()funkcja:

long random(void);

Ale, o ile wiem, random()nie jest standardem ANSI C. Biblioteka innej firmy może nie być złym pomysłem, ale wszystko zależy od losowości liczby, którą naprawdę musisz wygenerować.


3

C Program do generowania liczb losowych od 9 do 50

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

int main()
{
    srand(time(NULL));
    int lowerLimit = 10, upperLimit = 50;
    int r =  lowerLimit + rand() % (upperLimit - lowerLimit);
    printf("%d", r);
}

Zasadniczo możemy wygenerować losową liczbę między lowerLimit i upperLimit-1

tj. lowerLimit jest włącznie lub powiedz r ∈ [lowerLimit, upperLimit)


@Pang Właśnie to wyraźnie wspomniałem MIĘDZY 9 i 50, a NIE OD 9 i 50.
Shivam K. Thakkar

2
Twoja operacja modulo wprowadziła błąd.
jww

2

rand() to najwygodniejszy sposób generowania liczb losowych.

Możesz również złapać losową liczbę z dowolnego serwisu internetowego, takiego jak random.org.


1
Możesz również złapać losową liczbę z dowolnej usługi online, takiej jak random.org Bounty, jeśli podasz przenośny, wydajny sposób na zrobienie tego w C.
MD XF

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

void main() 
{
    int visited[100];
    int randValue, a, b, vindex = 0;

    randValue = (rand() % 100) + 1;

    while (vindex < 100) {
        for (b = 0; b < vindex; b++) {
            if (visited[b] == randValue) {
                randValue = (rand() % 100) + 1;
                b = 0;
            }
        }

        visited[vindex++] = randValue;
    }

    for (a = 0; a < 100; a++)
        printf("%d ", visited[a]);
}

następnie zdefiniuj u góry za pomocą zmiennych @ b4hand
Muhammad Sadiq

Kiedy pisałem ten komentarz, nie miałem uniwersalnych uprawnień do edycji i ogólnie zmiany kodu, które zmieniły rzeczywistą odpowiedź, zostałyby odrzucone. Jeśli nie chcesz naprawić swojej odpowiedzi, mogę to zrobić.
b4hand

Jeśli Twoim celem było wygenerowanie losowej kombinacji unikatowych wartości, ten kod nadal ma błąd. Tworzy wartość 84 dwukrotnie i nie wytwarza wartości 48. Ponadto nie uruchamia generatora liczb losowych, więc sekwencja jest taka sama przy każdym wykonaniu.
b4hand

1
zmienne a i b są zdefiniowane w tym kodzie. Jest wolny od błędów .. nie ma też błędu składniowego i logicznego.
Muhammad Sadiq,

Błędny. Jak już wspomniałem, już ręcznie potwierdziłem wynik.
b4hand

1
#include <stdio.h>
#include <dos.h>

int random(int range);

int main(void)
{
    printf("%d", random(10));
    return 0;
}

int random(int range)
{
    struct time t;
    int r;

    gettime(&t);
    r = t.ti_sec % range;
    return r;
}

1

Na nowoczesnych procesorach x86_64 można używać sprzętowego generatora liczb losowych za pośrednictwem _rdrand64_step()

Przykładowy kod:

#include <immintrin.h>

uint64_t randVal;
if(!_rdrand64_step(&randVal)) {
  // Report an error here: random number generation has failed!
}
// If no error occured, randVal contains a random 64-bit number

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

//generate number in range [min,max)
int random(int min, int max){
    int number = min + rand() % (max - min);
    return number; 
}

//Driver code
int main(){
    srand(time(NULL));
    for(int i = 1; i <= 10; i++){
        printf("%d\t", random(10, 100));
    }
    return 0;
}

0

Słysząc dobre wyjaśnienie, dlaczego używanie rand()do generowania równomiernie rozmieszczonych liczb losowych w danym zakresie jest złym pomysłem, postanowiłem rzucić okiem na to, jak wypaczony jest wynik. Mój przypadek testowy polegał na rzucie kostką. Oto kod C:

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

int main(int argc, char *argv[])
{
    int i;
    int dice[6];

    for (i = 0; i < 6; i++) 
      dice[i] = 0;
    srand(time(NULL));

    const int TOTAL = 10000000;
    for (i = 0; i < TOTAL; i++)
      dice[(rand() % 6)] += 1;

    double pers = 0.0, tpers = 0.0;
    for (i = 0; i < 6; i++) {
      pers = (dice[i] * 100.0) / TOTAL;
      printf("\t%1d  %5.2f%%\n", dice[i], pers);
      tpers += pers;
    }
    printf("\ttotal:  %6.2f%%\n", tpers);
}

a oto jego wynik:

 $ gcc -o t3 t3.c
 $ ./t3 
        1666598  16.67%     
        1668630  16.69%
        1667682  16.68%
        1666049  16.66%
        1665948  16.66%
        1665093  16.65%
        total:  100.00%
 $ ./t3     
        1667634  16.68%
        1665914  16.66%
        1665542  16.66%
        1667828  16.68%
        1663649  16.64%
        1669433  16.69%
        total:  100.00%

Nie wiem, jak jednolite są twoje losowe liczby, ale powyższe wydaje się wystarczająco jednolite dla większości potrzeb.

Edycja: dobrym pomysłem byłoby zainicjowanie PRNG za pomocą czegoś lepszego niż time(NULL).


rand () może zawieść inne testy losowości, takie jak testy twarde . rand () różni się w zależności od platformy; Wartości rand () z GNU / Linux mogą być lepsze niż wartości z BSD lub Windows.
George Koehler,

To nie jest poprawny sposób na sprawdzenie losowości.
Veedrac

Zależy od celu i modelu zagrożenia / ryzyka. W przypadku silnie kryptograficznie RNG - na pewno użyj RDRAND (lub RDSEED). Dla zwykłego rzucającego kostką (nie na poziomie kasyna) IMHO powinno wystarczyć. Słowo kluczowe jest „ wystarczająco dobre ”.
Myszka

0

Miałem poważny problem z generatorem liczb pseudolosowych w mojej niedawnej aplikacji: wielokrotnie wywoływałem mój program C za pomocą skryptu pyhonona i użyłem jako źródła następującego kodu:

srand(time(NULL))

Jednak ponieważ:

  • rand wygeneruje tę samą pseudolosową sekwencję, podając to samo ziarno w srand (patrz man srand);
  • Jak już wspomniano, funkcja czasu zmienia się z sekundy na sekundę: jeśli aplikacja zostanie uruchomiona wiele razy w ciągu tej samej sekundy, timeza każdym razem zwróci tę samą wartość.

Mój program wygenerował tę samą sekwencję liczb. Możesz zrobić 3 rzeczy, aby rozwiązać ten problem:

  1. wymieszaj czas wyjściowy z kilkoma innymi informacjami zmieniającymi się podczas uruchomień (w mojej aplikacji nazwa wyjścia):

    srand(time(NULL) | getHashOfString(outputName))

    Użyłem djb2 jako mojej funkcji skrótu.

  2. Zwiększ rozdzielczość czasową. Na mojej platformie clock_gettimebył dostępny, więc go używam:

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec);
  3. Użyj obu metod razem:

    #include<time.h>
    struct timespec nanos;
    clock_gettime(CLOCK_MONOTONIC, &nanos)
    srand(nanos.tv_nsec | getHashOfString(outputName));

Opcja 3 zapewnia (o ile mi wiadomo) najlepszą losowość nasion, ale może stworzyć różnicę tylko przy bardzo szybkiej aplikacji. Moim zdaniem opcja 2 to bezpieczny zakład.


Nawet przy tej heurystyce nie polegaj na rand () w przypadku danych kryptograficznych.
domenukk

rand()nie należy używać do danych kryptograficznych, zgadzam się. Przynajmniej dla mnie moja aplikacja nie zawierała danych kryptograficznych, więc dla mnie była w porządku podana metoda.
Koldar

0

Pomimo wszystkich sugestii ludzi rand(), nie chcesz używać, rand()chyba że musisz! Liczby losowe, które rand()produkują, są często bardzo złe. Cytując ze strony podręcznika systemu Linux:

Wersje rand()iw srand()bibliotece Linux C Library używają tego samego generatora liczb losowych co random(3)i srandom(3), więc bity niższego rzędu powinny być tak losowe jak bity wyższego rzędu. Jednak w starszych implementacjach rand () i bieżących implementacjach w różnych systemach bity niższego rzędu są znacznie mniej losowe niż bity wyższego rzędu . Nie używaj tej funkcji w aplikacjach, które mają być przenośne, gdy potrzebna jest dobra losowość. ( Zamiast tego użyj random(3). )

Jeśli chodzi o przenośność, random()od dłuższego czasu jest również definiowany przez standard POSIX. rand()jest starszy, pojawił się już w pierwszej specyfikacji POSIX.1 (IEEE Std 1003.1-1988), podczas gdy random()po raz pierwszy pojawił się w POSIX.1-2001 (IEEE Std 1003.1-2001), ale obecny standard POSIX to już POSIX.1-2008 (IEEE Std 1003.1-2008), który otrzymał aktualizację zaledwie rok temu (IEEE Std 1003.1-2008, edycja 2016). Uważałbym więc, że random()jest bardzo przenośny.

POSIX.1-2001 wprowadził także funkcje lrand48()i mrand48(), patrz tutaj :

Ta rodzina funkcji generuje liczby pseudolosowe przy użyciu liniowego algorytmu kongruencjalnego i 48-bitowej arytmetyki liczb całkowitych.

I całkiem dobrym pseudolosowym źródłem jest arc4random()funkcja dostępna w wielu systemach. Nie jest częścią żadnego oficjalnego standardu, pojawił się w BSD około 1997 roku, ale można go znaleźć w systemach takich jak Linux i macOS / iOS.


random()nie istnieje w systemie Windows.
Björn Lindqvist

@ BjörnLindqvist Windows również nie jest systemem POSIX; jest to właściwie jedyny system na rynku, który nie obsługuje przynajmniej podstawowych interfejsów API POSIX (które obsługują nawet zablokowane systemy, takie jak iOS). System Windows obsługuje tylko te rand()funkcje, które są wymagane przez standard C. Do wszystkiego innego potrzebujesz specjalnego rozwiązania tylko dla systemu Windows, tak jak zwykle. #ifdef _WIN32to fraza, którą najczęściej widzisz w kodzie wieloplatformowym, który chce obsługiwać system Windows, a zwykle jest jedno rozwiązanie, które działa ze wszystkimi systemami i jedno, które jest wymagane tylko dla systemu Windows.
Mecki

0

W przypadku aplikacji Linux C:

To jest mój przerobiony kod z powyższej odpowiedzi, która jest zgodna z moimi praktykami kodu C i zwraca losowy bufor dowolnej wielkości (z odpowiednimi kodami powrotu itp.). Pamiętaj, aby zadzwonić urandom_open()raz na początku programu.

int gUrandomFd = -1;

int urandom_open(void)
{
    if (gUrandomFd == -1) {
        gUrandomFd = open("/dev/urandom", O_RDONLY);
    }

    if (gUrandomFd == -1) {
        fprintf(stderr, "Error opening /dev/urandom: errno [%d], strerrer [%s]\n",
                  errno, strerror(errno));
        return -1;
    } else {
        return 0;
    }
}


void urandom_close(void)
{
    close(gUrandomFd);
    gUrandomFd = -1;
}


//
// This link essentially validates the merits of /dev/urandom:
// http://sockpuppet.org/blog/2014/02/25/safely-generate-random-numbers/
//
int getRandomBuffer(uint8_t *buf, int size)
{
    int ret = 0; // Return value

    if (gUrandomFd == -1) {
        fprintf(stderr, "Urandom (/dev/urandom) file not open\n");
        return -1;
    }

    ret = read(gUrandomFd, buf, size);

    if (ret != size) {
        fprintf(stderr, "Only read [%d] bytes, expected [%d]\n",
                 ret, size);
        return -1;
    } else {
        return 0;
    }
}

-2

Moje minimalistyczne rozwiązanie powinno działać dla liczb losowych w zakresie [min, max). Użyj srand(time(NULL))przed wywołaniem funkcji.

int range_rand(int min_num, int max_num) {
    if (min_num >= max_num) {
        fprintf(stderr, "min_num is greater or equal than max_num!\n"); 
    }
    return min_num + (rand() % (max_num - min_num));
} 

-3

Wypróbuj to, zestawiłem to z niektórych pojęć, o których już wspomniałem powyżej:

/*    
Uses the srand() function to seed the random number generator based on time value,
then returns an integer in the range 1 to max. Call this with random(n) where n is an integer, and you get an integer as a return value.
 */

int random(int max) {
    srand((unsigned) time(NULL));
    return (rand() % max) + 1;
}

16
Ten kod nie jest dobry. Dzwonienie za srand()każdym razem, gdy chcesz zadzwonić, rand()to straszny pomysł. Ponieważ time()zwykle zwraca wartość w sekundach, wywołanie tej funkcji szybko zwróci tę samą „losową” wartość.
Blastfurnace

3
Ta funkcja byłaby mylona z random()funkcją Unixa .
George Koehler
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.