2018年3月21日 星期三

C C++ 隨機亂數 如何每次執行程式都不一樣

C C++ 隨機亂數 如何每次執行程式都不一樣

一開始時做亂數的時候最常遇到的問題就是怎麼每一次執行的亂數都一樣,大概是幾個原因造成的,沒有給時間種子或者是把時間種子放到for裡面一起跑了。
下面是做好的範例版本,初始化的方法是用 static 避開,static 初始化的那一行只會執行一次,然後這個變數會一直留著不會被刪除(解構)直到程序結束才跟著解構。

範例代碼

預設函式都是不包含最大值的,使用的時候可以自己手動在乎叫的時候+1,或是從函式裡面把 --max 中的 -- 移除即可。
純C的版本
/*****************************************************************
Name : 
Date : 2018/03/21
By   : CharlotteHonG
Final: 2018/03/21
*****************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int rand_int(int min, int max){
    static int init=0;
    if(init == 0){ srand((unsigned)time(NULL)), rand(), ++init; }
    return (int)((rand() / (RAND_MAX+1.0)) * (--max - min + 1.0) + min);
}

int main(int argc, char const *argv[]){
    for(unsigned i = 0; i < 10; ++i) {
        printf("%d, ", rand_int(0, 10));
    } printf("\n");

    return 0;
}
再來是C++的版本
C++版本中
VS2017 使用的預設引擎是 mt19937
GCC 有查到使用的預設引擎是 minstd_rand0 (這個會導致第一個亂數永遠一樣)
/*****************************************************************
Name : 
Date : 2018/03/21
By   : CharlotteHonG
Final: 2018/03/21
*****************************************************************/
#include <iostream>
#include <random>
#include <ctime>
using namespace std;

template<typename T>
int randNum(int min, int max) {
    static std::mt19937 generator(time(NULL));
    std::uniform_int_distribution<T> distribution(min, --max);
    return distribution(generator);
}

int main(int argc, char const *argv[]){
    for(unsigned i = 0; i < 10; ++i) {
        cout << randNum<int>(1, 10) << ", ";
    } cout << endl;
    return 0;
}

為何亂數的第一個永遠都一樣

引用至:https://goo.gl/WJDCR7 留言中提到以下內容
我查到的是 default_random_engine 是使用 minstd_rand0引擎,使用的是linear_congruential_engine
算法大致為 xi+1 = (16807xi + 0) mod 2147483647.
那個如果是用time(NULL)
我們假設目前time是137486376xxxx(x我不知道是多少不過沒差)
要生成數字為0 - 168070000經過測試第一筆亂數為
136956956xxxx * 16807 / 16807000 = 136956956
這時time為
第二次跑程式時測是為生成 0-168070000
137490578xxxx * 16807 / 16807000
由於生成的數字太小,time的前幾位數並沒有改變所以導致
第一個數字都會一樣
而事實上,當你生成的亂數max等於100000時
第一次的第一個數 : 77018
//間隔約3秒
第二次的第一個數 : 77021
可以發現只有各位數在改變
而雖然microsoft 的定義是typedef mt19937 default_random_engine;但本質上用的引擎仍為mt19937,使用的是梅森算法(別問我是甚麼),所以不會有這問題
細節還是問其他懂的人吧
我以前用亂數時也發生過這問題所以我通常會用time(clock())

沒有留言:

張貼留言