Генерація випадкових чисел у Objective-C


741

Я головним чином голова Java, і я хочу, щоб генерувати псевдовипадкове число між 0 і 74. У Java я використовував би метод:

Random.nextInt(74)

Мене не цікавить дискусія про насіння або справжню випадковість, а саме те, як ви виконаєте те саме завдання в «Objective-C». Я переглянув Google, і, здається, є багато різних та суперечливих бітів інформації.

Відповіді:


1025

Вам слід скористатися arc4random_uniform()функцією. Він використовує чудовий алгоритм для rand. Вам навіть не потрібно встановлювати насіння.

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

Сторінка arc4randomлюдини:

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
Використовуйте, arc4random_uniform(x)як описано нижче від @yood. Він також знаходиться в stdlib.h (після OS X 10.7 та iOS 4.3) і дає більш рівномірний розподіл випадкових чисел. Використанняint r = arc4random_uniform(74);
LavaSlider

4
Примітка: розподіл від arc4random може бути дуже поганим, якщо у вас трапляється вибір поганого діапазону. Я не усвідомив сподівання на два сили. +1 для використання у версії @ yood - помітна різниця для більшої кількості (наприклад, діапазон 400)
Адам,

Чи генерує він лише 32-бітові числа?
jjxtra

4
@codecowboy Це не так. Він завжди повертає ціле число в діапазоні [0, (2 ^ 32) -1]. Саме модуль обмежує верхню межу діапазону вказаним вами номером.
Мотасім

1
Чи не дасть це випадкове число між 0 і 73?
Том Говард

424

Використовуйте arc4random_uniform(upper_bound)функцію для генерації випадкового числа в межах діапазону. Нижче наведено число від 0 до 73 включно.

arc4random_uniform(74)

arc4random_uniform(upper_bound)дозволяє уникнути зміщення модуля, як описано на сторінці людини:

arc4random_uniform () поверне рівномірно розподілене випадкове число менше, ніж верхнє_обмежене. arc4random_uniform () рекомендується над конструкціями, такими як `` arc4random ()% top_bound '', оскільки це дозволяє уникнути " модульного зміщення ", коли верхня межа не є потужністю двох.


32
Зауважте, що arc4random_uniform () вимагає iOS 4.3. Якщо ви підтримуєте більш старі пристрої, вам слід додати чек: #if __IPHONE_OS_VERSION_MIN_REQUIRED >= __IPHONE_4_3 якщо це не вдалося перевірити, поверніться до іншого рішення.
Рон

6
arc4random_uniform () також вимагає 10.7 або пізнішої версії. Він вибиває ваш додаток о 10.6
Тібідабо

@Tibidabo Ваш коментар дуже помилковий та оманливий. Мені просто набридло використовувати arc4random_uniform () на iOS 10.3, і проблем немає. Для цього не потрібно 10,7 або пізнішої версії
четвертого

1
@Fourth Немає такого поняття, як iOS 10.7, це macOS 10.7. Минуло більше 5 років, як я написав коментар, тоді це було max iOS 5.
Тібідабо

63

Те саме, що і C, ви б зробили

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

(припускаючи, що ви мали на увазі включення 0, але виключаючи 74, що робить ваш приклад Java)

Edit: Ви можете замінити random()або arc4random()для rand()(який, як уже зазначалося, досить Sucky).


43
-1. Вам потрібно викласти генератор випадкових чисел або ви отримаєте однаковий зразок чисел при кожному виконанні.
Алекс Рейнольдс

Як щодо цього я хочу почати з іншого числа, ніж нуля?
амок

2
@amok: Ви можете просто додати до результату число, з якого ви хочете почати
Флорін,

2
Я продовжую отримувати число 9. Досить випадково, я б сказав; D
alexyorke

Я щойно перевірив випадковий (), і він показав ту ж проблему, що і rand ()
LolaRun

50

Я думав, що можу додати метод, який використовую у багатьох проектах.

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

Якщо я в кінцевому підсумку використовую його у багатьох файлах, я зазвичай оголошую макрос як

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

Напр

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

Примітка. Тільки для iOS 4.3 / OS X v10.7 (Lion) та новіших версій


Додавання є комутативним, навіть у фіксованих двійковими цілими числами, використовуючи подвійні доповнення. Як такий max - min + 1 точно такий же, як max + 1 - min, а також 1 + max - min.
Майкл Морріс

44

Це дасть вам число з плаваючою комою між 0 і 47

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

Або просто просто

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

І нижня, і верхня межа можуть бути негативними . Наведений нижче приклад дає вам випадкове число від -35,76 до +12,09

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

Перетворити результат у ціле число округлення :

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

Це погано. Чому ви використовуєте float, а не подвійний? І якщо ваші значення з плаваючою комою становлять від 0 до 47, то (int) (rndValue + 0,5) буде перетворювати лише значення від 0,0 до 0,5 до 0, але значення від 0,5 до 1,5 перетворюються в 1 і т.д. Отже, числа 0 і 47 вийде лише наполовину частіше, ніж усі інші цифри.
gnasher729

@ gnasher729 Вибачте, я не бачу вашої точки зору. Очевидно, що вам потрібна подвійна точність, ви можете легко замінити "float" на "double"
Тібідабо

Я не думаю, що це велика справа. Якщо вам потрібні лише цілі значення, ви можете використовувати arc4random () / 0x100000000) * (high_bound-low_bound) + low_bound, видаляючи float / подвійне перетворення.
Тібідабо

Ви отримуєте упередження, перетворюючи ціле число в плаваючу крапку таким чином. Використовуйте drand48натомість для плаваючих точок.
Франклін Ю

