Решил рассказать немного о генерации псевдослучайных чисел. Почему псевдослучайных? Потому что все "случайные" числа в компьютере получают с помощью математических выражений, у процессора нет понятия "случайный", т.к. это устройство, которое работает по своим, строго заданным, правилам.
Не сложно догадаться, где используются алгоритмы генерации псевдослучайных чисел. В основном это механизмы защиты.
Есть множество алгоритмов генерации псевдослучайных чисел (XorShift, линейный конгруэнтный генератор, перемешанный конгруэнтный генератор и т.д.), они имеют разную скорость генерации чисел, также у генераторов псевдослучайных чисел есть математическая (через некоторое количество сгенерированных чисел, числа начнут повторяться), а также, зная алгоритм, можно получить исходное число, которое было преобразовано в псевдослучайное.
К генераторам псевдослучайных чисел можно отнести и HASH - функции, отличие от генераторов псевдослучайных чисел заключается в том, что обратного алгоритма для получения исходного числа не существует, например, многие знакомы или слышали про алгоритм MD5, а также есть такие популярные алгоритмы, как MurMur3 и WangHash.
Я покажу генерацию псевдослучайных чисел на примере алгоритма Парка - Миллера, математическое выражение таково: I(j+1)=a*I(j)(mod m). Это математическое выражение я упростил до I= (seed*a) mod m, в котором seed - это исходное число, которое будет преобразовано в число в диапазоне от 0 до m. Есть два значения числа а, которые используются в генераторе Парка - Миллера, это 48271 или число 65537, использовать можно любое на своё усмотрение. При генерации нескольких чисел, seed всегда является предыдущим сгенерированным числом.
А откуда же взять исходное число для преобразования? Если подумать, то вариантов не так уж и много, единственная область памяти, где записанные данные периодически меняются, это область памяти часов реального времени, я буду использовать за исходное время, записанное в области памяти BIOS.
Давайте напишем процедуру генерации псевдослучайных чисел и получим псевдослучайную строку на экране:
Код: Выделить всё
.model tiny
.286
code segment
org 100h
start:
mov cx, 80d
lea di, msg
write_array:
push 30h ;минимальное число } диапазон
push 122d ;максимальное число } чисел
call random
mov byte ptr[di], al
inc di
loop write_array
mov ah, 9d
lea dx, msg
int 21h
mov ax, 4c00h
int 21h
msg db 81d dup(24h)
;--------------------------------------
;Generate random numbers
;Input(Stack):
;-Minimal number in range(0-65535);
;-Maximum number in range(0-65535);
;Out:
;-Number in ax
random proc uses bp dx bx
mov bp, sp
mov ax, word ptr ss:[bp+8] ;максимальное число в заданном диапазоне
mov m, ax ;записать в переменную m
mov ax, strt
cmp ax, -1d ;проверить seed, если не равен -1
jnz randomize ;то начать преобразование
push ds ;если равен -1, то получить число
push 40h ;из области памяти BIOS
pop ds
mov ax, word ptr ds:[006Ch]
pop ds
mov strt, ax
randomize:
mov bx, a ;(a * seed)/m
mul bx
xor dx, dx
mov bx, m
div bx
mov strt, dx ;записать полученное число в переменную strt, это будет seed
;для последующих вызовов процедуры
cmp dx, ss:[bp+10] ;сравнить с минимальным числом в заданном диапазоне
jb randomize если полученное число меньше, то повторить преобразование
mov ax, dx
ret 4
a dw 48271
m dw 0
strt dw -1d
random endp
code ends
end start
Получилось не очень случайно, так как выражение упрощено, давайте уберём из алгоритма "запоминание" числа seed и добавим в программу задержку, которая будет позволять изменить счётчику времени значение:
Код: Выделить всё
.model tiny
.286
code segment
org 100h
start:
mov cx, 80d
lea di, msg
write_array:
push 30h
push 122d
call random
mov byte ptr[di], al
inc di
call delay ;Процедура задержки
loop write_array
mov ah, 9d
lea dx, msg
int 21h
mov ax, 4c00h
int 21h
msg db 81d dup(24h)
delay proc uses ds ax bx
push 40h
pop ds
mov ax, word ptr ds:[006Ch]
delay_:
push 40h
pop ds
mov bx, word ptr ds:[006Ch]
cmp al, bl
jz delay_
ret
delay endp
;--------------------------------------
;Generate random numbers
;Input(Stack):
;-Minimal number in range(0-65535);
;-Maximum number in range(0-65535);
;Out:
;-Number in ax
random proc uses bp dx bx
mov bp, sp
mov ax, word ptr ss:[bp+8] ;максимальное число в заданном диапазоне
mov m, ax ;записать в переменную m
get_num:
push ds ;если равен -1, то получить число
push 40h ;из области памяти BIOS
pop ds
mov ax, word ptr ds:[006Ch]
pop ds
randomize:
mov bx, a ;(a * seed)/m
mul bx
xor dx, dx
mov bx, m
div bx
cmp dx, ss:[bp+10] ;сравнить с минимальным числом в заданном диапазоне
jb get_num ;если полученное число меньше, то
;повторить преобразование
;получив новое число из области
;памяти BIOS
mov ax, dx
ret 4
a dw 48271
m dw 0
strt dw -1d
random endp
code ends
end start
Как можно видеть, разнообразие чисел очень сильно выросло, но мы пожертвовали временем выполнения программы, теперь программа выполняется гораздо дольше.
Это простой алгоритм генерации псевдослучайных чисел, есть много других алгоритмов, все они отличаются, вы можете использовать любой, который будет подходить под ваши задачи.
Была использована версия компилятора MASM 6.11 в контексте операционной системы MS DOS 6.22!
Спасибо за внимание!
С уважением, push0ret!