Generowanie liczb losowych w celu C


741

Jestem głównie głową Java i chcę sposób na wygenerowanie pseudolosowej liczby od 0 do 74. W Javie użyłbym metody:

Random.nextInt(74)

Nie interesuje mnie dyskusja na temat nasion ani prawdziwej przypadkowości, tylko tego, jak wykonujesz to samo zadanie w Objective-C. Przeszukałem Google i wydaje się, że jest wiele różnych i sprzecznych fragmentów informacji.

Odpowiedzi:


1025

Powinieneś użyć arc4random_uniform()funkcji. Wykorzystuje doskonały algorytm do rand. Nie musisz nawet ustawiać nasionka.

#include <stdlib.h>
// ...
// ...
int r = arc4random_uniform(74);

Strona arc4randompodręcznika:

NAME
     arc4random, arc4random_stir, arc4random_addrandom -- arc4 random number generator

LIBRARY
     Standard C Library (libc, -lc)

SYNOPSIS
     #include <stdlib.h>

     u_int32_t
     arc4random(void);

     void
     arc4random_stir(void);

     void
     arc4random_addrandom(unsigned char *dat, int datlen);

DESCRIPTION
     The arc4random() function uses the key stream generator employed by the arc4 cipher, which uses 8*8 8
     bit S-Boxes.  The S-Boxes can be in about (2**1700) states.  The arc4random() function returns pseudo-
     random numbers in the range of 0 to (2**32)-1, and therefore has twice the range of rand(3) and
     random(3).

     The arc4random_stir() function reads data from /dev/urandom and uses it to permute the S-Boxes via
     arc4random_addrandom().

     There is no need to call arc4random_stir() before using arc4random(), since arc4random() automatically
     initializes itself.

EXAMPLES
     The following produces a drop-in replacement for the traditional rand() and random() functions using
     arc4random():

           #define foo4random() (arc4random() % ((unsigned)RAND_MAX + 1))

96
Używaj w arc4random_uniform(x)sposób opisany poniżej przez @yood. Znajduje się również w stdlib.h (po OS X 10.7 i iOS 4.3) i zapewnia bardziej jednolity rozkład liczb losowych. Sposób użyciaint r = arc4random_uniform(74);
LavaSlider

4
Uwaga: rozkład z arc4random może być bardzo słaby, jeśli wybierzesz zły zakres. Nie zdawałem sobie sprawy z oczekiwań potęgi dwóch. +1 do użycia w wersji @ yood - zauważalna różnica dla większych liczb (np. Zakres 400)
Adam

Czy generuje tylko liczby 32-bitowe?
jjxtra

4
@codecowboy It nie. Zawsze zwraca liczbę całkowitą z zakresu [0, (2 ^ 32) -1]. Jest to moduł ograniczający górną granicę zakresu do określonej liczby.
Motasim

1
Czy to nie dałoby losowej liczby od 0 do 73?
Tom Howard

424

Użyj arc4random_uniform(upper_bound)funkcji, aby wygenerować losową liczbę w zakresie. Poniższe wygeneruje liczbę od 0 do 73 włącznie.

arc4random_uniform(74)

arc4random_uniform(upper_bound)unika modulo stronniczości, jak opisano na stronie podręcznika:

Funkcja arc4random_uniform () zwróci równomiernie rozłożoną liczbę losową mniejszą niż górna_końca. Funkcja arc4random_uniform () jest zalecana w stosunku do konstrukcji takich jak `` arc4random ()% upper_bound '', ponieważ pozwala uniknąć „ odchylenia modulo ”, gdy górna granica nie jest potęgą dwóch.


32
Pamiętaj, że arc4random_uniform () wymaga iOS 4.3. W przypadku obsługi starszych urządzeń należy dodać czek: #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_3 Jeśli test się nie powiedzie, wróć do innego rozwiązania.
Ron

6
arc4random_uniform () wymaga również wersji 10.7 lub nowszej. Awarie aplikacji w 10.6
Tibidabo

