Еще один генератор случайных чисел на Ассемблере

В закромах Родины нашлась реализация еще одного генератора случайных чисел. Он более громоздкий, чем минимальный генератор Парка-Миллера, использует операции с плавающей точкой и кольцевой буфер, но результаты выдает гораздо лучше. Кроме большого кода ему также требуется больше места и в сегменте данных:
section '.data' data readable writeable
 
; Данные для генератора случайных чисел
JJ      =       10             ; lag 1
KK      =       17             ; lag 2, size of circular buffer
R1      =       19             ; rotate count
R2      =       27             ; rotate count
randp1  dt      1.5            ; used for conversion to float
        dw      0              ; alignment
p1      dd      0              ; pointer in circular buffer
p2      dd      0              ; pointer in circular buffer
randbuf dd      (2*KK) dup(?)  ; circular buffer

Перед первым использованием генератор требуется инициализировать, вызвав функцию WRandomInit с каким-нибудь случайным значением, например, количеством тиков таймера:
        ; Инициализация генератора случайных чисел
        invoke  GetTickCount
        stdcall WRandomInit,eax
        ...

После инициализации генератор можно использовать в двух режимах: генерация одиночного случайного числа размером в два двойных слова (функция WRandom) или генерация случайного числа в заданном диапазоне от 1 до 0FFFFFFFEh (функция WIRandom). Формулировка "одиночное случайное число", наверное, не совсем подходит по смыслу к выполняемому действию, но тавтология "произвольное случайное число" мне показалась хуже.
        ; Получить случайное число
        stdcall WRandom
        ; В регистрах EDX:EAX случайное число
        ...
        ; Получить случайное число от 1000 до 100000
        stdcall WIRandom,1000,100000
        ; В регистре EAX случайное число
        ...

Одиночное случайное число возвращается в паре регистров EDX:EAX, случайное число из заданного интервала возвращается в регистре EAX. Если границы указаны неправильно, то EAX=80000000h. А вот и сам код генератора, данные к нему описаны выше.
;---------------------------------------------
; Получить случайное число
; stdcall WRandom
; на выходе EDX:EAX - случайное число
;---------------------------------------------
proc    WRandom
        stdcall WBRandom
        or      edx, 80000000h
        mov     dword [randp1],eax
        mov     dword [randp1+4],edx
        fld1
        fld     [randp1]
        ret
endp
 
;---------------------------------------------
; Получить случайное число в нужном интервале
; stdcall WIRandom,min,max
; на выходе EAX - случайное число
; EAX=80000000h - ошибка
;---------------------------------------------
proc    WIRandom rmin:dword,rmax:dword
        push    ebx
 
        stdcall WBRandom
        ; Проверить корректность задания границ, MIN<MAX
        mov     ebx,[rmax]
        sub     ebx,[rmin]
        js      .WIRandom_1
        inc     ebx
        mov     ecx,edx
        mul     ebx
        mov     eax,ecx
        mov     ecx,edx
        mul     ebx
        add     eax,ecx
        adc     edx,[rmin]
        mov     eax,edx
 
        pop     ebx
        ret
 
.WIRandom_1:
        mov     eax,80000000h
        pop     ebx
        ret
endp
 
;---------------------------------------------
; Инициализация генератора случайных чисел
; stdcall WRandomInit,seed
;---------------------------------------------
proc    WRandomInit seed:dword
        mov     eax,[seed]
        xor     ecx,ecx
.WRandomInit_1:
        imul    eax,2891336453
        inc     eax
        mov     [randbuf+ecx*4],eax
        inc     ecx
        cmp     ecx,KK*2
        JB      .WRandomInit_1
 
        fld1
        fstp    [randp1]
        mov     [p1],0
        mov     [p2],JJ*8
        stdcall WBRandom
 
        push    edi
 
        mov     edi,30
.WRandomInit_2:
        stdcall WBRandom
        dec     edi
        jnz     .WRandomInit_2
 
        pop     edi
        ret
endp
 
;---------------------------------------------
; Вспомогательная процедура генератора
; В пользовательском не вызывается
;---------------------------------------------
proc    WBRandom
        push    ebx
        mov     ebx, [p1]
        mov     ecx, [p2]
        mov     edx, [randbuf+ebx]
        mov     eax, [randbuf+ebx+4]
 
        rol     edx, R1
        rol     eax, R2
        add     edx, [randbuf+ecx]
        add     eax, [randbuf+ecx+4]
        mov     [randbuf+ebx], eax
        mov     [randbuf+ebx+4], edx
        sub     ebx, 8
        jnc     .WBRandom_1
        mov     ebx, (KK-1)*8
.WBRandom_1:
        sub     ecx, 8
        jnc     .WBRandom_2
        mov     ecx, (KK-1)*8
.WBRandom_2:
        mov     [p1], ebx
        mov     [p2], ecx
        pop     ebx
        ret
endp

Этот генератор можно использовать в проектах, где требуется более широкий разброс случайных чисел, нежели выдает генератор Парка-Миллера, и где не так критична минимизация кода.
Теги:
генератор случайных чисел
Добавлено: 10 Апреля 2018 20:07:20 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...