Blog. Just Blog
Генератор случайных чисел на Ассемблере
При написании программ часто возникает необходимость получить последовательность случайных чисел. В языках высокого уровня существуют штатные функции, а для Ассемблера я использую так называемый "Минимальный генератор Парка-Миллера" (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
Просмотров: 8535 | Комментариев: 5
Комментарии
Отзывы посетителей сайта о статье
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!
Добавить комментарий
Заполните форму для добавления комментария