@Tibidabo Twój komentarz jest bardzo fałszywy i mylący. Właśnie zmęczyłem się używaniem arc4random_uniform () na iOS 10.3 i nie ma żadnych problemów. Nie wymaga wersji 10.7 ani nowszej
czwarty

1
@Fourth Nie ma czegoś takiego jak iOS 10.7, to macOS 10.7. Minęło ponad 5 lat, odkąd napisałem komentarz, wtedy było to maksymalnie iOS 5.
Tibidabo

63

Zrobiłbyś tak samo jak C.

#include <time.h>
#include <stdlib.h>
...
srand(time(NULL));
int r = rand() % 74;

(zakładając, że miałeś na myśli włączenie 0, ale wykluczenie 74, co robi twój przykład Java)

Edycja: Zapraszam do zamiany random()lub arc4random()na rand()(co, jak zauważyli inni, dość szczęśliwy).


43
-1. Musisz zaszczepić generator liczb losowych, w przeciwnym razie otrzymasz ten sam wzór liczb przy każdym wykonaniu.
Alex Reynolds,

A może chcę zacząć od innej liczby niż zero?
amok

2
@amok: Możesz po prostu dodać do wyniku numer, od którego chcesz zacząć
Florin

2
Ciągle dostaję numer 9. Dość losowo powiedziałbym; D
alexyorke

Właśnie przetestowałem random () i wykazał ten sam problem co rand ()
LolaRun

50

Pomyślałem, że mogę dodać metodę, której używam w wielu projektach.

- (NSInteger)randomValueBetween:(NSInteger)min and:(NSInteger)max {
    return (NSInteger)(min + arc4random_uniform(max - min + 1));
}

Jeśli ostatecznie używam go w wielu plikach, zwykle deklaruję makro jako

#define RAND_FROM_TO(min, max) (min + arc4random_uniform(max - min + 1))

Na przykład

NSInteger myInteger = RAND_FROM_TO(0, 74) // 0, 1, 2,..., 73, 74

Uwaga: Tylko dla iOS 4.3 / OS X 10.7 (Lion) i nowszych


Dodawanie jest przemienne, nawet na stałych z liczbami całkowitymi binarnymi za pomocą uzupełnień dwójkowych. Jako taki max - min + 1 jest dokładnie taki sam jak max + 1 - min oraz 1 + max - min.
Michael Morris,

44

To daje zmiennoprzecinkowych liczbę między 0 i 47

float low_bound = 0;      
float high_bound = 47;
float rndValue = (((float)arc4random()/0x100000000)*(high_bound-low_bound)+low_bound);

Lub po prostu

float rndValue = (((float)arc4random()/0x100000000)*47);

Zarówno dolna, jak i górna granica mogą być również ujemne . Poniższy przykładowy kod podaje losową liczbę między -35,76 a +12,09

float low_bound = -35.76;      
float high_bound = 12.09;
float rndValue = (((float)arc4random()/0x100000000)*(high_bound-low_bound)+low_bound);

Konwertuj wynik na zaokrągloną wartość całkowitą :

int intRndValue = (int)(rndValue + 0.5);

To jest złe. Dlaczego używasz liczby zmiennoprzecinkowej, a nie podwójnej? A jeśli wartości zmiennoprzecinkowe wynoszą od 0 do 47, to (int) (rndValue + 0,5) konwertuje tylko wartości od 0,0 do 0,5 do 0, ale wartości od 0,5 do 1,5 zostaną przekonwertowane na 1 itd. Więc liczby 0 i 47 pojawi się tylko o połowę częściej niż wszystkie inne liczby.
gnasher729

@ gnasher729 Przepraszam, nie rozumiem, o co ci chodzi. Oczywiście, jeśli potrzebujesz podwójnej precyzji, możesz łatwo zastąpić słowo „float” słowem „double”
Tibidabo

Nie sądzę, żeby to była wielka sprawa. Jeśli potrzebujesz tylko liczb całkowitych, możesz użyć arc4random () / 0x100000000) * (high_bound-low_bound) + low_bound, usuwając zmiennoprzecinkową / podwójną konwersję.
Tibidabo,

