52704.fb2 Фундаментальные алгоритмы и структуры данных в Delphi - читать онлайн бесплатно полную версию книги . Страница 91

Фундаментальные алгоритмы и структуры данных в Delphi - читать онлайн бесплатно полную версию книги . Страница 91

begin

{получить случайное число с помощью первого генератора}

k := FSeed1 div ql;

FSeed1 := (al * (FSeed1 - (k * ql))) - (k * rl);

if (FSeed1 <= 0) then

inc(FSeed1, m1);

{получить случайное число с помощью второго генератора}

k := FSeed2 divq2;

FSeed2 := (a2 * (FSeed2 - (k * q2))) - (k * r2);

if (FSeed2 <= 0) then

inc(FSeed2, m2);

{объединить два случайных числа}

Z := FSeed1 - FSeed2;

if (Z <= 0) then

Z := Z + m1 - 1;

Result := Z * OneOverMl;

end;

procedure TtdCombinedPRNG.cpSetSeed1(aValue : longint);

const

m1 = 2147483563;

begin

if (aValue > 0) then

FSeed1 := aValue

else

FSeed1 := GetTimeAsLong;

{убедиться, что случайное число находится в диапазоне от 1 до m-1 включительно}

if (FSeed1 > - m1-1) then

FSeed1 := FSeed1 - (m1-1) + 1;

end;

procedure TtdCombinedPRNG.cpSetSeed2(aValue : longint);

const

m2 = 2147483399;

begin

if (aValue > 0) then

FSeed2 := aValue else

FSeed2 := GetTimeAsLong;

{убедиться, что случайное число находится в диапазоне от 1 до m-1 включительно}

if (FSeed2 >=m2-1) then

FSeed2 := FSeed2 - (m2 - 1) + 1;

end;

Как видите, код метода AsDouble в листинге 6.9 содержит два мультипликативных линейных конгруэнтных генератора: первый с параметрами {а, m} = {40014,2147483563}

и второй с параметрами {а, m} = {40692, 2147483399}.

Циклы обоих генераторов отличаются, но, тем не менее, близки к 2(^31^). Для преобразования промежуточного значения типа longint в значение типа double используется генератор с более длинным циклом.

Приведенный в листинге 6.9 генератор исключает двухмерную регулярность простого мультипликативного линейного конгруэнтного генератора, в чем можно убедиться с помощью программы тестирования. Можно показать, что длина цикла полученного комбинированного генератора составляет примерно 2 * 10(^18^). (Для сравнения, длина цикла стандартного генератора Delphi примерно равна 4 * 10(^9^).) Последовательность, вычисляемая с помощью комбинированного генератора полностью, определяется двумя начальными числами - по одному для каждого внутреннего генератора, в то время как для простого мультипликативного генератора было достаточно одного числа.

Аддитивные генераторы

Второй стандартный метод получения "более случайных" чисел от простого генератора называется аддитивным.

В соответствии с этим методом, мы инициализируем массив чисел с плавающей запятой с помощью простого генератора, например, минимального стандартного генератора случайных чисел, а затем используем два индекса в массиве для генерации последовательности случайных чисел на основе следующего алгоритма. Складываем значения, на которые указывают два индекса и записываем результат в элемент, на который указывает первый индекс (если полученная сумма будет больше 1.0, перед сохранением результата мы вычитаем из суммы значение 1.0). Возвращаем полученное значение в качестве следующего случайного числа. Перемещаем оба индекса вперед на одну позицию, при необходимости переходя от конца массива к его началу. Далее процесс повторяется снова.

Листинг 6.10. Аддитивный генератор

type

TtdAdditiveGenerator = class (TtdBasePRNG) private

FInx1 : integer;