php 从数组中获取n个随机值并防止连续重复的值

lnlaulya  于 2023-04-04  发布在  PHP
关注(0)|答案(2)|浏览(144)

我想填充一个包含从输入数组中随机抽取的值的结果数组,但结果数组不能有两个相同的连续值。
附加规则:
1.值的输入数组将仅包含唯一值,并且将至少具有两个值,以确保可以填充所需的结果数组。
1.随机值的数量可以大于或小于输入数组的大小。
1.如果随机值的数量大于输入数组的大小,则结果数组不得要求使用输入中的所有值。换句话说,随机选择的值不得偏向均匀分布。
样品输入:

$array = ['one', 'two', 'three', 'four'];
$n = 10;

可能有效结果的非详尽列表:

  • ["three","one","three","one","two","one","four","one","three","four"]
  • ["four","three","two","one","two","four","one","three","two","one"]
  • ["two","four","three","one","two","one","four","two","three","one"]

这个问题的灵感来自于this deleted question,它很难用明确的规则和期望来问这个问题。

kxeu7u2r

kxeu7u2r1#

为了保证两个连续的值不相同,请跟踪前一个值(或其键),并将其作为当前迭代的可能随机值删除。将随机值推入结果数组,然后更新“previous”变量。
array_diff_key()可用于在调用array_rand()返回随机密钥之前排除特定密钥。
产品编号:(Demo)(Reduced alternative)(The ugly version

$lastIndex = -1;
$result = [];
for ($x = 0; $x < $n; ++$x) {
    $key = array_rand(array_diff_key($array, [$lastIndex => null]));
    $result[] = $array[$key];
    $lastIndex = $key;
}
echo json_encode($result);

或者,您可以使用unset()来排除先前的随机值,但重要的是不要修改原始数组,否则可能没有足够的值来填充结果数组。修改输入数组的副本即可。
代码:(Demo

$lastIndex = -1;
$result = [];
for ($x = 0; $x < $n; ++$x) {
    $copy = $array;
    unset($copy[$lastIndex]);
    $key = array_rand($copy);
    $result[] = $copy[$key];
    $lastIndex = $key;
}
echo json_encode($result);

一个暴力脚本可以猜测、检查和覆盖一个连续的重复值--这不会有有限的循环次数。随着输入数组的减小,连续重复的可能性会越来越大。
在循环中,无条件地将随机获取的值推入结果数组,然后只有在结果数组有一个单独的值或最后两个值不同时才有条件地递增计数器变量。

$result = [];
for ($x = 0; $x < $n; $x += (int) (!$x || $result[$x] !== $result[$x - 1])) {
    $result[$x] = $array[array_rand($array)];
}
echo json_encode($result);
4uqofj5v

4uqofj5v2#

暴力破解

可以使用while循环来比较输入$array中当前随机选择的元素与$result数组的最后一个元素(使用PHP end()函数)。
这个方法将导致对array_rand()的调用多于需要返回的元素数,但是没有必要创建原始数组的副本。
然而,额外循环的数量将连接到$array的长度,因此当$array具有:

  • 4个元素,我们可以预期大约33%的额外循环
  • 5个元素,我们可以预期大约25%的额外循环
  • 6个元素,我们可以预期大约20%的额外循环

当我们将$n的起始值增加到大约1000时,这一点可以清楚地看到。
随着$array的长度增加,额外循环的预期百分比将趋于0,尽管显然永远不会达到0。
编码:Demo

$array = ['one', 'two', 'three', 'four', 'five'];
    // $array = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'];

    $n = 1000;
    $result = [];
    $loop_counter = 0;

    while($n > 0) {
        $key = array_rand($array);
        if(end($result) != $array[$key]) {
            $result[] = $array[$key];
            $n--;
        }
        $loop_counter++;
    }

    echo PHP_EOL . json_encode($result);
    echo PHP_EOL . $loop_counter;
有限循环数方法

有几种方法可以确保another answer to this question中给出的循环数量有限,但它们涉及为每个循环创建$array的副本,这可能是也可能不是问题。
下面的方法也创建$array的副本,但不是在每个循环中创建$array的新副本,而是创建数组的Map并查找数组是否已经创建。
代码:Demo

$array = ['zero', 'one', 'two', 'three', 'four'];
    $n = 10;
    $result = [];
    $map_of_arrays = [];

    // We only select a random element once from the original array
    $key = array_rand($array);
    $result[] = $array[$key];
    $n--;

    while($n > 0) {
        if(!array_key_exists($key, $map_of_arrays)) {
            $map_of_arrays[$key] = $array;
            unset($map_of_arrays[$key][$key]);
        }
        $new_key = array_rand($map_of_arrays[$key]);
        $result[] = $map_of_arrays[$key][$new_key];
        $key = $new_key;
        $n--;
    }

    echo PHP_EOL . json_encode($result);

    // Check that the map of arrays has been created correctly
    // by comparing the index with the string representation
    echo PHP_EOL . PHP_EOL;
    var_dump($map_of_arrays);

相关问题