Otrzymujesz odchylenie, gdy konwertujesz liczbę całkowitą na zmiennoprzecinkową w ten sposób. Użyj drand48zamiast tego dla zmiennoprzecinkowych.
Franklin Yu

37

Według strony podręcznika dla rand (3) rodzina funkcji rand została zdezaktualizowana przez random (3). Wynika to z faktu, że dolne 12 bitów rand () przechodzi przez cykliczny wzorzec. Aby uzyskać liczbę losową, po prostu uruchom generator, wywołując srandom () z niepodpisanym ziarnem, a następnie wywołaj random (). Tak więc odpowiednikiem powyższego kodu będzie

#import <stdlib.h>
#import <time.h>

srandom(time(NULL));
random() % 74;

Będziesz musiał wywołać srandom () tylko raz w swoim programie, chyba że chcesz zmienić ziarno. Chociaż powiedziałeś, że nie chcesz dyskutować o naprawdę losowych wartościach, rand () jest dość złym generatorem liczb losowych, a random () nadal cierpi z powodu błędu modulo, ponieważ wygeneruje liczbę między 0 a RAND_MAX. Tak więc, np. Jeśli RAND_MAX wynosi 3, a chcesz losową liczbę od 0 do 2, masz dwa razy większe szanse na uzyskanie 0 niż 1 lub 2.


7
Równie dobrze możesz wywołać srandomdev () zamiast tracić czas na srandom (); jest tak samo łatwe i matematycznie lepsze.
benzado

31

