单机的小项目,后台内部用,不需要开放,要求就是给数据生成一个 8 位纯数字的序列号,本身不需要包含什么含义,只要能通过序列号找到这个数据,不重复。 数据量也很小,可能一年都不会超过 1000 个,所以我在数据库设置了唯一索引后直接就用生成随机数的函数了,如下:
mt_rand(10000000,99999999);
但刚好我同事看到,说建议我改成:
str_shuffle(mt_rand(10000000,99999999));
说这样更好,更随机,重复的概率更低。 感觉不太对劲,我个人感觉这样不会更随机吧?但我对函数的底层的东西没什么深入研究。
我 google 了一下,但我可能搜索姿势不太对没找到完全相同的场景,但我看到有类似的代码,答案有提到说 str_shuffle 不会增加额外的熵。
是不是就是意思这样做不会增加随机性也不会让重复的概率变低?
所以发这个贴子来问问大家,谢谢!如果有相关的资料可以让我加深理解就更好了!
1
june4 2023-12-25 20:38:05 +08:00
你不是应该要你同事给出这样更随机的证明吗
|
2
infun 2023-12-25 20:41:13 +08:00
分别调用个十万次看看先?
|
3
sadfQED2 2023-12-25 20:41:46 +08:00 via Android
啊?如果随机出来的数是 1000000 ,你 str_shuffle 之后变成 00000001 ,这不会出问题吗?
我没看过 mt_rand 的实现方法,但是如果每个数出现概率一样的话,你再加个打乱逻辑的话,纯粹脱了裤子放屁。 如果 mt_rand 每个数出现概率不一样的话,那这样打乱一下有意义 |
4
maggch97 2023-12-25 20:42:59 +08:00
你要做的是处理出现重复数字之后的异常处理,这个概率并不低。
|
5
dobelee 2023-12-25 20:44:11 +08:00 via iPhone 2
觉得随机买一张彩票概率太低,于是买了十张,手动混一混,再从里面选一张。
|
6
imdong 2023-12-25 20:44:31 +08:00
对于你描述的场景,二者没法证明有实质意义的区别。
如果真的需要安全的随机数,我觉得你们需要的不是更随机的随机数,而是更可控的随机数。 说白了,就是需要一个看起来是随机但实际上不是随机的随机数。 |
7
passive 2023-12-25 20:44:51 +08:00 via Android 1
两个均匀分布相加或者嵌套不再是均匀分布,无数个均匀分布相加或者嵌套最后会变成正态分布🐶
|
8
way2create OP @maggch97 这个有做的 而且实际上并不是单纯 8 位 还有别的 这里省略了 就是好奇问的问题
|
9
way2create OP @sadfQED2 谢谢,你说的很有道理,变成 00000001 从我给的代码来看也是个问题,不过还好实际的场景不在乎这个,我这里只是用例子来表达我要问的问题,是我的疏忽。
|
10
maggch97 2023-12-25 20:51:42 +08:00
@way2create 做了兜底就别管你同事说啥了。没有什么恶意,但是我不觉得写 PHP 的对随机数生成有什么深入的理解
|
11
way2create OP @way2create 我是不深入 他我不知道 就是因为这事产生的疑问 水平有限所以来问问
|
12
geelaw 2023-12-25 21:00:57 +08:00
目测 mt_rand(10000000, 99999999) 的输出一定以 1 开始,那自然不是均匀随机的 8 位纯数字序列号(当然 MT 本身就不够随机,但这是另一码事儿了),毕竟均匀随机的 8 位纯数字序列号以 1/10 的概率以 0 开始。
对这个分布套上 str_shuffle 是会“变得更随机”(增加分布的熵)的,但 str_shuffle 之后得到的依然不是八位纯数字序列号,因为它不可能是 00000000 ,而真正随机的以 10^(-8) 的概率出现 00000000 。 如楼上 @maggch97 所说,这个数据范围出现碰撞的概率不小。最简单的做法是:预先生成好 0 到 99999999 的一个置换(打乱一个 0 到 99999999 的列表,并存下来),然后每次需要的时候取用。 |
13
kingjpa 2023-12-25 21:08:55 +08:00
time().mt_rand(1111,9999)
|
14
clemente 2023-12-25 21:12:53 +08:00
mt_rand(10000000, 99999999) 已经足够随机,并且使用 str_shuffle 不会增加其随机性。
mt_rand 函数使用 Mersenne Twister 算法生成伪随机数,而且已经足够随机,无需额外的操作。在你的用例中,使用 str_shuffle 并不会增加数字的随机性,因为它是为字符串设计的。 |
15
way2create OP @geelaw 谢谢你的回答 因为我们项目数据量实在是太小了 所以我只做了简单的随机数+万一重复的错误处理 感谢你提的方案 不过其实代码跟实际需求有点不一样 我省略了主要是想问标题本身的
|
16
way2create OP @clemente 谢谢 我 Google 的时候就是有看到类似的答案 想着来 V2 确认一下的
|
17
newaccount 2023-12-26 09:17:39 +08:00
随机数要保证结果的均匀分布,str_shuffle 这东西不像是做这个用的,理论上讲,加了之后是负优化
随机数算法的测试,印象中是在 《 delphi 算法与数据结构》这本书上看到过,通过撒点图形看是否存在不正常的 pattern ?年头有点久,记不太清了 算法是通用的,你看看别的语言实现的随机数算法评估方法 |
18
CodeCodeStudy 2023-12-26 09:18:20 +08:00
用 random_int 这个函数
random_int(int $min, int $max): int |
19
way2create OP @newaccount 好的谢谢我去搜搜看 其实我也就是单纯好奇他加这个函数 除了表面上的打乱 究竟是真的优化还是多此一举负优化
|
20
afeiche 2023-12-26 09:28:05 +08:00
我感觉需求和实现不匹配,需求是要唯一,而实现是生成随机数,但是用 rand 应该是会有一定的重复概率,不如按顺序生成靠谱一点
|
21
way2create OP @CodeCodeStudy 我的工作场景经常都是 mt_rand 就够了 你不提这个函数 用的少我都给忘了 你一提我去看了下才想起之前有用过相关的 random_bytes 与其说需要更好的 不如说我是很好奇他加个打乱函数是否真的有优化还是负优化
|
22
way2create OP @afeiche 谢谢 是我描述的不太清楚 有个要求是不要有规律的所以不能顺序 而且因为数量太小了 就如我说的可能一年都没 1000 个 其次这个是后台用的要求低一些 也做了错误处理了 不过肯定有非常多更好的方法 我只是结合场景图省事了
|
23
dode 2023-12-26 10:32:31 +08:00
|
24
zhoushiyi 2023-12-26 17:46:33 +08:00
mt_rand 取得值最终都是 10000000-99999999 区间的有序数字,只是先用那个数字的顺序不一样吧,是不是意味着记录数大于 99999999 条后,就不会再有随机数可用了,感觉 shuffule 应该起到了 0 开头的作用,随机数可用的范围多了,例如: 随机数 11223300 -> 00223311 rand 是不会产生 00223311 这样的随机数的
|
25
CodeCodeStudy 2023-12-26 18:36:22 +08:00
https://github.com/php/php-src/blob/master/ext/standard/string.c#L5630
```c PHPAPI bool php_binary_string_shuffle(const php_random_algo *algo, php_random_status *status, char *str, zend_long len) /* {{{ */ { int64_t n_elems, rnd_idx, n_left; char temp; /* The implementation is stolen from array_data_shuffle */ /* Thus the characteristics of the randomization are the same */ n_elems = len; if (n_elems <= 1) { return true; } n_left = n_elems; while (--n_left) { rnd_idx = algo->range(status, 0, n_left); if (EG(exception)) { return false; } if (rnd_idx != n_left) { temp = str[n_left]; str[n_left] = str[rnd_idx]; str[rnd_idx] = temp; } } return true; } ``` https://github.com/php/php-src/blob/master/ext/random/random.c#L423 ```c PHPAPI zend_long php_mt_rand_range(zend_long min, zend_long max) { return php_random_algo_mt19937.range(php_random_default_status(), min, max); } ``` 可以看出,都是调用某个算法的 range 函数,所以 str_shuffle 和 mt_rand 的底层都是相似的,差别可能就是算法不一样 |
26
way2create OP @CodeCodeStudy 说到这个 我看过源码早期版本的 rnd_idx = algo->range(status, 0, n_left);这行甚至直接就是调用的 php_rand() 而更旧版本的 php_rand() 好像还不如 php_mt_rand() 有些版本 php_rand 就等同于 php_mt_rand()了 但我不太懂 C
|
27
CodeCodeStudy 2023-12-27 09:40:58 +08:00
@way2create #26 对,旧版本的一样的
https://www.php.net/manual/en/function.rand.php As of PHP 7.1.0, rand() uses the same random number generator as mt_rand(). |