Arc4random() и arc4random_uniform() действительно не случайны?

Я использовал arc4random() и arc4random_uniform(), и у меня всегда было ощущение, что они не были точно случайными, например, я случайно выбрал значения из массива, но часто значения, которые выходили, были одинаковыми, когда я генерировал их несколько раз подряд, поэтому сегодня я подумал, что буду использовать игровую площадку Xcode, чтобы увидеть, как эти функции ведут себя, поэтому я сначала тестирую arc4random_uniform, чтобы сгенерировать число от 0 до 4, поэтому я использовал этот алгоритм:

import Cocoa

var number = 0

for i in 1...20 {
    number = Int(arc4random_uniform(5))
}

И я запускал его несколько раз, и вот как меняются ценности большую часть времени:
enter image description hereenter image description here

Так как вы можете видеть, что значения постоянно увеличиваются и уменьшаются, и как только значения находятся на максимуме/минимуме, они часто остаются на нем в течение определенного времени (см. первый скриншот на 5-м шаге, значение остается на 3 в течение 6 шагов, проблема в том, что это совсем не необычно, функция фактически ведет себя таким образом большую часть времени в моих тестах.

Теперь, если мы посмотрим на arc4random(), это в основном то же самое:
enter image description hereenter image description here

Итак, вот мои вопросы:

  • Почему эта функция ведет себя таким образом?
  • Как сделать его более случайным?

Спасибо.

РЕДАКТИРОВАТЬ:
Наконец, я сделал два эксперимента, которые были удивительными, первый с реальной кости:
enter image description here
Что меня удивило, так это то, что я бы не сказал, что это было случайным, так как я видел такой же шаблон, который описывался как неслучайный для arc4random() и arc4random_uniform(), так как Jean-Baptiste Yunès отметил, что людям нехорошо видеть, действительно ли последовательность чисел является случайной.

Я также хотел сделать более "научный" эксперимент, поэтому я сделал этот алгоритм:

import Foundation

var appeared = [0,0,0,0,0,0,0,0,0,0,0]
var numberOfGenerations = 1000

for _ in 1...numberOfGenerations {
    let randomNumber = Int(arc4random_uniform(11))
    appeared[randomNumber]++
}

for (number,numberOfTimes) in enumerate(appeared) {
    println("\(number) appeard \(numberOfTimes) times (\(Double(numberOfGenerations)/Double(numberOfTimes))%)")
}

Чтобы узнать, сколько раз каждый номер появился, и, фактически, цифры генерируются случайным образом, например, вот один вывод с консоли:
0 появилось 99 раз. 1 появилось 97 раз. 2 появилось 78 раз. 3 появилось 80 раз. 4 появилось 87 раз. 5 появилось 107 раз. 6 появилось 86 раз. 7 появилось 97 раз. 8 появилось 100 раз. 9 появилось 91 раз. 10 появилось 78 раз.

Так что это определенно ОК 😊

ИЗМЕНИТЬ № 2: я снова сделал эксперимент с кубиками с большим количеством рулонов, и это все еще так же удивительно для меня:
enter image description here

Ответ 1

Истинная случайная последовательность чисел не может быть сгенерирована алгоритмом. Они могут производить только псевдослучайную последовательность чисел (что-то похожее на случайную последовательность). Поэтому в зависимости от выбранного алгоритма качество "случайности" может варьироваться. Качество arc4random() последовательностей обычно считается хорошей случайностью.

Вы не можете визуально анализировать случайность последовательности... Люди очень плохо обнаруживают случайность! Они, как правило, находят какую-то структуру там, где ее нет. На ваших диаграммах ничего страшного нет (за исключением редкой подпоследовательности 6 три в ряд, но это случайность, иногда происходят необычные вещи). Вы были бы удивлены, если бы вы использовали кости для создания последовательности и рисования своего графика. Опасайтесь, что образец из 20 чисел не может быть серьезно проанализирован против его случайности, вам нужно гораздо больше образцов.

Если вам нужен какой-то другой случайный случай, вы можете попытаться использовать псевдо файл /dev/random, который генерирует случайное число каждый раз, когда вы читаете. Последовательность генерируется сочетанием алгоритмов и внешних физических событий, которые ay происходит на вашем компьютере.

Ответ 2

Это зависит от того, что вы имеете в виду, когда говорите произвольно.

Как указано в комментариях, истинная случайность является комковатой. Ожидаются длинные строки повторов или близкие значения.

Если это не соответствует вашему требованию, вам необходимо лучше определить ваше требование.

Другие варианты могут включать использование алгоритма перетасовки для удаления вещей в массиве или использования алгоритм с низкой частотой ошибок, чтобы обеспечить равное распределение значений.

Ответ 3

Я действительно не согласен с идеей людей, которые очень плохо обнаруживают случайность. Будете ли вы удовлетворены, если получите 1-1-2-2-3-3-4-4-5-5-6-6 после броска 6 пар кубиков? однако частоты кубиков идеальны...

Это как раз та проблема, с которой я сталкиваюсь с функциями arc4random или arc4random_uniform. Уже много лет я разрабатываю приложение для игры в нарды, основанное на нейронной сети, обучаемой игроками в слова чемпионов Я действительно знаю, что это играет намного лучше, чем кто-либо, но многие пользователи думают, что это обман. У меня тоже иногда бывают сомнения, поэтому я решил бросить все кубики сам...

Я не удовлетворен с arc4random, даже если частоты в порядке. Я всегда бросаю пару кубиков, и результаты приводят к недопустимым ситуациям, например: получение пяти последовательных двойных кубиков для одного и того же игрока, ожидание 12 ходов (24 кубика), пока не произойдут первые 6.

Это легко проверить (код C):

void randomDices ( int * dice1, int * dice2, int player )
{
    ( * dice1 ) = arc4random_uniform ( 6 ) ;
    ( * dice2 ) = arc4random_uniform ( 6 ) ;

    // Add to your statistics
    [self didRandomDice1:( * dice1 ) dice2:( * dice2 )  forPlayer:player] ;
}

Может быть, arc4random не нравится, когда его вызывают дважды за короткое время...

Итак, я попробовал несколько решений и, наконец, выбрал этот код, который выполняет второй уровень рандомизации после arc4random_uniform:

int CFRandomDice ()
{
    int __result = -1 ;

    BOOL __found = NO ;

    while ( ! __found )
    {
        // random int big enough but not too big
        int __bigint = arc4random_uniform ( 10000 ) ;

        // Searching for the first character between '1' and '6'
        // in the string version of bigint :

        NSString * __bigString = @( __bigint ).stringValue ;

        NSInteger __nbcar = __bigString.length ;
        NSInteger __i = 0 ;

        while ( ( __i < __nbcar ) && ( ! __found ) )
        {
            unichar __ch = [__bigString characterAtIndex:__i] ;
            if ( ( __ch >= '1' ) && ( __ch <= '6' ) )
            {
                __found = YES ;
                __result = __ch - '1' + 1 ;
            }
            else
            {
                __i++ ;
            }
        }
    }

    return ( __result ) ;
}

Этот код создает случайное число с помощью arc4random_uniform (10000), преобразует его в строку и затем ищет первую цифру между '1 и' 6 в строке.

Это показалось мне очень хорошим способом рандомизации кубиков, потому что: 1/частоты в порядке (см. Статистику ниже); 2/Исключительные последовательности игральных костей происходят в исключительные моменты времени.

10000 dices test:
----------
Game Stats
----------
HIM :
Total 1 = 3297
Total 2 = 3378
Total 3 = 3303
Total 4 = 3365
Total 5 = 3386
Total 6 = 3271
----------
ME :
Total 1 = 3316
Total 2 = 3289
Total 3 = 3282
Total 4 = 3467
Total 5 = 3236
Total 6 = 3410
----------
HIM doubles = 1623
ME doubles = 1648

Теперь я уверен, что игроки не будут жаловаться...