Lepszy w użyciu arc4random_uniform. Nie jest to jednak dostępne pod iOS 4.3. Na szczęście iOS wiąże ten symbol w czasie wykonywania, a nie w czasie kompilacji (więc nie używaj dyrektywy preprocesora #if, aby sprawdzić, czy jest dostępna).

Najlepszym sposobem ustalenia, czy arc4random_uniformjest dostępny, jest zrobienie czegoś takiego:

#include <stdlib.h>

int r = 0;
if (arc4random_uniform != NULL)
    r = arc4random_uniform (74);
else
    r = (arc4random() % 74);

9
To pytanie dotyczy celu C, który wykorzystuje późne wiązanie. W przeciwieństwie do C, który wiąże się w czasie kompilacji / łączenia, Objective-C wiąże symbole w czasie wykonywania, a symbole, których nie może powiązać, są ustawione na NULL. Chociaż masz rację, że nie jest to poprawne C, to z pewnością jest to Cel C. Używam dokładnie tego kodu w mojej aplikacji na iPhone'a. [ps proszę czy możesz poprawić swoje zdanie].
AW101

Podczas gdy cel-c używa późnego wiązania dla metod objc, w przypadku funkcji C tak nie jest. Ten kod z pewnością ulegnie awarii, jeśli funkcja nie istnieje w czasie wykonywania.
Richard J. Ross III

8
Według Apple „... łącznik ustawia adres niedostępnych funkcji na NULL ...”, patrz Listing 3.2: developer.apple.com/library/mac/#documentation/DeveloperTools/... . Ok, więc musi być słabo powiązany, ale nie ulega awarii.
AW101,

1
Sprawdzanie, czy adres funkcji ma wartość NULL, jest metodą stosowaną we wszystkich wersjach C, C ++ i Objective-C zarówno na MacOS X, jak i iOS.
gnasher729

14

Napisałem własną klasę narzędzi liczb losowych tylko po to, aby mieć coś, co działałoby trochę bardziej jak Math.random () w Javie. Ma tylko dwie funkcje i wszystko jest wykonane w C.

Plik nagłówka:

//Random.h
void initRandomSeed(long firstSeed);
float nextRandomFloat();

Plik implementacyjny:

//Random.m
static unsigned long seed;

void initRandomSeed(long firstSeed)
{ 
    seed = firstSeed;
}

float nextRandomFloat()
{
    return (((seed= 1664525*seed + 1013904223)>>16) / (float)0x10000);
}

To dość klasyczny sposób generowania pseudolosowych. W mojej aplikacji delegowanej dzwonię:

#import "Random.h"

- (void)applicationDidFinishLaunching:(UIApplication *)application
{
    initRandomSeed( (long) [[NSDate date] timeIntervalSince1970] );
    //Do other initialization junk.
}

Potem po prostu mówię:

float myRandomNumber = nextRandomFloat() * 74;

Zauważ, że ta metoda zwraca liczbę losową od 0,0f (włącznie) do 1,0f (wyłącznie).


3
1. Funkcje liczb losowych tworzone losowo zwykle nie są zbyt losowe. 2. Jest całkowicie uszkodzony na 64-bitowym procesorze. 3. Używanie sekund od 1970 r. Jako losowego materiału siewnego sprawia, że ​​liczby są przewidywalne.
gnasher729

7

Istnieje już kilka świetnych, wyraźnych odpowiedzi, ale pytanie wymaga losowej liczby od 0 do 74. Użyj:

arc4random_uniform(75)


4

Począwszy od iOS 9 i OS X 10.11, możesz używać nowych klas GameplayKit do generowania liczb losowych na różne sposoby.

Masz do wyboru cztery typy źródeł: ogólne losowe źródło (bez nazwy, aż do systemu, który wybierze, co robi), liniowy congruential, ARC4 i Mersenne Twister. Mogą generować losowe liczby całkowite, zmiennoprzecinkowe i boole.

Na najprostszym poziomie możesz wygenerować losową liczbę z wbudowanego losowego źródła systemu, jak to:

NSInteger rand = [[GKRandomSource sharedRandom] nextInt];

To generuje liczbę między -2 147 483 648 a 2 147 483 647. Jeśli chcesz liczbę z zakresu od 0 do górnej granicy (wyłącznie), użyj tego:

NSInteger rand6 = [[GKRandomSource sharedRandom] nextIntWithUpperBound:6];

GameplayKit ma wbudowane kilka wygodnych konstruktorów do pracy z kostkami. Na przykład możesz rzucić sześciościenną kostką w następujący sposób:

GKRandomDistribution *d6 = [GKRandomDistribution d6];
[d6 nextInt];

Ponadto możesz kształtować losowy rozkład, używając rzeczy takich jak GKShuffledDistribution.


Mersenne jest najszybszy i najlepszy w przypadku gier, w których jakość generowanej liczby losowej nie jest zwykle zbyt ważna.
Robert Wasmann

3

Wygeneruj losową liczbę od 0 do 99:

int x = arc4random()%100;

Wygeneruj losową liczbę od 500 do 1000:

int x = (arc4random()%501) + 500;

1

// Poniższy przykład wygeneruje liczbę od 0 do 73.

int value;
value = (arc4random() % 74);
NSLog(@"random number: %i ", value);

//In order to generate 1 to 73, do the following:
int value1;
value1 = (arc4random() % 73) + 1;
NSLog(@"random number step 2: %i ", value1);

Wynik:

  • losowa liczba: 72

  • liczba losowa krok 2: 52


1

Dla twórców gier użyj random () do generowania losowych. Prawdopodobnie co najmniej 5 razy szybszy niż użycie arc4random (). Odchylenie modulo nie stanowi problemu, szczególnie w grach, w przypadku generowania losowych przy użyciu pełnego zakresu random (). Pamiętaj, aby najpierw posiać. Wywołaj srandomdev () w AppDelegate. Oto kilka funkcji pomocniczych:

static inline int random_range(int low, int high){ return (random()%(high-low+1))+low;}
static inline CGFloat frandom(){ return (CGFloat)random()/UINT32_C(0x7FFFFFFF);}
static inline CGFloat frandom_range(CGFloat low, CGFloat high){ return (high-low)*frandom()+low;}

Pamiętaj jednak, że random () nie jest tak losowy, więc jeśli prędkość nie jest ważna w twoim kodzie (tak jakby był używany tylko raz na jakiś czas), użyj arc4random ().
Robert Wasmann
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.