Zufallszahlen in Objective-C generieren


741

Ich bin hauptsächlich ein Java-Kopf und möchte eine Möglichkeit finden, eine Pseudozufallszahl zwischen 0 und 74 zu generieren. In Java würde ich die Methode verwenden:

Random.nextInt(74)

Ich bin nicht an einer Diskussion über Samen oder echte Zufälligkeit interessiert, nur wie Sie dieselbe Aufgabe in Ziel-C erfüllen. Ich habe Google durchsucht und es scheint nur viele verschiedene und widersprüchliche Informationen zu geben.

Antworten:


1025

Sie sollten die arc4random_uniform()Funktion verwenden. Es wird ein überlegener Algorithmus verwendet rand. Sie müssen nicht einmal einen Samen setzen.

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

Die arc4randomManpage:

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
Verwenden Sie arc4random_uniform(x)wie unten von @yood beschrieben. Es befindet sich auch in stdlib.h (nach OS X 10.7 und iOS 4.3) und bietet eine gleichmäßigere Verteilung der Zufallszahlen. Verwendungint r = arc4random_uniform(74);
LavaSlider

4
NB: Die Verteilung von arc4random kann sehr schlecht sein, wenn Sie zufällig einen schlechten Bereich wählen. Ich hatte die Zwei-Kräfte-Erwartung nicht erkannt. +1 für die Verwendung von @ yoods Version - machte einen spürbaren Unterschied für größere Zahlen (z. B. Bereich von 400)
Adam

Erzeugt es nur 32-Bit-Zahlen?
jjxtra

4
@codecowboy Das tut es nicht. Es wird immer eine Ganzzahl im Bereich [0, (2 ^ 32) -1] zurückgegeben. Es ist das Modulo, das die Obergrenze des Bereichs auf die von Ihnen angegebene Zahl begrenzt.
Motasim

1
Würde dies nicht eine Zufallszahl zwischen 0 und 73 ergeben?
Tom Howard

424

Verwenden Sie die arc4random_uniform(upper_bound)Funktion, um eine Zufallszahl innerhalb eines Bereichs zu generieren. Im Folgenden wird eine Zahl zwischen 0 und einschließlich 73 generiert.

arc4random_uniform(74)

arc4random_uniform(upper_bound)vermeidet Modulo Bias wie in der Manpage beschrieben:

arc4random_uniform () gibt eine gleichmäßig verteilte Zufallszahl zurück, die kleiner als die obere Grenze ist. arc4random_uniform () wird gegenüber Konstruktionen wie "arc4random ()% Upper_bound" empfohlen, da es " Modulo Bias " vermeidet, wenn die Obergrenze keine Zweierpotenz ist.


32
Beachten Sie, dass für arc4random_uniform () iOS 4.3 erforderlich ist. #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_3 Wenn Sie ältere Geräte unterstützen, sollten Sie eine Prüfung hinzufügen: Wenn die Prüfung fehlschlägt, greifen Sie auf eine andere Lösung zurück.
Ron

6
arc4random_uniform () benötigt ebenfalls 10.7 oder höher. Es stürzt Ihre App in 10.6
Tibidabo

@ Tibidabo Dein Kommentar ist sehr falsch und irreführend. Ich bin nur müde mit arc4random_uniform () unter iOS 10.3 und es gibt keine Probleme. Es erfordert nicht 10.7 oder höher
4.

1
@Fourth Es gibt kein iOS 10.7, es ist macOS 10.7. Es ist mehr als 5 Jahre her, seit ich den Kommentar geschrieben habe, dass es damals maximal iOS 5 war.
Tibidabo

63

Genau wie C würden Sie tun

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

(Angenommen, Sie wollten 0 einschließen, aber 74 ausschließen, was in Ihrem Java-Beispiel der Fall ist.)

Edit: Fühlen Sie sich frei zu ersetzen random()oder arc4random()für rand()(die, wie andere haben darauf hingewiesen, ganz sucky).


43
-1. Sie müssen den Zufallszahlengenerator setzen, sonst erhalten Sie bei jeder Ausführung das gleiche Zahlenmuster.
Alex Reynolds

Wie wäre es, wenn ich von einer anderen Zahl als Null ausgehen möchte?
Amok

2
@amok: Sie können einfach die Nummer, von der Sie beginnen möchten, zum Ergebnis hinzufügen
Florin

2
Ich
bekomme

Ich habe gerade random () getestet und es zeigte das gleiche Problem wie rand ()
LolaRun

50

Ich dachte, ich könnte eine Methode hinzufügen, die ich in vielen Projekten verwende.

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

