我正在阅读这里:http://en.wikipedia.org/wiki/Permutation#Algorithms_to_generate_permutations,但维基百科从来没有擅长解释,我不太理解其中的大部分内容。
答案:可以使用递归算法来生成列表的所有排列。首先,选择一个元素作为第一个元素,并将其与列表中的其他元素交换。然后,对剩余的元素进行递归处理,直到只剩下一个元素时,生成一种排列。重复此过程,每次选择一个新的第一个元素。最终,将得到所有可能的排列组合。
基本上,对于从左到右的每个项目,都会生成剩余项目的所有排列(并且加上当前元素)。这可以递归地完成(或者如果你喜欢痛苦的话也可以迭代),直到到达最后一个项目为止,在这一点上只有一种可能的顺序。
因此,对于列表[1,2,3,4],将生成以1开头的所有排列,然后是以2、3和4开头的所有排列。
这有效地将问题从查找四个项目列表的排列问题减少为三个项目列表。在缩小到2和1项列表后,所有排列都将被找到。
以下是使用3个彩色球显示排列过程的示例:
(来自https://en.wikipedia.org/wiki/Permutation#/media/File:Permutations_RGB.svg - https://commons.wikimedia.org/wiki/File:Permutations_RGB.svg)
这里是一个使用Python编写的算法,它通过在数组上原地操作来运行:
def permute(xs, low=0):
if low + 1 >= len(xs):
yield xs
else:
for p in permute(xs, low + 1):
yield p
for i in range(low + 1, len(xs)):
xs[low], xs[i] = xs[i], xs[low]
for p in permute(xs, low + 1):
yield p
xs[low], xs[i] = xs[i], xs[low]
for p in permute([1, 2, 3, 4]):
print p
您可以在这里自行尝试代码:http://repl.it/J9v
permute array
if array is of size 2
return first and second element as new array
return second and first element as new array
else
for each element in array
new subarray = array with excluded element
return element + permute subarray
这是我在C#中实现它的方法:
public IEnumerable<List<T>> Permutate<T>(List<T> input)
{
if (input.Count == 2) // this are permutations of array of size 2
{
yield return new List<T>(input);
yield return new List<T> {input[1], input[0]};
}
else
{
foreach(T elem in input) // going through array
{
var rlist = new List<T>(input); // creating subarray = array
rlist.Remove(elem); // removing element
foreach(List<T> retlist in Permutate(rlist))
{
retlist.Insert(0,elem); // inserting the element at pos 0
yield return retlist;
}
}
}
}
对于“词典序”的维基百科回答对我来说似乎非常清晰简洁。它引用了一个14世纪的算法起源!
我刚用Java快速实现了维基百科算法进行检查,没什么麻烦。但是你在问题中举的例子不是“列出所有排列”,而是“所有排列的列表”,所以维基百科对你没有太大帮助。你需要一种可以可行地构建排列列表的语言。相信我,长度为几十亿的列表通常无法在命令式语言中处理。你真正想要的是一种非严格的函数式编程语言,其中列表是第一类对象,可以轻松地从中获取内容,而不会使计算机接近宇宙热寂。
这很容易。在标准的Haskell或任何现代的函数式编程语言中:
-- perms of a list
perms :: [a] -> [ [a] ]
perms (a:as) = [bs ++ a:cs | perm <- perms as, (bs,cs) <- splits perm]
perms [] = [ [] ]
和
-- ways of splitting a list into two parts
splits :: [a] -> [ ([a],[a]) ]
splits [] = [ ([],[]) ]
splits (a:as) = ([],a:as) : [(a:bs,cs) | (bs,cs) <- splits as]
int[]
和 array.clone()
)。public void permutate(int[] list, int pointer) {
if (pointer == list.length) {
//stop-condition: print or process number
return;
}
for (int i = pointer; i < list.length; i++) {
int[] permutation = (int[])list.clone();.
permutation[pointer] = list[i];
permutation[i] = list[pointer];
permutate(permutation, pointer + 1);
}
}
递归总是需要一定的心理努力来维护。对于大数,阶乘很容易变得非常巨大,并且堆栈溢出将很容易成为一个问题。
对于小的数字(3或4,这是最常见的),多重循环非常简单和直接。不幸的是,使用循环的答案没有被投票。
让我们从枚举开始(而不是排列)。只需将代码视为伪perl代码即可。
$foreach $i1 in @list
$foreach $i2 in @list
$foreach $i3 in @list
print "$i1, $i2, $i3\n"
枚举经常比排列出现得更多,但如果需要排列,只需添加条件:
$foreach $i1 in @list
$foreach $i2 in @list
$if $i2==$i1
next
$foreach $i3 in @list
$if $i3==$i1 or $i3==$i2
next
print "$i1, $i2, $i3\n"
如果您确实需要处理大型列表的通用方法,我们可以使用基数排序。首先考虑枚举问题:
$n=@list
my @radix
$for $i=0:$n
$radix[$i]=0
$while 1
my @temp
$for $i=0:$n
push @temp, $list[$radix[$i]]
print join(", ", @temp), "\n"
$call radix_increment
subcode: radix_increment
$i=0
$while 1
$radix[$i]++
$if $radix[$i]==$n
$radix[$i]=0
$i++
$else
last
$if $i>=$n
last
基数增量本质上是数字计数(在列表元素的基础上)。
现在,如果您需要排列,只需在循环内添加检查即可:
subcode: check_permutation
my @check
my $flag_dup=0
$for $i=0:$n
$check[$radix[$i]]++
$if $check[$radix[$i]]>1
$flag_dup=1
last
$if $flag_dup
next
编辑:上面的代码应该可以工作,但是对于排列来说,radix_increment可能会浪费时间。因此,如果时间是一个实际问题,我们必须将radix_increment更改为permute_inc:
subcode: permute_init
$for $i=0:$n
$radix[$i]=$i
subcode: permute_inc
$max=-1
$for $i=$n:0
$if $max<$radix[$i]
$max=$radix[$i]
$else
$for $j=$n:0
$if $radix[$j]>$radix[$i]
$call swap, $radix[$i], $radix[$j]
break
$j=$i+1
$k=$n-1
$while $j<$k
$call swap, $radix[$j], $radix[$k]
$j++
$k--
break
$if $i<0
break
我已经在ANSI C中编写了这个递归解决方案。每次执行Permutate函数都会提供一个不同的排列,直到所有排列都完成。全局变量也可以用于变量fact和count。
#include <stdio.h>
#define SIZE 4
void Rotate(int vec[], int size)
{
int i, j, first;
first = vec[0];
for(j = 0, i = 1; i < size; i++, j++)
{
vec[j] = vec[i];
}
vec[j] = first;
}
int Permutate(int *start, int size, int *count)
{
static int fact;
if(size > 1)
{
if(Permutate(start + 1, size - 1, count))
{
Rotate(start, size);
}
fact *= size;
}
else
{
(*count)++;
fact = 1;
}
return !(*count % fact);
}
void Show(int vec[], int size)
{
int i;
printf("%d", vec[0]);
for(i = 1; i < size; i++)
{
printf(" %d", vec[i]);
}
putchar('\n');
}
int main()
{
int vec[] = { 1, 2, 3, 4, 5, 6 }; /* Only the first SIZE items will be permutated */
int count = 0;
do
{
Show(vec, SIZE);
} while(!Permutate(vec, SIZE, &count));
putchar('\n');
Show(vec, SIZE);
printf("\nCount: %d\n\n", count);
return 0;
}
function permutations(arr){
var len = arr.length,
perms = [],
rest,
picked,
restPerms,
next;
//for one or less item there is only one permutation
if (len <= 1)
return [arr];
for (var i=0; i<len; i++)
{
//copy original array to avoid changing it while picking elements
rest = Object.create(arr);
//splice removed element change array original array(copied array)
//[1,2,3,4].splice(2,1) will return [3] and remaining array = [1,2,4]
picked = rest.splice(i, 1);
//get the permutation of the rest of the elements
restPerms = permutations(rest);
// Now concat like a+permute(bc) for each
for (var j=0; j<restPerms.length; j++)
{
next = picked.concat(restPerms[j]);
perms.push(next);
}
}
return perms;
}
请仔细阅读以下内容,这段代码是从 (JavaScript中的排列组合) 获取的。
在PHP中
$set=array('A','B','C','D');
function permutate($set) {
$b=array();
foreach($set as $key=>$value) {
if(count($set)==1) {
$b[]=$set[$key];
}
else {
$subset=$set;
unset($subset[$key]);
$x=permutate($subset);
foreach($x as $key1=>$value1) {
$b[]=$value.' '.$value1;
}
}
}
return $b;
}
$x=permutate($set);
var_export($x);
public class PermutationGenerator
{
private LinkedList<List<int>> _permutationsList;
public void FindPermutations(List<int> list, int permutationLength)
{
_permutationsList = new LinkedList<List<int>>();
foreach(var value in list)
{
CreatePermutations(value, permutationLength);
}
}
private void CreatePermutations(int value, int permutationLength)
{
var node = _permutationsList.First;
var last = _permutationsList.Last;
while (node != null)
{
if (node.Value.Count < permutationLength)
{
GeneratePermutations(node.Value, value, permutationLength);
}
if (node == last)
{
break;
}
node = node.Next;
}
List<int> permutation = new List<int>();
permutation.Add(value);
_permutationsList.AddLast(permutation);
}
private void GeneratePermutations(List<int> permutation, int value, int permutationLength)
{
if (permutation.Count < permutationLength)
{
List<int> copyOfInitialPermutation = new List<int>(permutation);
copyOfInitialPermutation.Add(value);
_permutationsList.AddLast(copyOfInitialPermutation);
List<int> copyOfPermutation = new List<int>();
copyOfPermutation.AddRange(copyOfInitialPermutation);
int lastIndex = copyOfInitialPermutation.Count - 1;
for (int i = lastIndex;i > 0;i--)
{
int temp = copyOfPermutation[i - 1];
copyOfPermutation[i - 1] = copyOfPermutation[i];
copyOfPermutation[i] = temp;
List<int> perm = new List<int>();
perm.AddRange(copyOfPermutation);
_permutationsList.AddLast(perm);
}
}
}
public void PrintPermutations(int permutationLength)
{
int count = _permutationsList.Where(perm => perm.Count() == permutationLength).Count();
Console.WriteLine("The number of permutations is " + count);
}
}