一次生成随机数引发的思考
问题描述和解决
问题描述:在学习一致性hash的时候遇到的一个问题,当我使用随机字符串测试的时候,造成了雪崩效应(所有的数据都打到一个机器上)。
bug定位:首先我排除了hash函数(没有哈希冲突),最终将问题定位在随机字符串生成上。我的随机字符串生成的代码是下面这样的。随机字符串生成的逻辑很简单,在结果字符串的位置 i 上,从备选字符中随机挑选一个字符作为当前位置的元素。但是问题出现在每次生成随机字符串都要重置种子上,这样会导致一个问题:在超短的时间内生成的字符串是一样的。
std::string getRandStr(const int& len){
static std::string bkStr = "0123456789qwertyuiopasdfghjklzxcvbnm";
std::string randStr(len, ' ');
srand((unsigned int)(time(NULL)));
for(int i = 0; i < len; i++){
randStr[i] = bkStr[rand() % bkStr.size()];
}
std::cout << randStr << endl;
return randStr;
}
解决办法也很简单,只需要把重置随机数种子的语句删除掉即可
进一步的思考:随机数是如何生成的
显然,问题不能停留在解决问题的表面,应该更深一层次的思考这种现象出现的原因。这需要进一步去认识到随机数是如何生成的,下面就介绍一下随机数生成机制。
前言:我们所有的随机数生成都是伪随机数。为什么叫伪随机数呢,肯定因为他不是真正的随机数,真正的随机数不依赖任何条件,是完全随机的。我们目前生成随机的方式大都是线性同余的变种。
产生随机数的原理是: y = (a * x + b ) % m,其中a, b, n 都是常数,a 和 n 是很大的素数, 每次的x都是上一次的y,最初的x被称为种子,一般种子确定为计算机当前的时间。下面是rand和srand的源码
unsigned long int next = 1;
/* rand: return pseudo-random integer on 0..32767 */
int rand ( void )
{
next = next * 1103515245 + 12345;
return (unsigned int )(next / 65536) % 32768;
}
/* srand: set seed for rand() */
void srand (unsigned int seed)
{
next = seed;
}
从源码中可以看出来,使用一个数字作为随机数种子,那么之后出现的随机数都是固定的。因此最初问题的根源在于使用了相同的数字初始化随机数种子,导致了每次生成的随机数都是一样的。这给我一个经验:要想在短时间内得到大量的随机字符串,不要重复初始化我们的的随机数种子。