Générer des nombres aléatoires dans Objective-C

741

Je suis principalement une tête Java, et je veux un moyen de générer un nombre pseudo-aléatoire entre 0 et 74. En Java, j'utiliserais la méthode:

Random.nextInt(74)

Je ne suis pas intéressé par une discussion sur les graines ou le vrai hasard, juste comment vous accomplissez la même tâche dans Objective-C. J'ai parcouru Google, et il semble qu'il y ait beaucoup d'informations différentes et contradictoires.

rustyshelf
la source

Réponses:

1025

Vous devez utiliser la arc4random_uniform()fonction. Il utilise un algorithme supérieur pour rand. Vous n'avez même pas besoin de définir une graine.

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

La arc4randompage de manuel:

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))
lajos
la source
96
Utilisez arc4random_uniform(x)comme décrit ci-dessous par @yood. Il se trouve également dans stdlib.h (après OS X 10.7 et iOS 4.3) et donne une distribution plus uniforme des nombres aléatoires. Utilisationint r = arc4random_uniform(74);
LavaSlider
4
NB: la distribution depuis arc4random peut être très mauvaise, si vous choisissez une mauvaise plage. Je n'avais pas réalisé l'attente du pouvoir de deux. +1 pour utiliser la version de @ yood - a fait une différence notable pour les plus grands nombres (par exemple une plage de 400)
Adam
Génère-t-il uniquement des nombres 32 bits?
jjxtra
4
@codecowboy Ce n'est pas le cas. Il renvoie toujours un entier dans la plage [0, (2 ^ 32) -1]. C'est le modulo qui limite la limite supérieure de la plage au nombre que vous spécifiez.
Motasim
1
Cela ne donnerait-il pas un nombre aléatoire entre 0 et 73?
Tom Howard
424

Utilisez la arc4random_uniform(upper_bound)fonction pour générer un nombre aléatoire dans une plage. Les éléments suivants généreront un nombre compris entre 0 et 73 inclus.

arc4random_uniform(74)

arc4random_uniform(upper_bound)évite les biais modulo comme décrit dans la page de manuel:

arc4random_uniform () renverra un nombre aléatoire uniformément distribué inférieur à upper_bound. arc4random_uniform () est recommandé par rapport aux constructions comme `` arc4random ()% upper_bound '' car il évite le " biais modulo " lorsque la limite supérieure n'est pas une puissance de deux.

bon
la source
32
Notez que arc4random_uniform () nécessite iOS 4.3. Si vous prenez en charge des appareils plus anciens, vous devez ajouter une vérification: #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_3 si la vérification échoue, revenez à une autre solution.
Ron
6
arc4random_uniform () requiert également 10.7 ou une version ultérieure. Il bloque votre application en 10.6
Tibidabo
@Tibidabo Votre commentaire est très faux et trompeur. Je suis juste fatigué d'utiliser arc4random_uniform () sur iOS 10.3 et il n'y a aucun problème. Il ne nécessite pas 10,7 ou plus tard
Quatrième
1
@Fourth Il n'existe pas d'iOS 10.7, c'est macOS 10.7. Cela fait plus de 5 ans que j'ai écrit le commentaire que c'était max iOS 5 à l'époque.
Tibidabo
63

Comme C, vous feriez

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

(en supposant que vous vouliez inclure 0 mais exclure 74, ce que fait votre exemple Java)

Edit: Ne hésitez pas à remplacer random()ou arc4random()pour rand()( ce qui est, comme d' autres l' ont souligné, tout à fait sucky).

James Ko
la source
43
-1. Vous devez amorcer le générateur de nombres aléatoires ou vous obtiendrez le même modèle de nombres à chaque exécution.
Alex Reynolds
Que diriez-vous que je veux commencer à partir d'un nombre différent de zéro?
amok
2
@amok: Vous pouvez simplement ajouter le nombre à partir duquel vous voulez commencer
Florin
2
Je continue à obtenir le numéro 9. Je dirais
plutôt
je viens de tester random (), et cela a montré le même problème que rand ()
LolaRun
50

J'ai pensé pouvoir ajouter une méthode que j'utilise dans de nombreux projets.

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

Si je finis par l'utiliser dans de nombreux fichiers, je déclare généralement une macro

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

