
Генератор случайных чисел на Ассемблере
При написании программ часто возникает необходимость получить последовательность случайных чисел. В языках высокого уровня существуют штатные функции, а для Ассемблера я использую так называемый "Минимальный генератор Парка-Миллера" (Minimal portable random generator by Park and Miller). От аналогичных алгоритмов его отличает очень малый размер и равномерное распределение получаемых случайных чисел. Математическую модель и описание работы алгоритма можно без труда найти в интернете, поэтому эту информацию я здесь не привожу.Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------
- ; Park Miller random number algorithm
- ; Получить случайное число 0 ... 99999
- ; stdcall WRandom
- ; на выходе EAX - случайное число
- ;---------------------------------------------
- proc WRandom
- push edx ecx
- mov eax,[random_seed]
- xor edx,edx
- mov ecx,127773
- div ecx
- mov ecx,eax
- mov eax,16807
- mul edx
- mov edx,ecx
- mov ecx,eax
- mov eax,2836
- mul edx
- sub ecx,eax
- xor edx,edx
- mov eax,ecx
- mov [random_seed],ecx
- mov ecx,100000
- div ecx
- mov eax,edx
- pop ecx edx
- ret
- endp
- ;---------------------------------------------
- ; Получить случайное число в нужном интервале
- ; Требуется процедура WRandom
- ; stdcall WIRandom,min,max
- ; на выходе EAX - случайное число
- ;---------------------------------------------
- proc WIRandom rmin:dword,rmax:dword
- push edx ecx
- mov ecx,[rmax]
- sub ecx,[rmin]
- inc ecx
- stdcall WRandom
- xor edx,edx
- div ecx
- mov eax,edx
- add eax,[rmin]
- pop ecx edx
- ret
- endp
- ;---------------------------------------------
- ; Инициализация генератора случайных чисел
- ; stdcall WRandomInit
- ;---------------------------------------------
- proc WRandomInit
- push eax edx
- rdtsc
- xor eax,edx
- mov [random_seed],eax
- pop edx eax
- ret
- endp
Пример использования генератора:
Code (Assembler) : Убрать нумерацию
- ; Сегмент данных
- section '.data' data readable writeable
- ...
- random_seed dd 0 ; Переменная для хранения промежуточных результатов
- ; работы генератора
- ; Сегмент кода
- section '.code' code readable executable
- ...
- ; Инициализация генератора
- stdcall WRandomInit
- ; Получить случайное число в интервале 0...99999
- stdcall WRandom
- ; EAX=случайное число
- ; Получить случайное число в интервале min...max (включая границы)
- stdcall WIRandom,5,20
- ; EAX=случайное число в интервале от 5 до 20
UPD: с подачи Дениса Внукова был выявлен серьезный косяк генератора. При нулевом или очень большом (типа 0xFFFFFFFE) значении random_seed генератор намертво зацикливался, возвращая каждый раз 0. Это можно обойти, добавив в WRandom дополнительную проверку значения random_seed. Как побочный положительный момент, генератор становится самоинициализирующимся и больше не требует вызова WRandomInit. Функция WIRandom остается без изменений.
Code (Assembler) : Убрать нумерацию
- ;---------------------------------------------
- ; Park Miller random number algorithm
- ; Получить случайное число 0 ... 99999
- ; stdcall WRandom
- ; на выходе EAX - случайное число
- ;---------------------------------------------
- proc WRandom
- push edx ecx
- mov eax,[random_seed]
- ; Модификация против зацикливания генератора
- ; на случай если в random_seed будет нулевое значение
- or eax,eax
- jnz @f
- ; Инициализация генератора при первом вызове или зацикливании
- rdtsc
- xor eax,edx
- mov [random_seed],eax
- @@:
- xor edx,edx
- mov ecx,127773
- div ecx
- mov ecx,eax
- mov eax,16807
- mul edx
- mov edx,ecx
- mov ecx,eax
- mov eax,2836
- mul edx
- sub ecx,eax
- xor edx,edx
- mov eax,ecx
- mov [random_seed],ecx
- mov ecx,100000
- div ecx
- mov eax,edx
- pop ecx edx
- ret
- endp
В приложении пример программы с исходным текстом, которая использует алгоритм Парка-Миллера для генерации случайных чисел.
Просмотров: 31293 | Комментариев: 7
Метки: Assembler, генератор ПСЧ

Внимание! Статья опубликована больше года назад, информация могла устареть!
Комментарии
Отзывы посетителей сайта о статье

Иван
(03.12.2012 в 23:57):
Ну, что сказать всё это ерунда. Всё начинаем сначало. Если поискать книжку по схемотехнике цифровых устойств. То в ней есть описание генератора псевдослучайной последовательности из одного регистра. Если понять как он работает,то не трудно написать код на ассемблере всего из 5-6 сторк. И это будет наиболее оптимальным вариантом по быстродействию. Проверено на личном опыте. Писал подпрограммы проверки внешней оперативной памяти объёмом 256 кБ. Время выполнения около 1 секунды.

Agranom
(24.12.2010 в 22:10):
Спасибо огоромное, целиком куросовую на этом генираторе построил!

Strike
(16.06.2010 в 18:07):
ELM есть в твоей функции пара недочетов:
1) не xchg esi,eax, а xchg eax,esi позволяет сэкономить 1 байт и увеличивает скорость.
2)xor edx,esi
xor edx,edx
Данные команды бесполезны т.к. последняя стирает результат предыдущей.
1) не xchg esi,eax, а xchg eax,esi позволяет сэкономить 1 байт и увеличивает скорость.
2)xor edx,esi
xor edx,edx
Данные команды бесполезны т.к. последняя стирает результат предыдущей.

ELM
(23.03.2010 в 11:00):
я использую такую иницыацыю
proc RandomNum,Min,Max ;на выходе в eax рандом байт в диапазоне
push ecx edx ebx edi esi
rdtsc
; push eax
xchg esi,eax
xchg edi,edx
; pop eax
; and eax,0x000000ff
; invoke Sleep,eax
rdtsc
xor eax,edi
xor edx,esi
xor edx,edx
mov ecx,[Max]
add ecx,1h
sub ecx,[Min]
div ecx
add edx,[Min]
mov eax,edx
pop esi edi ebx edx ecx
ret
endp
proc RandomNum,Min,Max ;на выходе в eax рандом байт в диапазоне
push ecx edx ebx edi esi
rdtsc
; push eax
xchg esi,eax
xchg edi,edx
; pop eax
; and eax,0x000000ff
; invoke Sleep,eax
rdtsc
xor eax,edi
xor edx,esi
xor edx,edx
mov ecx,[Max]
add ecx,1h
sub ecx,[Min]
div ecx
add edx,[Min]
mov eax,edx
pop esi edi ebx edx ecx
ret
endp

Norbert123
(11.03.2010 в 02:07):
спасибо
(я моленько сплагиатю данный код :-)
(я моленько сплагиатю данный код :-)

Rambler
(05.10.2009 в 20:57):
THX!

Добавить комментарий
Заполните форму для добавления комментария

Шедевр про "внешнюю оперативную память" понравился. Проверь книжку на всякий случай, там похоже вкралась опечатка. Память бывает внешняя ИЛИ оперативная, но не вместе.