PHP 中的实现mt_rand()
相当流畅,因此它可能会因版本而异。但是,以下是 PHP 版本 5 中使用的代码的一些摘录:
/* MT Rand */
#define PHP_MT_RAND_MAX ((long) (0x7FFFFFFF)) /* (1<<31) - 1 */
#ifdef PHP_WIN32
#define GENERATE_SEED() (((long) (sapi_get_request_time(TSRMLS_C) * GetCurrentProcessId())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
#else
#define GENERATE_SEED() (((long) (sapi_get_request_time(TSRMLS_C) * getpid())) ^ ((long) (1000000.0 * php_combined_lcg(TSRMLS_C))))
#endif
PHPAPI void php_srand(long seed TSRMLS_DC);
PHPAPI long php_rand(TSRMLS_D);
PHPAPI void php_mt_srand(php_uint32 seed TSRMLS_DC);
PHPAPI php_uint32 php_mt_rand(TSRMLS_D);
PHP_FUNCTION(mt_rand)
{
long min;
long max;
long number;
int argc = ZEND_NUM_ARGS();
if (argc != 0) {
if (zend_parse_parameters(argc TSRMLS_CC, "ll", &min, &max) == FAILURE) {
return;
} else if (max < min) {
php_error_docref(NULL TSRMLS_CC, E_WARNING, "max(%ld) is smaller than min(%ld)", max, min);
RETURN_FALSE;
}
}
if (!BG(mt_rand_is_seeded)) {
php_mt_srand(GENERATE_SEED() TSRMLS_CC);
}
从上面的最后三行中,您可以看到它mt_rand()
在第一次调用时自动播种。但是,该php_mt_srand()
函数采用 type 的参数php_uint32
。这意味着只有 2 32 个可能的种子状态mt_rand()
。因此,如果您的脚本运行大约 2 16次,则很可能mt_rand()
会产生完全相同的随机数序列。
正如 rossum 所建议的,将 AES 加密应用于递增的 128 位值会是一个更好的主意。如果您对加密结果进行 base64 编码并丢弃尾随==
,则生成的字符串将只有 22 个字符长。
附录
今天下午外出时,我让以下脚本运行:
for i in $(seq 1 100000) ; do
php -r 'for ($n=0; $n<32; $n++) echo chr(mt_rand(97,122)); echo chr(10);' >>out
done &
正如预期的那样,第一次碰撞发生在大约 2 16次迭代之后(远不及 26 16):
$ sort <out | uniq -d
vnexqclzkaluntglgadgwzjnjfsvqfhp
$ grep -n vnexqclzkaluntglgadgwzjnjfsvqfhp out
34417:vnexqclzkaluntglgadgwzjnjfsvqfhp
52159:vnexqclzkaluntglgadgwzjnjfsvqfhp