Свифт - Посевной arc4random_uniform? Или альтернатива?

Позвольте мне начать с того, что я пытаюсь достичь:

  1. Мне нужно случайным образом сгенерировать набор чисел в диапазоне
  2. Я хотел бы, чтобы эти числа были несколько равномерно распределены
  3. Мне нужно иметь возможность инициировать генерацию случайных чисел так, чтобы при заданном числе результирующие случайные числа всегда были одинаковыми.

После нескольких экспериментов с drand48(), rand() и arc4random () я решил использовать rand () для получения случайного числа и srand () для заполнения. Вот небольшой пример, упрощенный из того, что я делаю:

let seed: UInt32 = 10
srand(seed)
let start = 0
let end = 100
let randomNumber = Double(rand()) % (end + 1 - start) + start

Это работает. Учитывая то же самое семя, то же самое случайное число выходит. Выполнение нескольких расчетов randomNumber приводит к появлению нескольких различных случайных чисел. Повторный посев через srand снова запускает "случайность".

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

Похоже, что arc4random_uniform будет генерировать больше равномерного случайного вывода, однако из моего исследования не представляется возможным заполнить arc4random, так как он запускается сам при первом вызове и не обязательно "предназначен" для внешней посева.

Итак, мой вопрос; есть ли лучшая альтернатива srand() / rand(), которая все еще даст мне те же выходные данные для данного семени, но эти выходные данные распределены более равномерно?

Спасибо, - Адам

2 ответа

Решение

Оказывается, что комбинация srand / rand действительно соответствует моим потребностям, проблема, из-за которой результаты не выглядели "равномерно распределенными", была ошибкой в ​​моей собственной логике.

Для справки, по сути, то, что я делал, было следующим (на самом деле это было намного сложнее, но для демонстрационных целей):

let start = 0
let end = 100

for x in 0..<10 {

   let seed = UInt32(x)
   srand(seed)
   let randomNumber = Double(rand()) % (end + 1 - start) + start

   // Do something with random number

}

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

Простое решение состоит в том, чтобы не заново заполнять каждую итерацию цикла, а вместо этого начинать один раз перед циклом. Например:

let start = 0
let end = 100
let seed = UInt32(100)
srand(seed)

for x in 0..<10 {

   let randomNumber = Double(rand()) % (end + 1 - start) + start

   // Do something with random number

}

Благодаря этому простому изменению результирующие значения выглядят несколько равномерно распределенными по диапазону от 0 до 100, используемому в примере. Я не могу быть уверен, что есть "более равномерный" способ сделать это, но я предполагаю, что есть, поскольку я прочитал, что arc4random "намного превосходит" функции drand / rand / erand / etc для генерации случайных чисел, но по крайней мере, это, кажется, работает для моих нужд.

Я оставлю этот вопрос открытым еще ненадолго, если кто-то еще придумает лучший подход к выполнению того, что мне нужно.

Я знаю, что "GameKit" звучит так, будто он только для игр, но в нем есть серьезная система генерации случайных чисел. Я предлагаю вам взглянуть на GKMersenneTwisterRandomSource и GKRandomDistribution. GKMersenneTwisterRandomSource берет случайное семя (если вы того пожелаете) и GKRandomDistribution класс реализует равномерное распределение. При совместном использовании они делают именно то, что вы ищете.

import GameKit

// The Mersenne Twister is a very good algorithm for generating random
// numbers, plus you can give it a seed...    
let rs = GKMersenneTwisterRandomSource()
rs.seed = 1780680306855649768

// Use the random source and a lowest and highest value to create a 
// GKRandomDistribution object that will provide the random numbers.   
let rd = GKRandomDistribution(randomSource: rs, lowestValue: 0, highestValue: 100)

// Now generate 10 numbers in the range 0...100:    
for _ in 1...10 {
    print(rd.nextInt())
}

print("---")

// Let's set the seed back to the starting value, and print the same 10
// random numbers.    
rs.seed = 1780680306855649768
for _ in 1...10 {
    print(rd.nextInt())
}
Другие вопросы по тегам