生成除循环旋转之外的所有排列

Generating all permutations excluding cyclic rotations

本文关键字:排列 循环 旋转      更新时间:2023-10-16

因此,我需要一个算法来生成不包括循环旋转的数字列表的所有排列(例如[1,2,3]==[2,3,1]=[3,1,2])。

当序列中至少有1个唯一数时,这是相当直接的,取出该唯一数,生成剩余数的所有排列(但对"标准"排列算法进行了小修改),并将该唯一数添加到前面。

为了生成排列,我发现有必要将排列代码更改为:

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,2],并且前两个是循环旋转。

那么,有没有一种有效的算法可以让我生成所有的排列,而不必在之后进行循环旋转?

如果没有,去除循环旋转的最有效方法是什么?

注意:这不是使用Python,而是使用C++。

对于所有数字都不同的排列情况,这很简单。假设数字是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单词以生成项链的例子

考虑测试您输出的每一个排列,寻找一个"词汇上"早于现有排列的循环旋转。如果有,不要返回它——它会在其他地方被枚举。

选择"唯一"的第一个元素(如果存在)有助于优化。你知道,如果你修复了第一个元素,并且它是唯一的,那么你不可能用旋转来复制它。另一方面,如果没有这种独特的元素,只需选择出现最少的元素。这样,您只需要检查具有第一个元素的循环旋转。(例如,当您生成[1,2,2,1]时,您只需要检查[1,1,2,2],而不需要检查[2,2,1,1]或[2,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)

此解决方案将涉及一些itertools.permutations用法、set()和一些好的老式集差异。请记住,查找排列的运行时仍然是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]))

第三步:使用生成器给我们自己的集合,以及循环(我们想摆脱的东西)。

cycles = set(((i for i in rotations([1, 1, 2, 2]))))

步骤4:将设置差异应用于每个循环,然后删除循环。

perm = perm.difference(cycles)

希望这能帮助你。我愿意接受建议和/或更正。

首先我将展示容器和算法,我们将是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)