Wenn ich es in vielen Dateien verwende, deklariere ich normalerweise ein Makro als

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

Z.B

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

Hinweis: Nur für iOS 4.3 / OS X 10.7 (Lion) und höher


Die Addition ist kommutativ, selbst wenn sie mit binären Ganzzahlen unter Verwendung eines Zweierkomplements festgelegt ist. Als solches ist max - min + 1 genau das gleiche wie max + 1 - min sowie 1 + max - min.
Michael Morris

44

Dadurch erhalten Sie eine Gleitkommazahl zwischen 0 und 47

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

Oder einfach nur

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

Sowohl die untere als auch die obere Grenze können ebenfalls negativ sein . Der folgende Beispielcode gibt Ihnen eine Zufallszahl zwischen -35,76 und +12,09

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

Konvertieren Sie das Ergebnis in einen runderen Integer- Wert:

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

Das ist schlecht. Warum verwenden Sie float und nicht double? Und wenn Ihre Gleitkommawerte zwischen 0 und 47 liegen, konvertiert (int) (rndValue + 0.5) nur Werte von 0.0 bis 0.5 in 0, aber Werte von 0.5 bis 1.5 werden in 1 usw. konvertiert. Also die Zahlen 0 und 47 wird nur halb so oft auftauchen wie alle anderen Zahlen.
Gnasher729

@ gnasher729 Entschuldigung, ich verstehe deinen Standpunkt nicht. Wenn Sie doppelte Präzision benötigen, können Sie "float" natürlich einfach durch "double" ersetzen
Tibidabo

Ich denke nicht, dass es eine große Sache ist. Wenn Sie nur ganzzahlige Werte benötigen, können Sie arc4random () / 0x100000000) * (high_bound-low_bound) + low_bound verwenden, indem Sie die float / double-Konvertierung entfernen.
Tibidabo

Sie erhalten eine Verzerrung, wenn Sie auf diese Weise eine Ganzzahl in Gleitkomma konvertieren. Verwenden Sie drand48stattdessen für Gleitkommazahlen.
Franklin Yu

37

Laut der Handbuchseite für rand (3) wurde die rand-Funktionsfamilie zufällig (3) überholt. Dies liegt an der Tatsache, dass die unteren 12 Bits von rand () ein zyklisches Muster durchlaufen. Um eine Zufallszahl zu erhalten, setzen Sie einfach den Generator, indem Sie srandom () mit einem vorzeichenlosen Startwert aufrufen, und rufen Sie dann random () auf. Das Äquivalent des obigen Codes wäre also

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

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

Sie müssen srandom () nur einmal in Ihrem Programm aufrufen, es sei denn, Sie möchten Ihren Startwert ändern. Obwohl Sie sagten, Sie wollten keine Diskussion über wirklich zufällige Werte, ist rand () ein ziemlich schlechter Zufallszahlengenerator, und random () leidet immer noch unter Modulo Bias, da es eine Zahl zwischen 0 und RAND_MAX erzeugt. Wenn also RAND_MAX 3 ist und Sie eine Zufallszahl zwischen 0 und 2 möchten, ist die Wahrscheinlichkeit, dass Sie eine 0 als eine 1 oder eine 2 erhalten, doppelt so hoch.


7
Sie können auch srandomdev () aufrufen, anstatt die Zeit an srandom () zu übergeben. es ist genauso einfach und mathematisch besser.
Benzado

31

