随机数问题小结
1、利用(0,1)之间的随机数生成器rand()生成(a,b)之间的随机数:(b-a)/rand() + a;
2、利用(0,a)之间的随机数生成器rand()生成(0,b)之间的随机数:只要a和b之间没有倍数关系
3、求一个string的最长回文前缀子串:用逆序的数组作为patten,用KMP算法求最后停在哪里。
4、假设random(0,1)输出结果是50%的0和50%的1 ,要求利用random(0,1)随机在[a,b]内产生一个数。
题目2的解答(答案来自水木精华区):
已知 所给的随机数发生器的样本空间为 [0, M),即 [0, RAND_MAX]; 结果的样本空间大小 N,即 divisor。 考虑 M 进制数 D,即 dividend。 while divisor > space and error_bound > tolerant_error do // divisor > space 后面说; // error_bound 是误差上界,tolerant_error 是可容忍的误差, // 注意 tolerant_error 可为 0,但为 0 时不能保证算法停机。 dividend = remainder * (RAND_MAX+1) + random(0, RAND_MAX) // M 进制数 D 的次低位取 R(详见下文),而最低位在[0, M)上随机生成。 // 这里 random(0, RAND_MAX) 是原题中所给的随机数发生器。 remainder = dividend % divisor // D 除以 N 的余数 R 就是所求随机数,如果不考虑误差的话。 // (以上误差指的是结果分布的误差,定义参见原帖) // 考虑 D 值的样本空间[0, S),S = space * M, // 这里 space 是下条语句执行前的值。 space = space * (RAND_MAX+1) - dividend + remainder // 如果 floor(D/N+1)*N <= S,即 D 没有落在最后的“零头”上, // 那么 R 在[0, N)上均匀分布的。 // floor(D/N+1)*N 即 D-R+N,D-R+N <= S 即 N <= S-D+R, // 而此时 space = S-D+R,这就是前面为什么 while divisor > space // 如果 D 落在了最后的零头上,简单的办法就是重来, // 但这里将“零头” R 放在下次生成的 D 的次高位,以 减少调用 // 随机数发生器的次数 并 控制误差。 // (R 可能大于 M,不过这不影响算法。) error_bound /= (RAND_MAX+1) // M 进制数 D 每多生成一位,误差上界缩小到 1/M。 以上。 另一个等价的做法是逐位生成 M 进制纯小数 D,如果在小数点后第 B 位上, 存在 R 使 [D, D+1/M^B) 完全落在 [R/N, (R+1)/N) 内,那么该 R 值即所求, 如果只是有交集,那么就是还有误差,如果该误差在容忍范围内,那么 R 即所求, 否则就继续生成更多位。注意这里的区间是连续的。 以上做法的主要目的是在可容忍误差的情况下,保证算法可在确定步数内停机。 如果不能容忍误差,并且 N 和 M 的质因数集(非重集)不等,那么不存在有限算法, 严格来说就是不存在正确算法。
问题4的解答:random(0,1)每次都生成0和1,概率分别是0.5。考虑二进制数,设置n = b -a + 1, 找一个最小的m使得2^m>=n.
int generateRandom(int a, int b){ while(1){ int x = 1; int n = b -a + 1; for(int i = 0; i < m; i++){ x = x & random(); x >> 1; } if ( x >= 0 && x <= n){ return x + a; } } }