兰德() : 奇怪的行为

rand() : weird behavior

本文关键字:兰德      更新时间:2023-10-16

我正在对一个问题进行数值模拟,我需要首先生成一个包含 N/2 0 和其他 1 的 N 个元素数组。每次迭代时,数组被洗牌,下一个数组元素从上一次迭代中随机选择,直到只剩下 0 或 1。我正在记录 T 次试验中的迭代次数。为了生成随机整数,我正在使用rand()的丢弃模方法(从这里得到这个想法(。

#include <iostream>
#include <ctime>    
#include <cstdlib>  
#include <fstream>      
#include <algorithm> 
#include <array>
using namespace std;
//generate random integer between 0 and MAX
int randomn(int MAX);
int main()
{
const int N = 10000;
const int T = 100;     //Number of trials
srand((unsigned)time(0));   
ofstream writefile ("Observation.out");
for (int indexT = 0; indexT < T; indexT++) {
//initializing myArray
array<bool, N> myArray;
array<bool, N> newArray;
auto middle = myArray.begin() + myArray.size() / 2;
fill(myArray.begin(), middle, false);
fill(middle, myArray.end(), true);
int counterIt = 0;  //desired Iteration number
for (;;) {
int counterF = 0;
int randompos = 0;
bool savedata = true;
//suffling myArray using Fisher–Yates shuffle
for (int indexN = N-1; indexN > 0; indexN--) {
randompos = randomn(indexN);
savedata = myArray[randompos];
myArray[randompos] = myArray[indexN] ;
myArray[indexN] = savedata;
}           
//next Iteration
for (int indexN = 0; indexN < N; indexN++) {
randompos = randomn(N-1);
savedata = myArray[randompos];
newArray[indexN] = savedata;
if (savedata == false){
counterF += 1;
}
}
copy(begin(newArray), end(newArray), begin(myArray));
//updating Iteration number
counterIt += 1;
if ((counterF == 0)|| (counterF == N)) {
break;
}
}
writefile << indexT+1 <<"t"<<counterIt <<endl;
}
writefile.close();
return 0;
}
int randomn (int MAX){
int temp;
for (;;){
temp = rand();
if ( temp < RAND_MAX - RAND_MAX%(MAX+1) ) 
return temp%(MAX+1);
}
}

输出非常有趣。输出中的前几个数字(每次试验的迭代次数(不同,但无论我运行多少次,它都会收敛为振荡。 下面是输出的两个示例:

1st run            2nd run
1  28278          1    13583  
2  7754           2    7308   
3  11308          3    22580  
4  5093           4    6307    ** oscillation starts
5  4952           5    42060   
6  5017           6    10485   
7  10400          7    8525    
8  6307   **      8    31061   
9  42060          9    6307   ** 1st period 
10  10485         10   42060  
11  8525          11   10485 
12  31061         12   8525   
13  6307   **     13   31061  
14  42060         14   6307   ** 2nd period 
15  10485         15   42060 

现在我知道rand()不是这项工作的最佳函数(更好的选择是 c++11 中的<rand>库(。 但是它是如何从任何初始随机数收敛到这个确切周期的6307 - 42060 - 10485 - 8525 - 31061

观察:程序在该时间段内精确地使用2^31随机数,即随机数生成函数的周期。但是怎么做呢?

rand()

不应该用于任何严肃的事情。它的质量可能很差。

例如,我用它做了一个模拟,我知道确切的答案。随着rand(),模拟收敛到一个与确切答案略有不同的数字。我用更好的东西替换了rand(),并且:

  • 仿真速度提高 3 倍
  • 仿真收敛到精确的解决方案。

一个常见的建议是改用梅森纳捻线机。然而,即使是MT也有其缺点,它没有通过BigCrush测试。

然而,这个简单的随机生成器通过,而且非常快(xorshift128+(:

uint64_t s[2]; // seed this
uint64_t next(void) {
uint64_t s1 = s[0];
const uint64_t s0 = s[1];
const uint64_t result = s0 + s1;
s[0] = s0;
s1 ^= s1 << 23; // a
s[1] = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5); // b, c
return result; 
}

查看 http://xoroshiro.di.unimi.it/,以及他们最新的发电机xoroshiro128+