V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
way2create
V2EX  ›  程序员

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

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

    场景

    单机的小项目,后台内部用,不需要开放,要求就是给数据生成一个 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
    你不是应该要你同事给出这样更随机的证明吗
    infun
        2
    infun  
       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 每个数出现概率不一样的话,那这样打乱一下有意义
    maggch97
        4
    maggch97  
       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 从我给的代码来看也是个问题,不过还好实际的场景不在乎这个,我这里只是用例子来表达我要问的问题,是我的疏忽。
    maggch97
        10
    maggch97  
       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   ·   实用小工具   ·   956 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 22:43 · PVG 06:43 · LAX 14:43 · JFK 17:43
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.