线性同余法取随机数

线性同余法求伪随机数,Linear-Congruential: (a * x + c) % m, a > 0, m > 0, m % a < m / a.


首先,说明一下取随机数一般会用rand函数,取time.h文件中的clock()作为种子,产生我们需要的随机数


#include 
#include  //srand()、rand()
#include  //time();
int main() {
    int n;
    srand((unsigned)time(NULL)); //设置随机数种子
    for (int i= 0; i< 10; i++) {
	n = (rand() % 10) + 1 ;//产生1~10的随机数
    //rand()产生的是一个很大的数,对其求余就可以达到限定范围的目的
    printf("%d ", n);
}
    return 0;
}

但是如果我们产生随机数种子的周期小于1s,那么就会产生一系列相同的随机数。总而言之,当产生随机数的周期非常非常小时,用已无法满足这一需求。因此,我们采用线性同余法,也就是较为简便的方法,那就是用现有的随机数种子来产生新的种子,也就是Linear-Congruential generator(线性同余数发生器)。

它是根据递归公式:

seed = (a*seed) mod m;
or
seed = (a*seed + c) mod m;
这条公式需要满足:
a, m为正整数,m> a, m> seed;

它产生的随机数是以m为周期的,一般我们以2^32- 1为周期,a又不能取太大,所以为了避免取模溢出,

我们用一种方法解决:

Schrage’s Method Revealed算法,将公式形式变换,避免了溢出。

x = (a*x) mod m
= a*(x mod Q) - R*[xi/Q];
if (x > 0) result_seed = x;
else result_seed = x + m;
具体实现:

random_my.h文件

namespace RAND_GEN {
class mod_my {
  public:
    long long m, a, c;
    mod_my(long long _m, long long _a, long long _c) : m(_m), a(_a), c(_c) {}
 
    //  General case for x = (ax + c) mod m -- use Schrage's algorithm
    //  to avoid integer overflow.
    //  (ax + c) mod m can be rewritten as:
    //    a(x mod q) - r(x / q)  if >= 0
    //    a(x mod q) - r(x / q)  otherwise
    //  where: q = m / a , r = m mod a
    //
    //  Preconditions:  a > 0, m > 0.
    //
    //  Note: only works correctly for m % a < m / a.
    long long calc(long long x) {
        if (a == 1) {
            x %= m;
        } else {
            long long q = m / a;
            long long r = m % a;
            long long t1 = a * (x % q);
            long long t2 = r * (x / q);
            if (t1 >= t2) x = t1 - t2;
            else x = m - t2 + t1;
        }
        if (c != 0) {
            const long long d = m - x;
            if (d > c) x += c;
            else x = c - d;
        }
        return x;
    }
};
 
class linear_congruential_engine_my {
  public:
    long long multiplier, increment, modulus;
    unsigned long long default_seed_my, seed_my;
    mod_my mod_temp;
 
    linear_congruential_engine_my()
    : multiplier(16807), increment(1), modulus(2147483647)
    , default_seed_my(1u), mod_temp(modulus, multiplier, increment)
    { seed(default_seed_my); }
 
    linear_congruential_engine_my(long long _m, long long _a,
    long long _c, long long _s)
    : multiplier(_a), increment(_c), modulus(_m)
    , default_seed_my(_s), mod_temp(modulus, multiplier, increment)
    { seed(default_seed_my); }
 
    void seed(unsigned long long s)
    { seed_my = s; }
 
    long long min()
    { return  increment == 0u ? 1u : 0u; }
 
    long long max()
    { return modulus - 1u; }
 
    void discard(unsigned long long z)
    { for (; z != 0ULL; --z) (*this)(); }
 
    long long operator()() {
        seed_my = mod_temp.calc(seed_my);
        return seed_my;
    }
};
 
}
main.cpp

#include 
#include "random_my.h"
using namespace std;
using namespace RAND_GEN;
 
void test_calc() {
    mod_my mod_1(9223372036854775807, 16807, 1);
    if (mod_1.calc(9223372036854775) != 7443261233741790514 ||
        mod_1.calc(922337203685477580) != 6456360425798331301 ||
        mod_1.calc(9223372036852222220) != 9223371993936639099 ||
        mod_1.calc(922337203685473330) != 6456360425726901551 ||
        mod_1.calc(9223372022254775806) != 9223126654654759001)
        cout << "Your calc() is wrong.\n";
    else cout << "Pass all tests for calc().\n";
}
 
void test_engin() {
    linear_congruential_engine_my lce;
    int count = 1000;
    int num[1001] = {0};
    while (count--) num[lce()%5]++;
    if (num[0] != 216 || num[1] != 190 ||
        num[2] != 203 || num[3] != 216 ||
        num[4] != 175) {
        cout << "Your engin class is wrong in generator.\n";
        return;
    } else if (lce.min() != (lce.increment == 0u ? 1u : 0u)) {
        cout << "Your engin class is wrong in min().\n";
        return;
    } else if (lce.max() != (lce.modulus - 1u)) {
        cout << "Your engin class is wrong in max().\n";
        return;
    }
    else cout << "Pass all tests for class engin.\n";
}
 
void hard_test() {
    long long m, a, c, n, num[5] = {0};
    unsigned long long s;
    unsigned long long discard_n;
    cin >> m >> a >> c >> s;
    mod_my mod_1(m, a, c);
    for (int i = 0; i < 5; i++) {
        cin >> n;
        cout << "(MOD CALC) ";
        cout << n << ": " << mod_1.calc(n) << endl;
    }
    linear_congruential_engine_my lce(m, a, c, s);
    cin >> discard_n;
    lce.discard(discard_n);
    cin >> n;
    while (n--) num[lce()%5]++;
    for (int i = 0; i < 5; i++) {
        cout << "(ENGIN) ";
        cout << i << ": " << num[i] << endl;
    }
}
 
int main() {
    int t;
    cin >> t;
    if (t == 0) {
        test_calc();
        test_engin();
    } else {
        hard_test();
    }
    return 0;
}





你可能感兴趣的:(C++)