所以我需要一个算法来生成除循环旋转之外的数字列表的所有排列(例如[1,2,3] == [2,3,1] == [3,1,2])。< / p>
当序列中至少有一个唯一编号时,它是相当直接的,取出该唯一编号,生成剩余编号的所有排列(但对“标准”排列算法进行少量修改)并添加前面的唯一号码。
为了生成排列,我发现有必要将排列代码更改为:
def permutations(done, options)
permuts = []
seen = []
for each o in options
if o not in seen
seen.add(o)
permuts += permutations(done+o, options.remove(o))
return permuts
仅使用选项中的每个唯一数字意味着您没有获得322次。
当没有唯一元素时,该算法仍然输出旋转,例如对于[1,1,2,2],它将输出[1,1,2,2],[1,2,2,1]和[1,2,1,2],前两个是循环旋转。
那么是否有一种有效的算法可以让我生成所有的排列而无需事后去除循环旋转?
如果不是,那么删除循环旋转的最有效方法是什么?
注意:这是不使用Python,而是使用C ++。
答案 0 :(得分:5)
考虑测试您输出的每个排列,寻找比您所获得的更早“词法”的循环旋转。如果有的话,请不要返回 - 它将在其他地方枚举。
选择“唯一”第一个元素(如果存在)可帮助您进行优化。你知道如果你修复了第一个元素,并且它是唯一的,那么你就不可能通过旋转复制它。另一方面,如果没有这样的独特元素,只需选择最少的元素。这样,您只需要检查具有第一个元素的循环旋转。 (例如,当你生成[1,2,2,1] - 你只需要检查[1,1,2,2],而不是[2,2,1,1]或[2,1,1,2] ])。
好吧,伪代码......显然是O(n!),我确信没有更聪明的方法,因为“所有符号不同”的情况显然必须返回(n-1)!元件。
// generate all permutations with count[0] 0's, count[1] 1's...
def permutations(count[])
if(count[] all zero)
return just the empty permutation;
else
perms = [];
for all i with count[i] not zero
r = permutations(copy of count[] with element i decreased);
perms += i prefixed on every element of r
return perms;
// generate all noncyclic permutations with count[0] 0's, count[1] 1's...
def noncyclic(count[])
choose f to be the index with smallest count[f];
perms = permutations(copy of count[] with element f decreased);
if (count[f] is 1)
return perms;
else
noncyclic = [];
for each perm in perms
val = perm as a value in base(count.length);
for each occurence of f in perm
test = perm rotated so that f is first
tval = test as a value in base(count.length);
if (tval < val) continue to next perm;
if not skipped add perm to noncyclic;
return noncyclic;
// return all noncyclic perms of the given symbols
def main(symbols[])
dictionary = array of all distinct items in symbols;
count = array of counts, count[i] = count of dictionary[i] in symbols
nc = noncyclic(count);
return (elements of nc translated back to symbols with the dictionary)
答案 1 :(得分:5)
对于所有数字都不同的排列情况,这很简单。假设数字为1,2,...,n
,然后生成1,2,...,n-1
的所有排列并在前面粘贴n
。这给出了全套模数循环旋转的所有排列。例如,使用n=4
,您可以执行
4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1
这可确保1,2,3,4
的每个排列的某些循环旋转在列表中只出现一次。
对于您想要多重集的排列(允许重复输入)的一般情况,您可以使用类似的技巧。删除最大字母n
的所有实例(类似于忽略上例中的4
)并生成剩余多重集的所有排列。下一步是以规范的方式将n
重新置于排列中(类似于在上面的示例中将4
放在开头)。
这只是查找所有Lyndon words以生成necklaces
的情况答案 2 :(得分:1)
这个解决方案将涉及一些itertools.permutations
用法,set()
,以及一些好的&#39;塑造了差异。请记住,查找排列的运行时仍然是O(n!)。我的解决方案也不会在线进行,但可能是一个更优雅的解决方案,允许您这样做(并且不涉及itertools.permutations
)。为此,这是完成任务的直接方式。
步骤1:使用给定的第一个元素生成周期的算法。对于列表[1, 1, 2, 2]
,这将为我们提供[1, 1, 2, 2], [1, 2, 2, 1], [2, 1, 1, 2], [2, 2, 1, 1]
。
def rotations(li):
count = 0
while count < len(li):
yield tuple(li)
li = li[1:] + [li[0]]
count += 1
第2步:导入itertools.permutations
以便首先为我们提供排列,然后将结果设置为set
。
from itertools import permutations
perm = set(permutations([1, 1, 2, 2]))
步骤3:使用生成器为我们提供我们自己的集合,包含循环(我们想要摆脱的东西)。
cycles = set(((i for i in rotations([1, 1, 2, 2]))))
步骤4:对每个应用设置差异,并删除循环。
perm = perm.difference(cycles)
希望这会帮助你。我愿意接受建议和/或更正。
答案 3 :(得分:1)
首先,我将展示我们将using
的容器和算法:
#include <vector>
#include <set>
#include <algorithm>
#include <iostream>
#include <iterator>
using std::vector;
using std::set;
using std::sort;
using std::next_permutation;
using std::copy;
using std::ostream_iterator;
using std::cout;
接下来我们的vector
代表Permutation
:
typedef vector<unsigned int> Permutation;
我们需要一个比较对象来检查排列是否是一个旋转:
struct CompareCyclicPermutationsEqual
{
bool operator()(const Permutation& lhs, const Permutation& rhs);
};
typedef
使用循环比较对象的set
:
typedef set<Permutation, CompareCyclicPermutationsEqual> Permutations;
然后主要功能很简单:
int main()
{
Permutation permutation = {1, 2, 1, 2};
sort(permutation.begin(). permutation.end());
Permutations permutations;
do {
permutations.insert(permutation);
} while(next_permutation(numbers.begin(), numbers.end()))
copy(permutations.begin(), permutations.end(),
ostream_iterator<Permutation>(cout, "\n");
return 0;
}
输出:
1, 1, 2, 2,
1, 2, 1, 2,
我还没有实现CompareCyclicPermutationsEqual
。您还需要实施ostream& operator<<(ostream& os, const Permutation& permutation)
。