Свифт - Посевной arc4random_uniform? Или альтернатива?
Позвольте мне начать с того, что я пытаюсь достичь:
- Мне нужно случайным образом сгенерировать набор чисел в диапазоне
- Я хотел бы, чтобы эти числа были несколько равномерно распределены
- Мне нужно иметь возможность инициировать генерацию случайных чисел так, чтобы при заданном числе результирующие случайные числа всегда были одинаковыми.
После нескольких экспериментов с 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())
}