Решил написать статью о том, как работает генератор случайных чисел. С помощью функции Random можно узнать какое «случайное» число выдаст программа.
1. Как работает процедура Randomize?
Процедура Randomize инициализирует генератор случайных чисел, задавая значение переменной RandSeed, вычисленное с помощью системных часов.
procedure Randomize;
var SystemTime: TSystemTime;
begin
GetSystemTime(SystemTime);
With SystemTime do
RandSeed:=((wHour*60 + wMinute)*60 + wSecond)*1000 + wMilliseconds;
end;
2. Как работает функция Random?
Рассмотрим работу функции для целых чисел.
function Random(Range: integer): integer;
var z: int64;
begin
RandSeed:=RandSeed*$8088405 + 1;
z:=RandSeed;
If z<0 then
z:=z+$100000000;
z:=z*Range;
Result:=z div $100000000;
end;
Согласно этой функции, последовательность чисел зависит от переменной RandSeed. Но, если значение переменной RandSeed будет всегда одинаковым, то мы получим одинаковый набор случайных чисел.
Еще одна особенность заключается в том, что переменная RandSeed всегда изменяется при вызове функции Random, т.е. устанавливает генератор случайного числа на следующее значение.
Напишем эту функцию на ассемблере.
function Random(Range: integer): integer;
asm
MOV EAX,Range
IMUL EDX,RandSeed,$8088405
INC EDX
MOV RandSeed,EDX
MUL EDX
MOV Result,EDX
end;
Рассмотрим работу функции для вещественных (дробных) чисел.
function Random: extended;
const two2neg32 = 1/$100000000; // 2^-32
var z: int64;
begin
RandSeed:=RandSeed*$8088405 + 1;
z:=RandSeed;
If z<0 then
z:=z+$100000000;
Result:=z*two2neg32;
end;
Напишем эту функцию на ассемблере.
function Random: extended;
const two2neg32: double = 1/$100000000; // 2^-32
asm
IMUL EDX,RandSeed,$8088405
INC EDX
MOV RandSeed,EDX
FLD QWORD PTR two2neg32
PUSH $0
PUSH EDX
FILD QWORD PTR [ESP]
ADD ESP,$4
POP EDX
FMULP
FSTP TBYTE PTR Result
end;
Коды получены с помощью Delphi