【问题标题】:Shuffling array based on seed to get always the same result?基于种子的洗牌数组总是得到相同的结果?
【发布时间】:2011-12-07 23:34:28
【问题描述】:

我需要根据种子编号对数组进行随机播放,以便在需要时获得相同的随机播放。

例如:

1. print_r( shuffleIt( $array, 2 ) );
2. print_r( shuffleIt( $array, 6 ) );
3. print_r( shuffleIt( $array, 2 ) );
  1. 和 3. 将显示相同的随机数组,但与 2 不同。

我在谷歌上发现了这个函数:

function entropy( $array, $sort_seed ) {
    mt_srand( $sort_seed );
    $order = array_map( create_function( '$val', 'return mt_rand( );' ), range( 1, count( $array ) ) );
    array_multisort( $order, $array );
    return $array;
}

使用 php-cli 在我的电脑上运行良好,我总是为我使用的每个不同的 sort_seed 获得相同的数组,但是当我将它上传到服务器时,即使我使用相同的 sort_seed,我每次都会得到不同的数组.

如何在使用相同的 sort_seed 时始终获得相同的打乱数组?

顺便说一句。我需要保留键或对多维数组进行排序,以便将键存储在那里。

【问题讨论】:

  • 所以您使用mt_rand + mt_srand 创建一个在$seed 相同的情况下始终相同的订单?
  • @hakre 这个顺序总是在我的电脑上是一样的,不是在服务器上,我没有创建函数。
  • 我了解您对该功能的问题。但是你在看什么呢?每台计算机都有自己的随机数生成器,因此在计算机之间有所不同。你描述的很正常。但是你在找什么?
  • @hakre 是的,所以我正在寻找一个替代方案,这样我就可以在任何计算机上根据数字获得相同的洗牌数组。

标签: php arrays multidimensional-array shuffle entropy


【解决方案1】:

您是否托管了 Suhosin 安全扩展?显然,它有几个指令可以防止脚本设置种子:

suhosin.srand.ignore = On
suhosin.mt_srand.ignore = On

进一步阅读:

这里有一个快速的方法来测试这是否是问题:

<?php

mt_srand(33);
var_dump(mt_rand(1, 10000));

mt_srand(33);
var_dump(mt_rand(1, 10000));

mt_srand(33);
var_dump(mt_rand(1, 10000));

【讨论】:

  • 我知道 mt_rand 不会完成这项工作,但是我可以使用什么来根据键号对数组进行洗牌并在任何电脑上获得相同的结果?
  • 看来我误解了你的问题。 “即使我使用相同的 sort_seed,我每次都得到不同的数组”这句话暗示了完全不同的事情:(
【解决方案2】:

如果您需要在任何 PC 上使用它,您需要一个在计算机上工作相同的随机数生成器。我从Random Number Generation Wikipedia page 中查找了一个可能适合您的伪随机数生成器,并将其放入一个类中,以便您可以播种。

我不知道你需要它,但这可能适合你的需要。它独立于系统配置:

function shuffleIt($array, $seed)
{
    $mwc = new mwc($seed);
    $order = array();
    $count = count($array);
    while($count--)
        $order[] = $mwc->random()
    ;

    array_multisort($order, $array);
    return $array;
}

/**
 * Multiply-with-carry RNG
 * 
 * method invented by George Marsaglia
 */
class mwc
{
    private static $def_m_w = 1712; /* must not be zero */
    private static $def_m_z = 23;   /* must not be zero */
    private $m_w, $m_z;
    public function __construct($seed = NULL)
    {
        $this->m_w = self::$def_m_w;
        $this->m_z = self::$def_m_z;
        if (NULL !== $seed)
            $this->seed($seed);
    }
    public function seed($seed)
    {
        $seed = (int) $seed;
        if (!$seed) throw new InvalidArgumentException('Must not be zero.');
        $this->m_z = $seed;
        $this->random();
    }
    public function random()
    {
        $this->m_z = 36969 * ($this->m_z & 65535) + ($this->m_z >> 16);
        $this->m_w = 18000 * ($this->m_w & 65535) + ($this->m_w >> 16);
        return ($this->m_z << 16) + $this->m_w;  /* 32-bit result */
    }
}

注意:这可能在 32/64 位系统之间表现不同,尤其是 PHP 在 Windows 和 unix 之间的整数和溢出方面有所不同。您可能希望 PHP 中 32 位整数的有符号最小值偏移,而不是现在的 0,以便将实现切换到 gmp 或仅将大小减小一位。


荷兰 ekke 报告的 32 位使用示例

$shuffle = new GeorgeShuffle();
$seed    = $shuffle->seed();
$a       = array('A', 'B', 'C', 'D', 'E', 'F', 'G');
$shuffle->reOrder($a);
var_dump($a);
$shuffle->seed($seed);
$shuffle->reOrder($a);
var_dump($a);

/**
 * Array shuffle class using
 * the multiply-with-carry method
 * invented by George Marsaglia
 */
class GeorgeShuffle
{

    private static $def_m_w = 1959; /* must not be zero */
    private static $def_m_z = 2006; /* must not be zero */
    private $m_w, $m_z;
    const maxint = 2147483647;

    public function __construct($seed = null)
    {
        $this->m_w = self::$def_m_w;
        $this->m_z = self::$def_m_z;
        if ($seed) $this->seed($seed);
    }

    public function reOrder(&$array, $seed = null)
    {
        if (!empty($seed)) $this->seed($seed);
        $a = array();
        for ($i = 0, $j = count($array); $i < $j; $i++) {
            $a[$i] = $this->random();
        }
        array_multisort($a, $array);
        //- to return a copy, remove the &
        return $array;
    }

    public function seed($seed = false)
    {
        if (is_string($seed)) $seed = hexdec($seed);
        if (empty($seed)) $seed = round(mt_rand(1, self::maxint));
        $this->m_z = $seed;
        $this->random();
        //- return the seed used in hex (8 chars) for reproducing
        return str_pad(dechex($seed), 8, '0', STR_PAD_LEFT);
    }

    public function random()
    {
        $this->m_z = 36969 * (($this->m_z And 65535) + ($this->m_z >> 16));
        $this->m_w = 18000 * (($this->m_w And 65535) + ($this->m_w >> 16));
        return ($this->m_z << 16) + $this->m_w; /* 32-bit signed result */
    }
}

【讨论】:

  • 哇哦,太棒了。谢谢老兄。
猜你喜欢
  • 2017-06-24
  • 2022-01-26
  • 2022-01-14
  • 1970-01-01
  • 1970-01-01
  • 2014-09-12
  • 1970-01-01
  • 1970-01-01
  • 2023-02-08
相关资源
最近更新 更多