
В строго детерминированном мире процессорных кодов внесение в программу элемента случайности - не такая простая задача, как может показаться на первый взгляд. Наиболее часто встречающиеся приложения, в которых необходимо использование случайных чисел - это численное моделирование методом Монте-Карло и создание компьютерных игр.
Метод Монте-Карло в широком смысле - это численный метод решения математических задач при помощи моделирования случайных величин. Мы же будем использовать термин метод МК более узко - как метод реализации вероятностного процесса, в ходе которого ведется генерация достаточно большого числа состояний исследуемой системы,причем ожидаемые значения характеристик системы являются результатом усреднения по многим конфигурациям. Получение случайных чисел - важная стадия компьютерного экс перимента, которой не всегда уделяется должное внимание. Используемые на практике численные алгоритмы приводят к получению псевдослучайных чисел, особенностями которых являются ограниченность и воспроизводимость последовательности.
Исчерпание этой последовательности при большом числе циклов МК или размере системы снижает ее фактический размер до
N = P/(k*n) (1)
где N - размер системы (количество частиц);
P - период последовательности псевдослучайных чисел;
k - количество случайных чисел, используемых для определения состояния одной частицы;
n - суммарное количество циклов МК, необходимое для стабилизации (термализации) системы и расчета ее характеристик.
Например,при моделировании системы Изинга, состоящей из 2000 частиц требуется, как правило, не менее 500 циклов МК, т.е. необходимо не меее 105 случайных чисел. Если используемый генератор является 16 разядным и не может произвести последовательность, состоящую из более чем 216 (65536) псевдослучайных чисел, то фактический размер системы по формуле (1) будет порядка 1000 частиц.
С играми ситуация еще более трагическая: например,колода из 52 карт может быть упорядочена 52! (это не выражение восторга, а символ факториала) способами. Это примерно 8e67 или 2226. Значит для того, чтобы в процессе игры мог возникнуть любой расклад, создателю полноценной игры типа "21" необходим 256 разрядный генератор случайных чисел. Если колода состоит из 36 карт,то соответствующие числа равны 4e41 и 2138, т.е. без суперкомпьютера опять не обойдешься. В преферансе количество вариантов раздач равно 32!/10! или 296, что тоже не мало. Несмотря на несравнимость этих чисел с реальными возможностями 32-разрядного процессорора, необходимо, конечно, использовать его возможности максимально, ведь только так можно приблизиться к разнообразию реальности.
Традиционный способ получения случайных чисел - это использование метода сравнений или вычетов. В переводной литературе его упорно называют "линейным конгруэнтным", но мы сейчас не будем бороться за чистоту языка,тем более что программисты безусловно лидируют по части некритического заимствования англоязычных терминов. В общем виде алгоритм метода сравнений выглядит так:
r(i+1)=(r(i)*a+c)%m, (2)
где % - операция нахождения остатка от деления,
r(i+1) и r(i) - соседние члены последовательности;
a, c и m - некоторые целые магические числа, выбору которых по священа обширная литература (см. например [1,2]).
Если c=0, то генератор называется мультипликативным, в противном случае - смешанным. Смешанные генераторы дают плохие результаты.
Основное свойство генератора (2) - это периодичность, то-есть существует такое P (период), что для любого i r(P+i)=r(i). Понятно, что P не может превосходить m. Конкретная реализация такого генератора ограничена типом используемых переменных и поэтому набор получаемых чисел - L (длина апериодичности) ограничен и не может превосходить Xmax - максимальную величину переменной данного типа. При использовании нормализованных случайных чисел ( rn(i)=r(i)/m, 0<=rn<1 ) следует также учитывать их дискретность - min(rn(i)-rn(j))=1/m. Известно также, что этот метод приводит к детерминированному набору чисел, ведь из конкретного r(i) получается всегда r(i+1). Из сказанного ясно, почему чистые 16-разрядные генераторы не получили широкого распространения, ведь их набор не превышает 65536 значений, а дискретность примерно равна 1.5e-5. Сейчас в приложениях наиболее распространенными являются
32(31)-разрядные мультипликативные генераторы, использующие тип long int. В таблице приведены примеры трех реализаций такого генератора.
Библиотеки некоторых фирм включают функциии случайных чисел, являющиеся "скрыто 32-разрядными": число получается как 32(31) разрядное, но усекается до 15 разрядного, причем возвращается младшее слово.Тогда P оказывается как у 32-разрядного генератора, а L и дискретность как у 15. Интересно, кто решил, что нам этого хватит?
При использовании генераторов псевдослучайных чисел следует убедиться, что конкретный алгоритм библиотечной или собственной функции является "хорошим".Как можно охарактеризовать качество генератора? Разумеется, алгоритм должен быть быстрым. Поскольку предполагается, что случайные числа обладают равномерным распределением, то для нормализованных случайных чисел должны примерно соблюдаться следующие статистические соотношения:
M = SUM(N)(rn(i)/N) = 1/2;
D = SUM(N)((rn(i)-M)^2/N) = 1/12;
C_2 = SUM(N-1)(rn(i)*rn(i+1))/(N-1))= 0;
C_3 = SUM(N-2)(rn(i)*rn(i+1)*rn(i+2))/(N-2))= 0.
где M - среднее из N последовательных чисел;
D - дисперсия;
C_2 - последовательная парная корреляция;
C_3 - последовательная тройная корреляция;
C_2 и C_3 характеризуют случайность наборов соответственно 2 и 3 чисел, используемых, например, при получениии случайных 2d и 3d координат.
Приведем результаты тестирования некоторых известных генераторов. В таблице первая цифра - секунд на вызов в цикле,вторая - (М-1/2) третья (D-1/12), следующие - C_2 и C_3. Расчет статистик для 226 исытаний (для шестого - 215 испытаний). Времена - для Pentium-133, ориентировочные, зависят также от условий компиляции. Разумеется, приведенные статистики зависят от базы - числа испытаний. Все генераторы инициализованы единицей. Сравнительные характеристики некоторых генераторов случайных чисел с разрядностью не выше 32.
|
Сравнительные характеристики некоторых генераторов случайных чисел с разрядностью не выше 32.
|
|||||
|
Название
|
Параметры
|
T, сек.
|
M-1/2
|
D-1/12
|
C_2 и C_3
|
| RND_32 | (P=L=229, a=513) |
4.12e-07
|
-2.71e-05
|
1.02e-05 |
-2.01e-05 -2.61e-06
|
| IBM RANDU [1,2] | (P=L=229, a=65539) |
8.40e-07*
|
6.63e-05
|
-3.16e-06 |
8.85e-06 -2.77e-06
|
| 32-разрядный [2] | (P=L=229, a=16807) |
**
|
-7.13e-05
|
-5.57e-07 |
1.85e-06 5.03e-07
|
| Borland 3.0 C++ | (P=231, L=215) |
5.88e-07
|
-1.29e-05
|
1.87e-05 |
-1.69e-05 3.49e-06
|
| WATCOM 11.0 C++ | (P=231, L=215) |
6.86e-07
|
4.91e-05
|
4.26e-06 |
-1.18e-06 8.73e-07
|
| 16-разрядный | (P=L=216, a=25173, c=13849) |
5.50e-07#
|
-1.41e-03
|
-4.94e-04 |
-8.38e-05 -7.7937e-05
|
| Примечания. * Функция, написанная на Си. Для ассемблерной функции будет как у первой. ** Как у 1 и 2, в зависимости от реализации. # Функция, написанная на Си. На ассемблере можно сделать гораздо быстрее, но не зачем - не нужна. Хорошие статистики для генераторов 4 и 5 - во многом следствие их разрядной "усеченности". |
|||||
В заключении предлагаю вашему вниманию довольно быстрые реализации алгоритма (2). Понятно, что возможна подстановка других чисел, в частности из примеров 2 и 3 таблицы. Вторая процедура (для получения нормализованных псевдослучайных чисел) использует свойство паралельной обработки. Для этого первое случайное целое число сначала используется - передается на деление в сопроцессор, а затем процессор считает следующее.
;RND_32
;Процедура генерации
;псевдослучайных целых 32-разрядных чисел.
;Реализация для процессора не хуже 386DX.
;Текст - В.С.Горшков, 1996-97.
P286
MODEL LARGE, C
CODESEG
PUBLIC C RND_32
RAND dd 00000001h
ZEROSGN dd 01111111111111111111111111111111b
MULT dd 48C27395h
RND_32 PROC C FAR
P386
mov eax,MULT ;ввод множителя
mul RAND ;умножение - основной тормоз
and eax,ZEROSGN ;обнуление знака
mov RAND,eax ;сохранение для
;следующего обращения
mov edx,eax ;подготовка к возврату
shr edx,010h ;через DX:AX
ret RND_32
ENDP
END
;RND_32_1
;Процедура генерации
;псевдослучайных нормализованных чисел
;типа double, основанная
;на методе процедуры RAND_32.
;Реализация для процессора не хуже 386DX/387.
;Текст - В.С.Горшков, 1996-97.
P286
MODEL LARGE,C
CODESEG
PUBLIC C RND_32_1
RAND_MAX8 dq 41E0000000000000h ;2^31 в формате
;double
RAND dd 48C27395h
MULT dd 48C27395h
ZEROSGN dd 01111111111111111111111111111111b
RND_32_1 PROC C FAR
P386
enter 4,0
mov eax,RAND ; загружаем предыдущее
mov dword ptr [bp-4],eax
fild dword ptr [bp-4] ; в сопроцессор его
fld RAND_MAX8
fdiv ; деление на максимальное значение
mov ebx,MULT ; получение следующего
mul ebx
and eax,ZEROSGN
mov RAND,eax ; его возвращать не надо
leave ; а результат берет сам C с
; вершины стека
ret RND_32_1
END
/* C-декларации процедур. */
/* Объектные файлы должны быть включены в проект. */
unsigned long rnd_32(void);
/* целое случайное число в интервале 0-2^31 */
double rnd_32_1(void);
/* случайное число в интервале 0-1 */
Литература.
[1] Хеерман Д.В. Методы компьютерного эксперимента в теоретической
физике. М., Наука. 1990. С.134-144.
[2] Соболь И.М. Метод Монте-Карло. М., Наука. 1985. С.20-25, 57-59