Используемый алгоритм взят из статьи П. Л'Экюйе Эффективные и портативные комбинированные генераторы случайных чисел.
Документ можно найти здесь и бесплатно загрузить с сайта здесь.
Алгоритм, используемый калькуляторами Ti, находится на правой стороне стр. 747. Я включил изображение.
а>
Я перевел это в программу на C++
#include <iostream>
#include <iomanip>
using namespace std;
long s1,s2;
double Uniform(){
long Z,k;
k = s1 / 53668;
s1 = 40014*(s1-k*53668)-k*12211;
if(s1<0)
s1 = s1+2147483563;
k = s2/52774;
s2 = 40692*(s2-k*52774)-k*3791;
if(s2<0)
s2 = s2+2147483399;
Z=s1-s2;
if(Z<1)
Z = Z+2147483562;
return Z*(4.656613e-10);
}
int main(){
s1 = 12345; //Gotta love these seed values!
s2 = 67890;
for(int i=0;i<10;i++)
cout<<std::setprecision(10)<<Uniform()<<endl;
}
Обратите внимание, что начальные значения — s1 = 12345
и s2 = 67890
.
И получил результат от эмулятора Ti-83 (извините, я не смог найти ПЗУ Ti-84):
а>
Это соответствует тому, что производит моя реализация
Я только что увеличил точность вывода в своей реализации и получил следующие результаты:
0.9435973904
0.9083188494
0.1466878273
0.5147019439
0.4058096366
0.7338123019
0.04399198693
0.3393625207
Обратите внимание, что они расходятся с результатами Ти в менее значащих цифрах. Это может быть различием в том, как два процессора (Ti Z80 по сравнению с моим X86) выполняют вычисления с плавающей запятой. Если да, то решить эту проблему будет сложно. Тем не менее, случайные числа по-прежнему будут генерироваться в той же последовательности (с оговоркой ниже), поскольку последовательность зависит только от целочисленной математики, которая является точной.
Я также использовал тип long
для хранения промежуточных значений. Существует некоторый риск того, что реализация Ti полагается на целочисленное переполнение (я не слишком внимательно читал статью L'Ecuyer), и в этом случае вам придется настроить на int32_t
или аналогичный тип, чтобы эмулировать это поведение. Предполагая, опять же, что процессоры работают одинаково.
Изменить
На этом сайте представлена следующая реализация кода на Ti-Basic:
:2147483563→mod1
:2147483399→mod2
:40014→mult1
:40692→mult2
#The RandSeed Algorithm
:abs(int(n))→n
:If n=0 Then
: 12345→seed1
: 67890→seed2
:Else
: mod(mult1*n,mod1)→seed1
: mod(n,mod2)→seed2
:EndIf
#The rand() Algorithm
:Local result
:mod(seed1*mult1,mod1)→seed1
:mod(seed2*mult2,mod2)→seed2
:(seed1-seed2)/mod1→result
:If result<0
: result+1→result
:Return result
Я перевел это на C++ для тестирования:
#include <iostream>
#include <iomanip>
using namespace std;
long mod1 = 2147483563;
long mod2 = 2147483399;
long mult1 = 40014;
long mult2 = 40692;
long seed1,seed2;
void Seed(int n){
if(n<0) //Perform an abs
n = -n;
if(n==0){
seed1 = 12345; //Gotta love these seed values!
seed2 = 67890;
} else {
seed1 = (mult1*n)%mod1;
seed2 = n%mod2;
}
}
double Generate(){
double result;
seed1 = (seed1*mult1)%mod1;
seed2 = (seed2*mult2)%mod2;
result = (double)(seed1-seed2)/(double)mod1;
if(result<0)
result = result+1;
return result;
}
int main(){
Seed(0);
for(int i=0;i<10;i++)
cout<<setprecision(10)<<Generate()<<endl;
}
Это дало следующие результаты:
0.9435974025
0.908318861
0.1466878292
0.5147019502
0.405809642
0.7338123114
0.04399198747
0.3393625248
0.9954663411
0.2003402617
которые соответствуют тем, которые были достигнуты при реализации, основанной на исходной статье.
person
Richard
schedule
25.09.2015