Генератор случайных чисел на Ассемблере

При написании программ часто возникает необходимость получить последовательность случайных чисел. В языках высокого уровня существуют штатные функции, а для Ассемблера я использую так называемый "Минимальный генератор Парка-Миллера" (Minimal portable random generator by Park and Miller). От аналогичных алгоритмов его отличает очень малый размер и равномерное распределение получаемых случайных чисел. Математическую модель и описание работы алгоритма можно без труда найти в интернете, поэтому эту информацию я здесь не привожу.
;---------------------------------------------
; 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

После небольшой доработки он получил возможность генерировать случайные числа в заданном интервале.

Пример использования генератора:
; Сегмент данных
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

Примечание: процедура инициализации должна обязательно вызываться перед первым обращением к генератору, а переменная random_seed должна находиться в сегменте, доступном для записи. При вызове процедуры WIRandom дополнительных проверок на правильность границ не производится, на их последовательность (условие min < max) - тоже.

UPD: с подачи Дениса Внукова был выявлен серьезный косяк генератора. При нулевом или очень большом (типа 0xFFFFFFFE) значении random_seed генератор намертво зацикливался, возвращая каждый раз 0. Это можно обойти, добавив в WRandom дополнительную проверку значения random_seed. Как побочный положительный момент, генератор становится самоинициализирующимся и больше не требует вызова WRandomInit. Функция WIRandom остается без изменений.
;---------------------------------------------
; 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
Теги:
Генератор случайных чисел
Добавлено: 10 Апреля 2018 20:16:23 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...