我正在尝试用PHP编写一个函数来获取所有可能大小的所有排列。我认为一个例子是开始的最佳方式:
$my_array = array(1,1,2,3);
不同大小的可能排列:
1 1 // * See Note 2 3 1,1 1,2 1,3 // And so forth, for all the sets of size 2 1,1,2 1,1,3 1,2,1 // And so forth, for all the sets of size 3 1,1,2,3 1,1,3,2 // And so forth, for all the sets of size 4
注意:我不在乎是否有重复。出于此示例的目的,已省略所有未来的重复项。
到目前为止,我在PHP中的所有内容:
function getPermutations($my_array){
$permutation_length = 1;
$keep_going = true;
while($keep_going){
while($there_are_still_permutations_with_this_length){
// Generate the next permutation and return it into an array
// Of course, the actual important part of the code is what I'm having trouble with.
}
$permutation_length++;
if($permutation_length>count($my_array)){
$keep_going = false;
}
else{
$keep_going = true;
}
}
return $return_array;
}
我能想到的最接近的事情就是改组数组,选择前n个元素,看看它是否已经存在于结果数组中,如果不存在,则将其添加进去,然后在数学上不再存在可能的排列时停止这个长度。但它很丑陋且资源效率低下。
任何伪代码算法都将非常感激。
<小时/> 此外,对于超级(无价值)奖励积分,有没有办法只使用该功能获得1个排列,但是为了使它不必重新计算所有先前的排列以获得下一个?
例如,我传给它一个参数3,这意味着它已经完成了3个排列,它只生成了4号而没有重做前3个? (传递参数不是必需的,它可以跟踪全局或静态)。
我问这个的原因是因为随着阵列的增长,可能的组合数量也会增加。可以说,只有十几个元素的一个小数据集迅速增长到数万亿个可能的组合中,我不想让PHP同时在其内存中保留数万亿个排列。
答案 0 :(得分:2)
抱歉没有php代码,但我可以给你一个算法。
可以使用少量内存完成,因为你不关心欺骗,代码也很简单。
首先:生成所有可能的子集。
如果将子集视为位向量,则可以看到与集合和二进制数字的对应关系为1-1。
因此,如果你的数组有12个元素,你将有2 ^ 12个子集(包括空集)。
因此,为了生成一个子集,从0开始并继续递增直到达到2 ^ 12。在每个阶段,您都会读取数字中的设置位,以从数组中获取相应的子集。
获得一个子集后,您现在可以完成其排列。
下一个排列(数组索引,而不是元素本身)可以按字典顺序生成,如下所示:http://www.de-brauwer.be/wiki/wikka.php?wakka=Permutations,可以用最少的内存完成。
你应该能够将这两者结合起来,给你自己一个next_permutation函数。您可以传入一个包含12个元素的数组,而不是传入数字,这些元素包含先前的排列,可能还有一些更多的信息(小内存),无论您是否需要转到下一个子集等。
您实际上应该能够找到使用最少内存的非常快速的算法,提供next_permutation类型功能并且不生成欺骗:在Web上搜索多集排列/组合生成。
希望有所帮助。祝你好运!
答案 1 :(得分:1)
我提出的最好的功能集是一些用户在php.net上的shuffle函数的注释中提供的函数。这是link它工作得非常好。
希望它有用。
答案 2 :(得分:0)
问题似乎是试图为每个排列提供索引并且具有恒定的访问时间。我想不出一个恒定时间算法,但也许你可以改进这个算法。该算法的时间复杂度为O(n),其中n是集合的长度。空间复杂度应该可以简化为O(1)。
假设我们的集合是1,1,2,3,我们想要第10个排列。另外,请注意,我们将集合中的每个元素从0到3编制索引。按顺序排序,这意味着首先是单个元素排列,然后是两个元素,依此类推。我们将从数字10中减去,直到我们完全确定第10个排列。
首先是单元素排列。其中有4个,所以我们可以将其视为从10减去四次。我们剩下6个,所以显然我们需要开始考虑两个元素的排列。其中有12个,我们可以将其视为从6减去3到4次。我们发现第二次减去3,我们留下0.这意味着我们的排列索引必须是2(因为我们减去3两次)和0,因为0是余数。因此,我们的排列必须是2,1。
分区和模数可能会对你有帮助。
如果我们正在寻找第12个排列,我们将遇到剩余2的情况。根据您的期望行为,排列2,2可能无效。然而,解决这个问题非常简单,因为我们可以轻易地检测到索引2和2(不要与元素混淆)是相同的,所以第二个应该被碰到3.因此,第12个排列可以很简单计算为2,3。
现在最大的困惑是索引和元素值碰巧匹配。我希望我的算法解释不会因此而混淆。如果是,我将使用除您的示例之外的其他集合并重新编写内容。
答案 3 :(得分:0)
输入:置换索引k,索引集S。
伪代码:
L = {S_1}
for i = 2 to |S| do
Insert S_i before L_{k % i}
k <- k / i
loop
return L
也可以轻松修改此算法以使用重复项。