【发布时间】:2013-04-04 09:36:32
【问题描述】:
我想比较数组的每个元素。
$char=array();
for($i=0;$i<=10;$i++)
{
$char[$i]=rand(0,35);
}
我想比较 $char 数组的每个元素。如果有任何重复的值比它应该改变值并选择另一个在数组中应该是唯一的随机值..
【问题讨论】:
我想比较数组的每个元素。
$char=array();
for($i=0;$i<=10;$i++)
{
$char[$i]=rand(0,35);
}
我想比较 $char 数组的每个元素。如果有任何重复的值比它应该改变值并选择另一个在数组中应该是唯一的随机值..
【问题讨论】:
在这个特定示例中,可能值的范围非常小,最好采用另一种方式:
$allPossible = range(0, 35);
shuffle($allPossible);
// Guaranteed exactly 10 unique numbers in the range [0, 35]
$char = array_slice($allPossible, 0, 10);
或使用 array_rand 的等效版本:
$allPossible = range(0, 35);
$char = array_rand(array_flip($allPossible), 10);
如果值的范围更大,那么这种方法将非常浪费,您应该在每次迭代时检查唯一性:
$char = array();
for ($i = 0; $i < 10; ++$i) {
$value = null;
// Try random numbers until you find one that does not already exist
while($value === null || in_array($value, $char)) {
$value = rand(0, 35);
}
$char[] = $value;
}
但是,这是一种概率方法,可能需要很长时间才能完成,具体取决于 rand 的输出恰好是什么(如果您想要的值的数量接近数字,这将特别糟糕所有可能的值)。
此外,如果您要选择的值的数量很大(比如说超过 50 个左右),那么in_array 可能会成为瓶颈。在这种情况下,使用数组键而不是值来检查唯一性应该更快,因为搜索键的存在是常数时间而不是线性时间:
$char = array();
for ($i = 0; $i < 100; ++$i) {
$value = null;
// Try random numbers until you find one that does not already exist
while($value === null || array_key_exists($char, $value)) {
$value = rand(0, 1000);
}
$char[$value] = $value;
}
$char = array_values($char); // reindex keys to start from 0
【讨论】:
要将任何重复值更改为随机值,您应该循环遍历数组两次:
$cont= 0;
foreach($char as $c){
foreach($char as $d){
if($c == $d){
//updating the value
$char[$cont] = rand(0,35);
}
}
$cont++;
}
但我不知道随机值是否也可以重复。那样的话就不会这么简单了。
【讨论】:
我从PHP Manual page for rand()获取了这段代码
<?php
function uniqueRand($n, $min = 0, $max = null)
{
if($max === null)
$max = getrandmax();
$array = range($min, $max);
$return = array();
$keys = array_rand($array, $n);
foreach($keys as $key)
$return[] = $array[$key];
return $return;
}
?>
这个函数生成一个大小为 $n 的数组,你可以像 rand 一样设置最小值和最大值。
所以你可以像这样使用它
uniqueRand(10, 0, 35);
【讨论】:
getrandmax() 作为默认值。玩得开心参观地狱厨房。而是需要指定它。另见shuffle()。
首先在$char 数组上使用array_count_values()。
之后,您可以循环所有大于 1 的条目并将它们随机化。您必须不断检查,直到所有计数都为 1。因为即使是随机的也可能会再次复制。
【讨论】:
我建议使用两个选项来制作随机数组:
<?php
$questions = array(1, 2, 3, 4, 5, ..., 34, 35);
$questions = shuffle($questions);
?>
然后选择前 10 个元素。
【讨论】:
您可以尝试使用此代码替换任何重复的值。
for ($i = 0; $i < count($char); $i++) {
for ($n = 0; $n < count($char); $n++) {
if($char[$i] == $char[$n]){
$char[$i] = rand(0,35);
}
}
}
【讨论】:
函数array_unique() 从数组中获取所有唯一值,以它们的第一次出现为键。
array_diff() 函数允许从一个数组中删除另一个数组中的值。
根据您需要(或不拥有)键控结果的方式或保留键的顺序,您需要执行多个步骤。一般来说,它的工作原理与我在以下段落中概述的一样(使用 PHP 代码示例):
在一个数组中,您有 N 元素,其中 Nu 是唯一的。
$N = array(...);
$Nu = array_unique($N);
然后替换重复项所需的随机元素r 的数量是N 的计数减去Nu 的计数。由于N 的计数通常是一个有用的值,所以我也将其分配给nc:
$nc = count($N);
$r = $nc - count($Nu);
这使得r 成为一个从0 到count(N) - 1 的整数:
0 : no duplicate values / all values are unique
1 : one duplicate value / all but one value are unique
...
count(N) - 1 : all duplicate values / no unique value
因此,如果您需要零随机值 ($r === 0),则输入 $N 就是结果。这个边界条件是第二个最简单的结果(第一个简单的结果是一个没有成员的输入数组)。
对于所有其他情况,您需要 r 随机唯一值。在您的问题中,您从 0 写到 35。但这不可能是完整的故事。假设您的输入数组有 36 个重复值,从 0 到 35 范围内的每个数字都重复一次。再次将 0 到 35 范围内的随机数添加到数组将再次创建重复 - 保证。
相反,我已阅读您的问题,即您只是在寻找尚不属于输入数组的唯一值。
因此,您不仅需要 r 随机值 (Nr),而且到目前为止它们也不能是 N 或 Nu 的一部分。
要实现这一点,您只需要创建 count(N) 唯一值,请从这些值中删除唯一值 Nu,以确保不会重复 Nu 中的值。由于这是理论上的最大值,而不是所需的确切数字,因此该数组用于从以下位置获取恰好 r 元素的切片:
$Nr = array_slice(array_diff(range(0, $nc - 1), $Nu), 0, $r);
如果您还希望将这些新值添加到按range(0, $nc - 1) 排序的顺序,您可以执行以下操作:
shuffle($Nr);
这应该会将您在问题中似乎要求的随机性带回到答案中。
现在您可以使用原始数组 $Nu 的独特部分和 r $Nr 中的新值。合并这两个数组将为您提供一个忽略键 => 值关系的结果数组(该数组是重新索引):
array_merge($Nu, $Nr);
例如对于$N 的示例array(3, 4, 2, 1, 4, 0, 5, 0, 3, 5),它给出的结果是:
Array
(
[0] => 3
[1] => 4
[2] => 2
[3] => 1
[4] => 0
[5] => 5
[6] => 7
[7] => 9
[8] => 6
[9] => 8
)
如您所见,所有唯一值 (0-5) 都在开头,然后是新值 (6-9)。不保留原始密钥,例如值5的键原来是6,现在是5。
由于array_merge(),关系或键 => 值不会被保留,它会重新索引数字键。在 Nr 键中的唯一数字旁边也需要在数组中是唯一的。因此,对于添加到唯一现有数字的每个新数字,都需要使用一个密钥,该密钥是重复数字的密钥。为了获得所有重复数字的键,原始数组中的键集合被所有重复数字匹配的键集合减少(“唯一数组”中的键$Nu):
$Kr = array_keys(array_diff_assoc($N, $Nu));
现有结果$Nr 现在可以使用这些键进行键控。 PHP中设置数组所有键的函数是使用函数array_combine():
$Nr = array_combine($Kr, $Nr);
这允许使用 数组联合运算符 (+) 获得具有键 => 值关系的结果:
$Nu + $Nr;
例如,使用上一个示例中的$N,它给出的结果是:
Array
(
[0] => 3
[1] => 4
[2] => 2
[3] => 1
[5] => 0
[6] => 5
[4] => 8
[7] => 6
[8] => 9
[9] => 7
)
您现在可以看到,值 5 的键 6 以及值 0 的键 5 在原始数组中,现在也在输出中键 4 与前面的示例一样。
但是,由于现在已为原始值的第一次出现保留了键,因此顺序仍然发生了变化:首先是所有以前唯一的值,然后是所有新值。但是,您可能希望在适当的位置添加新值。为此,您需要获取新值的原始键的顺序。这可以通过按键映射顺序并使用array_multisort() 根据该顺序进行排序来完成。
因为这需要通过参数传递返回值,所以这需要我选择以字母 V 开头的附加临时变量:
// the original array defines the order of keys:
$orderMap = array_flip(array_keys($N));
// define the sort order for the result with keys preserved
$Vt = $Nu + $Nr;
$order = array();
foreach ($Vt as $key => $value) {
$order[] = $orderMap[$key];
}
然后排序完成(这里使用保留键):
// sort an array by the defined order, preserve keys
$Vk = array_keys($Vt);
array_multisort($order, $Vt, $Vk);
那么结果是:
array_combine($Vk, $Vt);
再次使用上面的示例值:
Array
(
[0] => 3
[1] => 4
[2] => 2
[3] => 1
[4] => 7
[5] => 0
[6] => 5
[7] => 8
[8] => 6
[9] => 9
)
这个示例输出很好地显示了键从 0 到 9 排序,因为它们在输入数组中很好。例如,与之前的输出相比,您可以看到第一个附加值 7(键控 4)位于第 5 位 - 与原始数组中键控 4 的值相同。键的顺序也得到了。
如果这是您努力争取的结果,您也可以通过迭代原始数组键来缩短此步骤的路径,如果这些键中的每一个都不是任何重复值的第一个值,您可以从中弹出而是新的值数组:
$result = array();
foreach ($N as $key => $value) {
$result[$key] = array_key_exists($key, $Nu) ? $Nu[$key] : array_pop($Nr);
}
再次使用示例数组值,结果(与之前的不同,因为$Nr 被打乱了:
Array
(
[0] => 3
[1] => 4
[2] => 2
[3] => 1
[4] => 7
[5] => 0
[6] => 5
[7] => 8
[8] => 9
[9] => 6
)
这到底可能是回答您问题的最简单方法。希望这可以帮助您回答问题。请记住以下几点:
array_unique() 在这里为您提供帮助。array_diff() 在这里为您提供帮助。就像这个例子:
// original array
$array = array(3, 4, 2, 1, 4, 0, 5, 0, 3, 5);
// obtain unique values (1.)
$unique = array_unique($array);
// obtain new unique values (2.)
$new = range(0, count($array) - 1);
$new = array_diff($new, $unique);
shuffle($new);
// process original array (3.)
foreach ($array as $key => &$value) {
if (array_key_exists($key, $unique)) {
continue;
}
$value = array_pop($new);
}
unset($value, $new);
// result in $array:
print_r($array);
然后(以shuffle($new) 为例)输出:
Array
(
[0] => 3
[1] => 4
[2] => 2
[3] => 1
[4] => 9
[5] => 0
[6] => 5
[7] => 8
[8] => 7
[9] => 6
)
【讨论】: