随机化算法(randomized algorithm)
随机化算法(randomized algorithm)的运行时间不只依赖于特定的输入,而且依赖于所发生的随机数。
对于快排而言,使用随机枢纽元(pivot),将获得
1. 随机数生成器(generator)
产生随机数的最简单方法是线性同余数发生器,它于 1951 年由 Lehmer 首先提出。随机数列 xi 的生成满足:
为了开始这个序列,必须给出
- 7,5,2,3,10,4,6,9,8,1,7,5,2…(循环出现)
# n 控制随机数的个数
def randgen(n):
A, M, x = 7, 11, 1
for _ in range(n):
x = x*A % M
yield x
如果 M 选择得很大,比如 31 比特(7FFF FFFF)的素数,那么对于大部分的应用来说,周期是非常长的,Lehmer 建议使用 31 个比特的素数
C 程序员来说,在实现时,全局变量用来存放随机数序列的当前值(本文件全局可见)。这个全局变量交由某个程序初始化。当在调试一个使用随机数的程序的时候,最好设置种子(seed,也即
static unsigned long Seed = 1;
#define A 48271
#define M 2147483647
double Random() {
Seed = (Seed * A) % M;
return (double) Seed / M;
// (0, 1)
}
void Init(unsigned long InitVal) {
Seed = InitVal;
}
对程序中的加法和乘法一定要特别注意,因为这些运算可能会产产生溢出,虽然不会提示错误,但会影响最终的结果,从而影响伪随机性。
Schrage 给出了一个即使在 32 位机器上执行也不会发生溢出的方案,计算 M/A 的商和余数分别为 Q (quotient)和 R(Remainder)。在 A = 48271,M =
则有:
等等经历一系列等价替换之后,最终得:
其中
static unsigned long Seed = 1;
#define A 48271
#define M 2147483647
#define Q ( M / A )
#define R ( M % R )
double Random() {
Seed = A*(Seed % M) - R * (Seed / Q);
if (Seed < 0)
Seed += M;
return (double) Seed / M;
}
void Init(unsigned long InitVal) {
Seed = InitVal;
}