【问题标题】:Difference between mt_rand() and rand()mt_rand() 和 rand() 的区别
【发布时间】:2015-04-29 21:42:03
【问题描述】:

使用mt_rand($min, $max)rand($min, $max)在速度上有什么区别?

【问题讨论】:

    标签: php performance optimization random


    【解决方案1】:

    mt_rand() 上的 PHP 手册指出:

    生成随机数的速度比 libc rand() 提供的平均速度快四倍。

    【讨论】:

    • 那么为什么 rand() 存在?
    • @Marveylabs rand() 是在 PHP 中使用了很长时间的标准语言结构(可能从 C 语言移植而来)。据我所知,mt_rand() 有点新。这就是为什么 mt_rand() 被手册称为 better rand :)
    • @Forien:你忘记了 rand 的问题,而 mt_rand => 例如,可以从输出中轻松推断出种子
    • @Forien:请再看一遍文档,不是mt_rand函数说的快,是它使用的算法平均 libc rand 函数。这并不意味着这种速度差异将转化为 PHP 的性能差异......它只是说明 PRNG 将更快地生成数字,仅此而已,仅此而已
    • @EliasVanOotegem 是的,这意味着系统“更快”,而不是 PHP。但是如果你没有看到 PHP 速度的差异,但是有更好的分布并且对服务器系统的压力更小,为什么不使用它呢?
    【解决方案2】:


    以下是两者的速度差异:-
    mt_rand($min, $max)rand($min, $max)

    原因是那,rand($min, $max) 使用 libc 随机数生成器,而 mt_rand($min, $max) 使用 Mersenne Twister,速度快了四倍。

    希望它能解决您的疑问。
    谢谢。

    【讨论】:

    • 如果您引用文档,请准确地说:它是 Mersenne Twister 算法,它产生随机数的速度比 平均 libc rand 函数快四倍。这并不意味着mt_randrand 快四倍。无论如何都不是。速度甚至不是两者之间最大的区别:看我的答案
    • 你们中的王者,但我认为您已经详细说明了(例如)我以直接方式从文档中引用的关于该问题的内容。感谢您的建议。
    • 您的回答表明 mt_randrand4 倍。它不是。这是一个错误的说法:生成数字的内部机制更快,但这并不意味着函数变得更快。乔治的回答很好地说明了这一点。
    • 非常正确,但我所说的也是正确的。与 rand() 函数相比,它生成更好的随机值,但同时速度快四倍。我正在回答与被问到的问题有关的问题。请参考w3schools.com/php/func_math_mt_rand.asp
    • 现在看:我同意你所说的算法更快,并且值的随机性更好。但是您说mt_rand(PHP 函数)比rand(PHP 函数)快。它只是不是。它生成数字的方式更快,但就您的 PHP 代码而言,它并没有快 4 倍。 而且你从来没有提到mt_rand的更好分布,这实际上是这里两个函数之间最大的区别
    【解决方案3】:

    它们的速度似乎相等:

    function timeit($times, $func) {
        $t = microtime(1);
        while($times--) $func();
        return microtime(1) - $t;
    }
    
    echo PHP_OS, " ", phpversion(), "\n";
    echo timeit(100000, function() {    rand(0,1000); }), "\n";
    echo timeit(100000, function() { mt_rand(0,1000); }), "\n";
    

    OSX Mavericks 和 VirtualBox 的 Ubuntu 11 的结果:

    Darwin 5.5.19
    0.038038969039917
    0.033117055892944
    
    Linux 5.3.6-13ubuntu3.10
    0.031459093093872
    0.031935214996338
    

    如果这些措施是正确的,则其他地方提到的手动注释应被视为错误/过时。

    【讨论】:

    • 声称mt_rand 快四倍的说法是一个可怕的笼统说法,确实很可能已经过时了。 "average" libc rand 可能已经改变了一百次,因为这句话被添加到 PHP 文档中......此外,mt_rand 出于速度原因没有添加,它被添加了因为旧的rand PRNG 的固有缺陷。速度提升从来不是mt_rand 的主要原因,这似乎是每个人都忘记的。
    • @EliasVanOotegem:实际上,整个辩论对我来说似乎无足轻重。对于“弱”随机性(“洗牌”),使用 rand 或 mt_ 并不重要,而在严肃的应用程序(密码学)中,您都不应该使用。
    • 你是对的。这是一场毫无意义的辩论。我只是被答案迷住了,都没有提到mt_rand 的更好分布,只关注速度,但是,那是 OP 的问题。那个,以及一些用户的态度让我无所适从......不过我和你在一起,OP接受了更好的答案,所以我会留下这个
    【解决方案4】:

    更新

    由于 PHP 7.1 mt_rand 已完全取代 rand,并且 rand 已成为 mt_rand 的别名。下面的答案主要针对老版本这两个函数的区别,以及引入mt_rand的原因。


    速度不是引入mt_rand 的原因!

    rand 函数早于mt_rand 就存在,但它存在严重缺陷。 PRNG 必须获得一些熵,即它生成随机数序列的数字。如果您打印出由rand() 生成的十个数字的列表,如下所示:

    for ($i=0;$i<10;++$i)
        echo rand(), PHP_EOL;
    

    输出可用于计算rand 种子是什么,并使用它,您可以预测下一个随机数。有一些工具可以做到这一点,所以谷歌一下并测试它。

    rand 的随机数 as demonstrated here 相对快速地显示模式也存在问题。 mt_rand 的问题似乎也解决了很多。

    mt_rand 使用更好的随机化算法 (Mersenne Twist),它需要在确定种子之前知道更多随机数并且更快。 这并不意味着根据定义,mt_randrand,这只意味着生成数字的方式更快,并且似乎正如此处的其他答案所证明的那样,对函数的性能没有真正的影响。
    不管怎样,看看the mt_srandthe srand docs。我相信它们会包含更多信息

    如果mt_rand 的算法可以提高性能,那么这对您来说是件好事,但这是一个巧合。 TL;TR:

    引入mt_rand 是为了解决rand 中存在的问题!

    【讨论】:

    • @PEMapModder 答案是大约 2 年前写的,在 rand 成为别名之前。 WRT 内容:它的主要重点是解释 为什么 mt_rand 最初被引入(这仍然是相关的)。为了完整起见,我将添加一个更新,说明 rand 现在是别名。除此之外,提供的信息仍然是正确的 IMO
    • 请注意,mt_rand() 也有缺陷,可以使用untwister 恢复种子,并可用于计算未来(和过去)值。在 PHP 7 中,出于安全考虑,请使用 random_int()random_bytes()
    【解决方案5】:

    更新(PHP 7.1):

    rand() and srand() have now been made aliases to mt_rand() and mt_srand(), respectively. 这意味着以下函数的输出发生了变化:rand()shuffle()str_shuffle()array_rand()

    这意味着从 7.1 版开始,它们之间没有实际区别,因为rand calls mt_rand internally


    PHP 7.1 之前:

    如果不用于安全目的,使用rand() 并不是一个坏习惯,我通常使用rand()(习惯?)。

    如果您需要大量随机数,则需要mt_rand 而不是randmt_rand 的周期为 219937 - 1,远远优于 rand (232)。看看this article,了解使用randmt_rand 生成图形模式。

    Periodicityentropy 是使用 mt_rand() 而不是 rand() 的唯一原因,而不是提高安全性或速度。

    从数学上讲,mt_randrand 有更多的entropy 和更大的periodicity(219937-1 与 232)。

    如果您需要一些随机数并且安全性不成问题,rand 将完成这项工作(获取一个随机数来决定触发清理过程)。


    测试速度提升

    实际上这两个函数的速度差别不大(可能是因为 PHP⇔C 包装器开销?)。

    PHP测试代码:

    <?php
    for ($c = 0; $c < 3; $c++) {
      $start = microtime(true);
      $sum = 0.0;
      for ($i = 0; $i < 100000000; $i++) {
        $sum += rand();
      }
      printf('[rand %d] Time: %.3f s%s', $c, microtime(true) - $start, PHP_EOL);
    }
    for ($c = 0; $c < 3; $c++) {
      $start = microtime(true);
      $sum = 0.0;
      for ($i = 0; $i < 100000000; $i++) {
        $sum += mt_rand();
      }
      printf('[mt_rand %d] Time: %.3f s%s', $c, microtime(true) - $start, PHP_EOL);
    }
    

    PHP 7.0.19 中的测试:

    $ php timing.php
    [rand 0] Time: 4.658 s
    [rand 1] Time: 4.664 s
    [rand 2] Time: 4.654 s
    [mt_rand 0] Time: 4.267 s
    [mt_rand 1] Time: 4.255 s
    [mt_rand 2] Time: 4.261 s
    

    在 PHP 5.4.45 中的测试(较慢的机器):

    $ php timing.php
    [rand 0] Time: 10.862 s
    [rand 1] Time: 10.889 s
    [rand 2] Time: 10.615 s
    [mt_rand 0] Time: 10.948 s
    [mt_rand 1] Time: 9.883 s
    [mt_rand 2] Time: 10.190 s
    

    只有 6-9%,而不是声称的 400%。


    出于安全目的使用

    但是如果您的应用程序因为安全问题需要大量熵,您将需要一种更安全的方法,而openssl_random_pseudo_bytes() 可能是最好的解决方案,它的工作是否有效(好得多但速度较慢?我们需要安全而不是速度?) relying on openssl related issues.

    rand() nor mt_rand() 都不够安全

    注意此函数不会生成加密安全 值,不应用于加密目的。如果你需要 一个加密安全值,考虑使用random_int()random_bytes(),或openssl_random_pseudo_bytes()

    有像random_compat这样的PHP扩展,但如果没有必要,我不建议使用它们。

    【讨论】:

    • 我认为如果将更新(PHP 7.1)放在答案的开头,这个答案会更好。
    • 感谢您的建议!我会在家编辑答案。
    【解决方案6】:

    从 PHP 7.1 开始,完全没有区别。 rand() 现在是 mt_rand() 的别名。

    http://php.net/manual/en/migration71.incompatible.php#migration71.incompatible.rand-srand-aliases

    更多详情:https://wiki.php.net/rfc/rng_fixes

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-04-07
      • 2015-08-31
      • 1970-01-01
      • 2014-10-28
      • 1970-01-01
      • 2013-09-14
      • 2013-01-30
      • 2015-07-24
      相关资源
      最近更新 更多