大佬们,生成随机数这样加多个函数有意义吗 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
请不要在回答技术问题时复制粘贴 AI 生成的内容
way2create
V2EX    程序员

大佬们,生成随机数这样加多个函数有意义吗

  •  
  •   way2create 2023-12-25 20:33:59 +08:00 2331 次点击
    这是一个创建于 662 天前的主题,其中的信息可能已经有所发展或是发生改变。

    场景

    单机的小项目,后台内部用,不需要开放,要求就是给数据生成一个 8 位纯数字的序列号,本身不需要包含什么含义,只要能通过序列号找到这个数据,不重复。 数据量也很小,可能一年都不会超过 1000 个,所以我在数据库设置了唯一索引后直接就用生成随机数的函数了,如下:

    mt_rand(10000000,99999999); 

    但刚好我同事看到,说建议我改成:

    str_shuffle(mt_rand(10000000,99999999)); 

    说这样更好,更随机,重复的概率更低。 感觉不太对劲,我个人感觉这样不会更随机吧?但我对函数的底层的东西没什么深入研究。

    我 google 了一下,但我可能搜索姿势不太对没找到完全相同的场景,但我看到有类似的代码,答案有提到说 str_shuffle 不会增加额外的熵。

    是不是就是意思这样做不会增加随机性也不会让重复的概率变低?

    所以发这个贴子来问问大家,谢谢!如果有相关的资料可以让我加深理解就更好了!

    第 1 条附言    2023-12-26 10:00:48 +08:00
    统一谢谢各位的回复 我自己再查阅消化一下 。

    不得不说,各位结合我提的需求场景提出的一些建议,考虑的非常周到。

    但其实需求场景跟代码不完全是这样,只是我做了一些省略和处理来引出问题,但也是我提问考虑的不够全面。

    本质是想知道这样外层套一个打乱函数除了打乱的作用,究竟是优化还是多此一举或负优化。
    27 条回复    2023-12-27 09:40:58 +08:00
    june4
        1
    june4  
       2023-12-25 20:38:05 +08:00
    你不是应该要你同事给出这样更随机的证明吗
    GlobalNPC
        2
    GlobalNPC  
       2023-12-25 20:41:13 +08:00
    分别调用个十万次看看先?
    sadfQED2
        3
    sadfQED2  
       2023-12-25 20:41:46 +08:00 via Android
    啊?如果随机出来的数是 1000000 ,你 str_shuffle 之后变成 00000001 ,这不会出问题吗?

    我没看过 mt_rand 的实现方法,但是如果每个数出现概率一样的话,你再加个打乱逻辑的话,纯粹脱了裤子放屁。

    如果 mt_rand 每个数出现概率不一样的话,那这样打乱一下有意义
    penzi
        4
    penzi  
       2023-12-25 20:42:59 +08:00
    你要做的是处理出现重复数字之后的异常处理,这个概率并不低。
    dobelee
        5
    dobelee  
       2023-12-25 20:44:11 +08:00 via iPhone   2
    觉得随机买一张彩票概率太低,于是买了十张,手动混一混,再从里面选一张。
    imdong
        6
    imdong  
       2023-12-25 20:44:31 +08:00
    对于你描述的场景,二者没法证明有实质意义的区别。

    如果真的需要安全的随机数,我觉得你们需要的不是更随机的随机数,而是更可控的随机数。

    说白了,就是需要一个看起来是随机但实际上不是随机的随机数。
    passive
        7
    passive  
       2023-12-25 20:44:51 +08:00 via Android   1
    两个均匀分布相加或者嵌套不再是均匀分布,无数个均匀分布相加或者嵌套最后会变成正态分布
    way2create
        8
    way2create  
    OP
       2023-12-25 20:47:27 +08:00
    @maggch97 这个有做的 而且实际上并不是单纯 8 位 还有别的 这里省略了 就是好奇问的问题
    way2create
        9
    way2create  
    OP
       2023-12-25 20:49:02 +08:00
    @sadfQED2 谢谢,你说的很有道理,变成 00000001 从我给的代码来看也是个问题,不过还好实际的场景不在乎这个,我这里只是用例子来表达我要问的问题,是我的疏忽。
    penzi
        10
    penzi  
       2023-12-25 20:51:42 +08:00
    @way2create 做了兜底就别管你同事说啥了。没有什么恶意,但是我不觉得写 PHP 的对随机数生成有什么深入的理解
    way2create
        11
    way2create  
    OP
       2023-12-25 20:54:35 +08:00
    @way2create 我是不深入 他我不知道 就是因为这事产生的疑问 水平有限所以来问问
    geelaw
        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 的列表,并存下来),然后每次需要的时候取用。
    kingjpa
        13
    kingjpa  
       2023-12-25 21:08:55 +08:00
    time().mt_rand(1111,9999)
    clemente
        14
    clemente  
       2023-12-25 21:12:53 +08:00
    mt_rand(10000000, 99999999) 已经足够随机,并且使用 str_shuffle 不会增加其随机性。

    mt_rand 函数使用 Mersenne Twister 算法生成伪随机数,而且已经足够随机,无需额外的操作。在你的用例中,使用 str_shuffle 并不会增加数字的随机性,因为它是为字符串设计的。
    way2create
        15
    way2create  
    OP
       2023-12-25 21:35:31 +08:00
    @geelaw 谢谢你的回答 因为我们项目数据量实在是太小了 所以我只做了简单的随机数+万一重复的错误处理 感谢你提的方案 不过其实代码跟实际需求有点不一样 我省略了主要是想问标题本身的
    way2create
        16
    way2create  
    OP
       2023-12-25 21:50:41 +08:00
    @clemente 谢谢 我 Google 的时候就是有看到类似的答案 想着来 V2 确认一下的
    newaccount
        17
    newaccount  
       2023-12-26 09:17:39 +08:00
    随机数要保证结果的均匀分布,str_shuffle 这东西不像是做这个用的,理论上讲,加了之后是负优化

    随机数算法的测试,印象中是在 《 delphi 算法与数据结构》这本书上看到过,通过撒点图形看是否存在不正常的 pattern ?年头有点久,记不太清了

    算法是通用的,你看看别的语言实现的随机数算法评估方法
    CodeCodeStudy
        18
    CodeCodeStudy  
       2023-12-26 09:18:20 +08:00
    用 random_int 这个函数

    random_int(int $min, int $max): int
    way2create
        19
    way2create  
    OP
       2023-12-26 09:25:13 +08:00
    @newaccount 好的谢谢我去搜搜看 其实我也就是单纯好奇他加这个函数 除了表面上的打乱 究竟是真的优化还是多此一举负优化
    afeiche
        20
    afeiche  
       2023-12-26 09:28:05 +08:00
    我感觉需求和实现不匹配,需求是要唯一,而实现是生成随机数,但是用 rand 应该是会有一定的重复概率,不如按顺序生成靠谱一点
    way2create
        21
    way2create  
    OP
       2023-12-26 09:47:25 +08:00
    @CodeCodeStudy 我的工作场景经常都是 mt_rand 就够了 你不提这个函数 用的少我都给忘了 你一提我去看了下才想起之前有用过相关的 random_bytes 与其说需要更好的 不如说我是很好奇他加个打乱函数是否真的有优化还是负优化
    way2create
        22
    way2create  
    OP
       2023-12-26 09:52:34 +08:00
    @afeiche 谢谢 是我描述的不太清楚 有个要求是不要有规律的所以不能顺序 而且因为数量太小了 就如我说的可能一年都没 1000 个 其次这个是后台用的要求低一些 也做了错误处理了 不过肯定有非常多更好的方法 我只是结合场景图省事了
    dode
        23
    dode  
       2023-12-26 10:32:31 +08:00
    zhoushiyi
        24
    zhoushiyi  
       2023-12-26 17:46:33 +08:00
    mt_rand 取得值最终都是 10000000-99999999 区间的有序数字,只是先用那个数字的顺序不一样吧,是不是意味着记录数大于 99999999 条后,就不会再有随机数可用了,感觉 shuffule 应该起到了 0 开头的作用,随机数可用的范围多了,例如: 随机数 11223300 -> 00223311 rand 是不会产生 00223311 这样的随机数的
    CodeCodeStudy
        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 的底层都是相似的,差别可能就是算法不一样
    way2create
        26
    way2create  
    OP
       2023-12-26 18:59:20 +08:00
    @CodeCodeStudy 说到这个 我看过源码早期版本的 rnd_idx = algo->range(status, 0, n_left);这行甚至直接就是调用的 php_rand() 而更旧版本的 php_rand() 好像还不如 php_mt_rand() 有些版本 php_rand 就等同于 php_mt_rand()了 但我不太懂 C
    CodeCodeStudy
        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().
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2492 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 10:44 PVG 18:44 LAX 03:44 JFK 06:44
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86