Par exemple

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

Remarque: Uniquement pour iOS 4.3 / OS X v10.7 (Lion) et versions ultérieures

Groot
la source
L'addition est commutative, même sur fixe avec des entiers binaires utilisant un complément à deux. En tant que tel, max - min + 1 est exactement le même que max + 1 - min ainsi que 1 + max - min.
Michael Morris
44

Cela vous donnera un nombre à virgule flottante compris entre 0 et 47

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

Ou tout simplement

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

Les limites inférieure et supérieure peuvent également être négatives . L'exemple de code ci-dessous vous donne un nombre aléatoire compris entre -35,76 et +12,09

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

Convertir le résultat en une valeur entière arrondie :

int intRndValue = (int)(rndValue + 0.5);
Tibidabo
la source
C'est mauvais. Pourquoi utilisez-vous float, et pas double? Et si vos valeurs à virgule flottante sont comprises entre 0 et 47, alors (int) (rndValue + 0,5) convertira uniquement les valeurs de 0,0 à 0,5 à 0, mais les valeurs de 0,5 à 1,5 seront converties en 1, etc. Les nombres 0 et 47 n'apparaîtront que la moitié aussi souvent que tous les autres chiffres.
gnasher729
@ gnasher729 Désolé, je ne vois pas votre point. Évidemment, si vous avez besoin d'une double précision, vous pouvez facilement remplacer "float" par "double"
Tibidabo
Je ne pense pas que ce soit un gros problème. Si vous avez uniquement besoin de valeurs entières, vous pouvez utiliser arc4random () / 0x100000000) * (high_bound-low_bound) + low_bound en supprimant la conversion float / double.
Tibidabo
Vous obtenez un biais lorsque vous convertissez un entier en virgule flottante de cette façon. Utilisez drand48plutôt des virgules flottantes.
Franklin Yu
37

Selon la page de manuel de rand (3), la famille de fonctions rand a été obsolète par random (3). Cela est dû au fait que les 12 bits inférieurs de rand () passent par un modèle cyclique. Pour obtenir un nombre aléatoire, il suffit d'amorcer le générateur en appelant srandom () avec une graine non signée, puis d'appeler random (). Ainsi, l'équivalent du code ci-dessus serait

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

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

Vous n'aurez besoin d'appeler srandom () qu'une seule fois dans votre programme, sauf si vous souhaitez modifier votre graine. Bien que vous ayez dit que vous ne vouliez pas discuter de valeurs vraiment aléatoires, rand () est un assez mauvais générateur de nombres aléatoires, et random () souffre toujours d'un biais modulo, car il générera un nombre compris entre 0 et RAND_MAX. Par exemple, si RAND_MAX est 3 et que vous voulez un nombre aléatoire entre 0 et 2, vous êtes deux fois plus susceptible d'obtenir un 0 qu'un 1 ou un 2.

Michael Buckley
la source
7
Vous pourriez aussi bien appeler srandomdev () au lieu de passer le temps à srandom (); c'est tout aussi simple et mathématiquement meilleur.
benzado
31

Mieux à utiliser arc4random_uniform. Cependant, cela n'est pas disponible sous iOS 4.3. Heureusement, iOS liera ce symbole au moment de l'exécution, pas au moment de la compilation (alors n'utilisez pas la directive de préprocesseur #if pour vérifier s'il est disponible).

La meilleure façon de déterminer si elle arc4random_uniformest disponible est de faire quelque chose comme ceci:

#include <stdlib.h>

int r = 0;
if (arc4random_uniform != NULL)
    r = arc4random_uniform (74);
else
    r = (arc4random() % 74);
