Область применения предсказуемого генератора псевдослучайных чисел (далее ПГПСЧ) достаточно широка в 3D-геймплеях. Допустим, вам необходимо сделать достаточно огромных размеров карту ландшафтов (10000х10000 кв.км). Причем, эта карта должна содержать различные геологические и климатические зоны. Понятно, что держать такую карту в памяти компьютеров достаточно сложно, тем более, если перемещаться по карте скачками. Поэтому ландшафт такой карты должен формироваться «на лету». Но, попадая каждый раз в одни и те же координаты, ваша карта не должна меняться. Вот тут на помощь и приходит ПГПСЧ, который вам выдаст те же случайные величины, что и в предыдущий раз.
ВНИМАНИЕ! Данную функцию ПГПСЧ нельзя использовать в средствах и алгоритмах шифрования данных!
А теперь все по порядку.
Чтобы вам проще было представить работу данной функции, сделаю кое-какие пояснения. Наверное, многим из вас доводилось по фильмам видеть игровые автоматы в казино. Есть там такой замечательный автомат – «однорукий бандит». Принцип его работы достаточно прост. Дергаешь рычаг – и начинают вращаться несколько барабанов с картинками. Если выпадают одинаковые картинки на всех барабанах, то получаешь выигрыш.
В ПГПСЧ в роли «вращающихся барабанов» выступает линейный массив данных из 256 байт, где содержатся значения от 0 до 255. Каждая ячейка массива должна содержать свое индивидуальное натуральное число в диапазоне от 0 до 255 и не повторяться! Но числа в массиве должны располагаться хаотично. Эта процедура делается всего один раз при инициализации программы и затем храниться в памяти постоянно.
А теперь главное. Разница между ПГПСЧ и «одноруким бандитом» заключается в том, что последовательность псевдослучайных чисел («выпадение картинок») должна повторяться и зависеть от количества раз дергания «рычага». Например, если мы в функцию ПГПСЧ предали значение 238910 («дернули рычаг» 238910 раз), то каждый раз передавая в нее это же число, должны на выходе получать одно и то же случайное число («набор картинок», например 0.727394). Считайте, что каждая цифра в случайном числе 0.727394 после плавающей точки - «картинка». Надеюсь, аналогия понятна.
Итак, у меня в ПГПСЧ стоит четыре одинаковых «барабана» (массива байтов со случайно расположенными значениями от 0 до 255). Но данные «барабаны» из значений которых будет потом собираться случайное число вращаются не хаотично, а по определенному закону, так сказать со своим передаточным числом (скоростью вращения). Это передаточное число для каждого «барабана» зависит от выбранного конкретного простого числа (у каждого барабана оно свое. Примечание: простое число делится только на само себя и на единицу.). Для «барабанов», следующих за первым, передаточное число еще зависит и от числа, выбранного предыдущим «барабаном». Из сказанного следует, что скорость вращения только крайнего правого барабана будет постоянной. А передаточные числа остальных будут с каждым новым шагом меняться, но сохраняя при этом порядок этих изменений. Поясню это примером - алгоритмом на VB6:
- Код: Выделить всё
Dim RandSeed(0 To 255) As Byte
Public Type LongFromBytes
Byte0 As Byte
Byte1 As Byte
Byte2 As Byte
Byte3 As Byte
End Type
Public Type Type_Long
TLong As Long
End Type
Function Random(Number As Long) As Single
Dim Rand As LongFromBytes, TL As Type_Long
Dim Ind1 As Byte, Ind2 As Byte, Ind3 As Byte
Rand.Byte0 = RandSeed(Number And &HFF)
Ind1 = ((Number \ &HEF) * &HEF + Rand.Byte0) And &HFF
Rand.Byte1 = RandSeed(Ind1)
Ind2 = ((Number \ &HF1) * &HF1 + Rand.Byte1) And &HFF
Rand.Byte2 = RandSeed(Ind2)
Ind3 = ((Number \ &HFB) * &HFB + Rand.Byte2) And &HFF
Rand.Byte3 = RandSeed(Ind3) And &H7F '&H7F-избавление от знака
LSet TL = Rand 'сборка в целое число
Random = TL.TLong * 4.65661287307739E-10 '(деление на большое целое число 2147483648 заменяется умножением на инверсное двойной точности 4.65661287307739E-10)
End Function
Number – любое натуральное число от -2147483648 до 2147483647;
RandSeed() – это массив случайных однобайтовых чисел от 0 до 255, расположенных хаотично;
Rand.Byte0...Rand.Byte3 – это однобайтные числа, выбранные из массива;
Ind1, Ind2, Ind1 – индексы, или так называемые «передаточные числа», где &HEF = 239, &HF1 = 241, &HFB = 251 – простые числа.
Примечание: Изменяя Number во всем диапазоне значений (от -2147483648 до 2147483647) цикл начнет повторяться через 255×239×241×251 = 3 686 623 995 значений.
Для реализации функции ГППСЧ от двух аргументов X и Y необходимо внести дополнения в саму функцию и вместо одного массива RandSeed использовать два RandSeed(0 TO 1, 0 TO 255). Алгоритм представлен ниже:
- Код: Выделить всё
Dim RandSeed(0 To 1, 0 To 255) As Byte
Public Type LongFromBytes
Byte0 As Byte
Byte1 As Byte
Byte2 As Byte
Byte3 As Byte
End Type
Public Type Type_Long
TLong As Long
End Type
Function Random2D(X As Long, Y As Long) As Double
Dim RandX As LongFromBytes, TLX As Type_Long
Dim RandY As LongFromBytes, TLY As Type_Long
Dim Ind1X As Byte, Ind2X As Byte, Ind3X As Byte
Dim Ind1Y As Byte, Ind2Y As Byte, Ind3Y As Byte
Dim RandomX As Double, RandomY As Double
RandX.Byte0 = RandSeed(0, X And &HFF)
Ind1X = ((X \ &HEF) * &HEF + RandX.Byte0) And &HFF
RandX.Byte1 = RandSeed(0, Ind1X)
Ind2X = ((X \ &HF1) * &HF1 + RandX.Byte1) And &HFF
RandX.Byte2 = RandSeed(0, Ind2X)
Ind3X = ((X \ &HFB) * &HFB + RandX.Byte2) And &HFF
RandX.Byte3 = RandSeed(0, Ind3X) And &H7F
LSet TLX = RandX
RandomX = TLX.TLong * 4.65661287307739E-10
RandY.Byte0 = RandSeed(1, Y And &HFF)
Ind1Y = ((Y \ &HEF) * &HEF + RandY.Byte0) And &HFF
RandY.Byte1 = RandSeed(1, Ind1Y)
Ind2Y = ((Y \ &HF1) * &HF1 + RandY.Byte1) And &HFF
RandY.Byte2 = RandSeed(1, Ind2Y)
Ind3Y = ((Y \ &HFB) * &HFB + RandY.Byte2) And &HFF
RandY.Byte3 = RandSeed(1, Ind3Y) And &H7F
LSet TLY = RandY
RandomY = TLY.TLong * 4.65661287307739E-10
Random2D = RandomY * RandomX + RandomY + RandomX
Random2D = Random2D - Int(Random2D)
End Function
Здесь, как вы заметили, обе координаты X и Y тоже изменяются в диапазоне от 2147483648 до 2147483647, что позволяет создать 2^64-1=1,84E+19 случайных значений, из них только 1.359E+19 не будут повторятся. Такой диапазон значений нам позволит покрыть поверхность Земли площадью 510 млн. кв. км сеткой 5×5 кв. мм!
Для заполнения массива RandSeed случайными значениями можно использовать этот алгоритм:
- Код: Выделить всё
Function InitRandom()
'Инициализация 2-х массивов случайными числами от 0 до 255
Dim Count As Integer, i As Integer, j As Integer
For j = 0 To 1
Count = 0
Do
i = Int(256 * Rnd)
If RandSeed(j, i) = 0 Then
Count = Count + 1
RandSeed(j, i) = Count Mod 256
End If
Loop While Count < 256
Next
End Function
Удачи!