我有一个完全填充的值数组,我想随意删除此数组中的元素,并向远端移除更多元素。
例如,给定输入(其中a。表示填充的索引)
............................................
我想要像
这样的东西....... . ... .. . . .. . .
我的第一个想法是计算元素,然后遍历数组,在当前索引和数组总大小之间的某处生成一个随机数,例如:
if ( mt_rand( 0, $total ) > $total - $current_index )
//remove this element
然而,因为这需要在循环每次循环时产生一个随机数,所以它变得非常艰巨。
有更好的方法吗?
答案 0 :(得分:2)
一种简单的方法是为每个条目翻转加权硬币,硬币翻转更加加权到最后。例如,如果数组的大小为n,则对于每个条目,您可以选择从0
到n-1
的随机数,并且只有在索引小于或等于随机数时才保留该值。 (也就是说,保持每个条目的概率1 - index/total
。)这有一个很好的优势,如果你打算压缩你的数组,你正在使用一个足够好但有效的随机数发生器(可能是对一个nonce的简单整数哈希),它对内存访问来说会相当快。
另一方面,如果您只是消除了一些项目并且没有重新排列数组,那么您可以使用某种加权随机数生成器,它通常会选择朝向索引末尾的数字。例如,如果你有一个随机数生成器,它生成值为[0,1]的浮点数(闭合或开放边界无关紧要),可以考虑获得这样一个随机浮点r
并将其平方。这往往更喜欢较低的价值。你可以通过翻转来解决这个问题:1-r^2
。当然,您需要将其设置在0
到n - 1
的索引范围内,因此请floor(n * (1 - r^2))
并将n
向下舍入到n-1
。
这两种技术实际上存在无限多种变化。
答案 1 :(得分:2)
这可能不是最好/最有效的方法,但这是我能提出的最好的it does work。
N.B。键盘示例需要很长时间才能执行,但这是因为我添加到最后的漂亮打印循环,所以你可以看到它明显有效。如果删除内部循环,执行时间将降至可接受的水平。
<?php
$array = range(0, 99);
for ($i = 0, $count = count($array); $i < $count; $i++) {
// Get array keys
$keys = array_keys($array);
// Get a random number between 0 and count($keys) - 1
$rand = mt_rand(0, count($keys) - 1);
// Cut $rand elements off the beginning of the keys
$keys = array_slice($keys, $rand);
// Unset a random key from the remaining keys
unset($array[$keys[array_rand($keys)]]);
}
答案 2 :(得分:1)
此方法不是随机的 - 它可以通过定义函数及其反函数来实现。具有不同常系数的不同函数将具有不同的分布特征。
结果非常像模式,就像将连续函数映射到像数组这样的离散结构时所期望的那样。
这是使用二次函数的示例。你可以尝试改变常数。
演示:http://codepad.org/ojU3s9xM
#as in y = x^2 / 7;
function y($x) {
return $x * $x / 7;
}
function x($y) {
return 7 * sqrt($y);
}
$theArray = range(0,100);
$size = count($theArray);
//use func inverse to find the max value we can input to $y() without going out of array bounds
$maximumX = x($size);
for ($i=0; $i<$maximumX; $i++) {
$index = (int) y($i);
//unset the index if it still exists, else, the next greatest index
while (!isset($theArray[$index]) && $index < $size) {
$index++;
}
unset($theArray[$index]);
}
for ($i=0; $i<$size; $i++) {
printf("[%-3s]", isset($theArray[$i]) ? $theArray[$i] : '');
}