变化大小的排列

时间:2010-06-11 01:22:52

标签: php algorithm permutation memory-optimization

我正在尝试用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同时在其内存中保留数万亿个排列。

4 个答案:

答案 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

也可以轻松修改此算法以使用重复项。