37

Відповідно до сторінки керівництва для rand (3), сімейство функцій rand застаріло випадково (3). Це пов'язано з тим, що нижні 12 біт rand () проходять через циклічну схему. Щоб отримати випадкове число, просто надішліть генератор, зателефонувавши srandom () з непідписаним насінням, а потім зателефонуйте random (). Отже, еквівалент коду був би вище

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

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

Вам потрібно буде зателефонувати srandom () лише один раз у програму, якщо ви не хочете змінити своє насіння. Хоча ви сказали, що не хочете обговорення справді випадкових значень, rand () є досить поганим генератором випадкових чисел, а random () все ще страждає від зміщення модуля, оскільки він генерує число від 0 до RAND_MAX. Так, наприклад, якщо RAND_MAX дорівнює 3, і ви хочете випадкове число між 0 і 2, ви вдвічі більше шансів отримати 0, ніж 1 або 2.


7
Ви також можете зателефонувати srandomdev () замість того, щоб передати час srandom (); це так само просто і математично краще.
бензадо

31

Краще використовувати arc4random_uniform. Однак це недоступно під iOS 4.3. На щастя, iOS прив’яже цей символ під час виконання, а не під час компіляції (тому не використовуйте директиву препроцесора #if, щоб перевірити, чи він доступний).

Найкращий спосіб визначити, чи arc4random_uniformє такий варіант, - це зробити щось подібне:

#include <stdlib.h>

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

9
Це питання стосується Objective-C, який використовує пізнє зв'язування. На відміну від C, який пов'язується під час компіляції / посилання, Objective-C пов'язує символи під час виконання, а символи, які він не може зв'язувати, встановлюються на NULL. Хоча ви маєте рацію, що це неправда C, але це, безумовно, є дійсним Objective-C. Я використовую цей точний код у своєму додатку iPhone. [ps, будь ласка, ви можете виправити свій голос].
AW101

Хоча target-c використовує пізнє зв'язування для методів objc, для функції C це не так. Цей код, безумовно, вийде з ладу, якщо функція не існує під час виконання.
Річард Дж. Росс III

8
Відповідно до Apple, "... лінкер встановлює адресу недоступних функцій NULL ...", Див. Перелік 3.2: developer.apple.com/library/mac/#documentation/DeveloperTools/… . Гаразд, це має бути слабко пов'язане, але воно не руйнується.
AW101

1
Перевірка наявності адреси функції NULL - це метод, який застосовується у всіх версіях C, C ++ та Objective-C як на MacOS X, так і на iOS.
gnasher729

14

Я написав свій власний клас утиліти випадкових чисел просто для того, щоб у мене було щось, що функціонувало трохи більше, як Math.random () на Java. У нього є лише дві функції, і все це зроблено в C.

Файл заголовка:

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

Файл реалізації:

//Random.m
static unsigned long seed;

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

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

Це досить класичний спосіб генерування псевдо-рандов. У своєму делегаті програми я телефоную:

#import "Random.h"

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

Потім я просто кажу:

float myRandomNumber = nextRandomFloat() * 74;

Зауважте, що цей метод повертає випадкове число між 0,0f (включно) та 1,0f (виключно).


3
1. Функції випадкових чисел, створені навмання, зазвичай не дуже випадкові. 2. Він повністю розбитий на 64-бітовому процесорі. 3. Використання секунд з 1970 року як випадкове насіння робить цифри передбачуваними.
gnasher729

7

Уже є кілька чудових, чітко виражених відповідей, але запитання задає випадкове число між 0 і 74. Використовуйте:

arc4random_uniform(75)


4

Як і для iOS 9 та OS X 10.11, ви можете використовувати нові класи GameplayKit для генерації випадкових чисел різними способами.

Ви можете обрати чотири типи джерел: загальний випадковий джерело (без назви, вниз до системи, щоб вибрати, що він робить), лінійний конгруентний, ARC4 та Mersenne Twister. Вони можуть генерувати випадкові вставки, поплавці та булі.

На найпростішому рівні ви можете генерувати випадкове число з вбудованого випадкового джерела системи таким чином:

NSInteger rand = [[GKRandomSource sharedRandom] nextInt];

Це генерує число від -2,147,483,648 до 2,147,483,647. Якщо вам потрібно число від 0 до верхньої межі (виключно), ви використовуєте це:

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

У GameplayKit є деякі конструктори зручності, вбудовані для роботи з кубиками. Наприклад, ви можете закатати шестигранну штамп таким чином:

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

Крім того, ви можете сформувати випадковий розподіл, використовуючи такі речі GKShuffledDistribution.


Мерсен є найшвидшим і найкращим для таких речей, як ігровий розробник, де якість генерованого випадкового числа зазвичай не надто важлива.
Роберт Васман

3

Створити випадкове число від 0 до 99:

int x = arc4random()%100;

Створити випадкове число між 500 і 1000:

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

1

// Наступний приклад генерує число від 0 до 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);

Вихід:

  • випадкове число: 72

  • випадкове число крок 2: 52


1

Для ігрового розробника використовуйте random () для генерації randoms. Можливо, принаймні в 5 разів швидше, ніж використання arc4random (). Модульне зміщення не є проблемою, особливо для ігор, коли генеруються рандеми, використовуючи весь спектр випадкових (). Обов’язково спочатку сійте. Виклик srandomdev () в AppDelegate. Ось кілька допоміжних функцій:

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

Будьте в курсі, що випадковий () не настільки випадковий, тому якщо швидкість не важлива у вашому коді (наприклад, якщо використовується лише раз у раз), тоді використовуйте arc4random ().
Роберт Васман
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.