Besser zu benutzen arc4random_uniform. Dies ist jedoch unter iOS 4.3 nicht verfügbar. Glücklicherweise bindet iOS dieses Symbol zur Laufzeit und nicht zur Kompilierungszeit (verwenden Sie also nicht die Präprozessor-Direktive #if, um zu überprüfen, ob es verfügbar ist).

Der beste Weg, um festzustellen, ob arc4random_uniformverfügbar ist, besteht darin, Folgendes zu tun:

#include <stdlib.h>

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

9
Diese Frage bezieht sich auf Objective-C, bei dem eine späte Bindung verwendet wird. Im Gegensatz zu C, das zur Kompilierungs- / Verknüpfungszeit bindet, bindet Objective-C Symbole zur Laufzeit, und Symbole, die nicht gebunden werden können, werden auf NULL gesetzt. Während Sie Recht haben, dass dies kein gültiges C ist, ist es mit Sicherheit ein gültiges Ziel-C. Ich verwende genau diesen Code in meiner iPhone App. [ps bitte kannst du deine downvote korrigieren].
AW101

Während Objective-C für objc-Methoden eine späte Bindung verwendet, ist dies für eine C-Funktion nicht der Fall. Dieser Code wird sicherlich abstürzen, wenn die Funktion zur Laufzeit nicht vorhanden ist.
Richard J. Ross III

8
Laut Apple "... setzt der Linker die Adresse nicht verfügbarer Funktionen auf NULL ...", siehe Listing 3.2: developer.apple.com/library/mac/#documentation/DeveloperTools/… . Ok, es muss schwach verknüpft sein, aber es stürzt nicht ab.
AW101

1
Das Überprüfen, ob die Adresse einer Funktion NULL ist, wurde in allen Versionen von C, C ++ und Objective-C sowohl unter MacOS X als auch unter iOS verwendet.
Gnasher729

14

Ich habe meine eigene Zufallszahlen-Dienstprogrammklasse geschrieben, damit ich etwas habe, das ein bisschen mehr wie Math.random () in Java funktioniert. Es hat nur zwei Funktionen und alles ist in C hergestellt.

Header-Datei:

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

Implementierungsdatei:

//Random.m
static unsigned long seed;

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

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

Es ist eine ziemlich klassische Art, Pseudo-Zufälle zu erzeugen. In meinem App-Delegaten rufe ich an:

#import "Random.h"

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

Dann später sage ich einfach:

float myRandomNumber = nextRandomFloat() * 74;

Beachten Sie, dass diese Methode eine Zufallszahl zwischen 0.0f (einschließlich) und 1.0f (exklusiv) zurückgibt.


3
1. Zufällig erstellte Zufallszahlenfunktionen sind normalerweise nicht sehr zufällig. 2. Auf einem 64-Bit-Prozessor ist es völlig kaputt. 3. Die Verwendung von Sekunden seit 1970 als zufälliger Startwert macht die Zahlen vorhersehbar.
Gnasher729

7

Es gibt bereits einige gute, artikulierte Antworten, aber die Frage fragt nach einer Zufallszahl zwischen 0 und 74. Verwendung:

arc4random_uniform(75)


4

Ab iOS 9 und OS X 10.11 können Sie die neuen GameplayKit-Klassen verwenden, um Zufallszahlen auf verschiedene Arten zu generieren.

Sie haben vier Quelltypen zur Auswahl: eine allgemeine Zufallsquelle (unbenannt, bis auf das System, um auszuwählen, was sie tut), linear kongruent, ARC4 und Mersenne Twister. Diese können zufällige Ints, Floats und Bools erzeugen.

Auf der einfachsten Ebene können Sie eine Zufallszahl aus der integrierten Zufallsquelle des Systems wie folgt generieren:

NSInteger rand = [[GKRandomSource sharedRandom] nextInt];

Das ergibt eine Zahl zwischen -2.147.483.648 und 2.147.483.647. Wenn Sie eine Zahl zwischen 0 und einer Obergrenze (exklusiv) möchten, verwenden Sie Folgendes:

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

GameplayKit verfügt über einige praktische Konstruktoren, die für die Arbeit mit Würfeln integriert sind. Zum Beispiel können Sie einen sechsseitigen Würfel wie folgt würfeln:

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

Außerdem können Sie die zufällige Verteilung mithilfe von Dingen wie gestalten GKShuffledDistribution.


Mersenne ist am schnellsten und am besten für Dinge wie Spieleentwickler geeignet, bei denen die Qualität der generierten Zufallszahl normalerweise nicht allzu wichtig ist.
Robert Wasmann

3

Zufallszahl zwischen 0 und 99 generieren:

int x = arc4random()%100;

Zufallszahl zwischen 500 und 1000 generieren:

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

1

// Das folgende Beispiel generiert eine Zahl zwischen 0 und 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);

Ausgabe:

  • Zufallszahl: 72

  • Zufallszahl Schritt 2: 52


1

Verwenden Sie für Spieleentwickler random (), um Zufälle zu generieren. Wahrscheinlich mindestens 5x schneller als mit arc4random (). Die Modulo-Verzerrung ist insbesondere bei Spielen kein Problem, wenn Zufälle mit dem gesamten Bereich von random () generiert werden. Achten Sie darauf, zuerst zu säen. Rufen Sie srandomdev () in AppDelegate auf. Hier sind einige Hilfsfunktionen:

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;}

Beachten Sie jedoch, dass random () nicht ganz so zufällig ist. Wenn also die Geschwindigkeit in Ihrem Code nicht wichtig ist (z. B. wenn sie nur gelegentlich verwendet wird), verwenden Sie arc4random ().
Robert Wasmann
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.