754 votes

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

Je suis une tête en Java principalement, 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, mais simplement par la façon dont on peut accomplir la même tâche en Objective-C. J'ai parcouru Google, et il semble y avoir beaucoup d'informations différentes et contradictoires.

1036voto

lajos Points 13791

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

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

El arc4random page 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))

97 votes

Utilice arc4random_uniform(x) comme décrit ci-dessous par @yood. Elle 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. Utilisation int r = arc4random_uniform(74);

4 votes

NB : la distribution de arc4random peut être très mauvaise, si vous choisissez une mauvaise plage. Je n'avais pas réalisé l'attente des puissances de deux. +1 pour l'utilisation de la version de @yood - a fait une différence notable pour les grands nombres (par exemple, une plage de 400).

0 votes

Génère-t-il uniquement des nombres de 32 bits ?

429voto

yood Points 3642

Utilisez le arc4random_uniform(upper_bound) pour générer un nombre aléatoire dans une plage donnée. La fonction suivante générera un nombre compris entre 0 et 73 inclus.

arc4random_uniform(74)

arc4random_uniform(upper_bound) évite modulo biais 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 à des constructions comme "arc4random() % upper_bound" car il évite " modulo biais "lorsque la limite supérieure n'est pas une puissance de deux.

32 votes

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 S'il échoue à la vérification, revenez à une autre solution.

6 votes

Arc4random_uniform() nécessite également la version 10.7 ou ultérieure. Il fait planter votre application en 10.6

0 votes

@Tibidabo Votre commentaire est très faux et trompeur. Je viens de me fatiguer à utiliser arc4random_uniform() sur iOS 10.3 et il n'y a aucun problème. Il n'a pas besoin de 10.7 ou plus.

65voto

Comme pour le C, vous feriez

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

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

Edit : N'hésitez pas à remplacer random() o arc4random() para rand() (ce qui est, comme d'autres l'ont souligné, assez nul).

45 votes

-1. Vous devez ensemencer le générateur de nombres aléatoires, sinon vous obtiendrez le même modèle de nombres à chaque exécution.

0 votes

Et si je voulais partir d'un nombre différent de zéro ?

3 votes

@amok : Vous pouvez simplement ajouter le nombre à partir duquel vous voulez commencer au résultat.

51voto

Filip Points 2856

J'ai pensé que je pourrais 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 comme étant

#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 ultérieur.

0 votes

L'addition est commutative, même sur une base fixe avec des entiers binaires utilisant le complément à deux. Ainsi, max - min + 1 est exactement la même chose que max + 1 - min ainsi que 1 + max - min.

44voto

Tibidabo Points 10510

Cela vous donnera un virgule flottante nombre 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 être négatif également. L'exemple de code ci-dessous vous donne un nombre aléatoire 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 un rond Entier valeur :

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

0 votes

C'est mauvais. Pourquoi utilisez-vous float, et non double ? Et si vos valeurs à virgule flottante sont comprises entre 0 et 47, alors (int) (rndValue + 0,5) ne convertira que les valeurs de 0,0 à 0,5 en 0, mais les valeurs de 0,5 à 1,5 seront converties en 1, etc. Ainsi, les nombres 0 et 47 apparaîtront deux fois moins souvent que tous les autres nombres.

0 votes

@gnasher729 Désolé je ne vois pas où vous voulez en venir. Évidemment, si vous avez besoin d'une double précision, vous pouvez facilement remplacer "float" par "double".

0 votes

Je ne pense pas que ce soit un gros problème. Si vous avez besoin de valeurs entières uniquement, vous pouvez utiliser arc4random()/0x100000000)*(high_bound-low_bound)+low_bound en supprimant la conversion float/double.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X