AW101
la source
9
Cette question concerne Objective-C qui utilise une liaison tardive. Contrairement à C qui se lie au moment de la compilation / liaison, Objective-C lie les symboles au moment de l'exécution, et les symboles qu'il ne peut pas lier sont définis sur NULL. Bien que vous ayez raison de dire qu'il ne s'agit pas d'un C valide, il s'agit très certainement d'un Objective-C valide. J'utilise ce code exact dans mon application iPhone. [ps s'il vous plaît pouvez-vous corriger votre downvote].
AW101
Alors que objective-c utilise une liaison tardive pour les méthodes objc, pour une fonction C, ce n'est pas le cas. Ce code se bloquera certainement si la fonction n'existe pas au moment de l'exécution.
Richard J. Ross III
8
Selon Apple "... l'éditeur de liens définit l'adresse des fonctions non disponibles sur NULL ...", voir la liste 3.2: developer.apple.com/library/mac/#documentation/DeveloperTools/… . D'accord, il doit donc être faiblement lié, mais il ne plante pas.
AW101
1
Vérifier que l'adresse d'une fonction est NULL est une méthode qui a été utilisée dans toutes les versions de C, C ++ et Objective-C sous MacOS X et iOS.
gnasher729
14

J'ai écrit ma propre classe d'utilité de nombres aléatoires juste pour que j'aie quelque chose qui fonctionne un peu plus comme Math.random () en Java. Il n'a que deux fonctions, et tout est fait en C.

En tête de fichier:

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

Fichier d'implémentation:

//Random.m
static unsigned long seed;

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

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

C'est une façon assez classique de générer des pseudo-aléas. Dans mon délégué d'application, j'appelle:

#import "Random.h"

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

Plus tard, je dis simplement:

float myRandomNumber = nextRandomFloat() * 74;

Notez que cette méthode renvoie un nombre aléatoire compris entre 0,0f (inclus) et 1,0f (exclusif).

Eli
la source
3
1. Les fonctions numériques aléatoires créées au hasard ne sont généralement pas très aléatoires. 2. Il est totalement cassé sur un processeur 64 bits. 3. L'utilisation de secondes depuis 1970 comme graine aléatoire rend les nombres prévisibles.
gnasher729
7

Il existe déjà d'excellentes réponses articulées, mais la question demande un nombre aléatoire compris entre 0 et 74. Utilisez:

arc4random_uniform(75)

Tom Howard
la source
4

Depuis iOS 9 et OS X 10.11, vous pouvez utiliser les nouvelles classes GameplayKit pour générer des nombres aléatoires de différentes manières.

Vous avez le choix entre quatre types de sources: une source aléatoire générale (sans nom, jusqu'au système pour choisir ce qu'il fait), congruentielle linéaire, ARC4 et Mersenne Twister. Ceux-ci peuvent générer des ints, des flottants et des bools aléatoires.

Au niveau le plus simple, vous pouvez générer un nombre aléatoire à partir de la source aléatoire intégrée du système comme ceci:

NSInteger rand = [[GKRandomSource sharedRandom] nextInt];

Cela génère un nombre compris entre -2 147 483 648 et 2 147 483 647. Si vous voulez un nombre entre 0 et une limite supérieure (exclusive), vous utiliseriez ceci:

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

GameplayKit a des constructeurs de commodité intégrés pour fonctionner avec les dés. Par exemple, vous pouvez lancer un dé à six faces comme ceci:

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

De plus, vous pouvez façonner la distribution aléatoire en utilisant des choses comme GKShuffledDistribution.

TwoStraws
la source
Mersenne est le plus rapide et le meilleur pour des choses comme le développement de jeux où la qualité du nombre aléatoire généré n'est généralement pas trop importante.
Robert Wasmann
3

Générez un nombre aléatoire entre 0 et 99:

int x = arc4random()%100;

Générez un nombre aléatoire entre 500 et 1000:

int x = (arc4random()%501) + 500;
adijazz91
la source
1

// L'exemple suivant va générer un nombre compris entre 0 et 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);

Production:

  • nombre aléatoire: 72

  • nombre aléatoire étape 2:52

soumya
la source
1

Pour les développeurs de jeux, utilisez random () pour générer des aléas. Probablement au moins 5 fois plus rapide qu'avec arc4random (). Le biais Modulo n'est pas un problème, en particulier pour les jeux, lors de la génération d'aléas en utilisant la gamme complète de random (). Assurez-vous de semer en premier. Appelez srandomdev () dans AppDelegate. Voici quelques fonctions d'assistance:

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;}
Robert Wasmann
la source
Sachez cependant que random () n'est pas aussi aléatoire, donc si la vitesse n'est pas importante dans votre code (comme si elle n'était utilisée qu'une fois de temps en temps), utilisez arc4random ().
Robert Wasmann