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

发布于 2024-12-29 12:08:17 字数 703 浏览 4 评论 0原文

因此,我需要一种算法来生成不包括循环旋转的数字列表的所有排列(例如 [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] ,1,2],前两个是循环旋转。

那么是否有一种有效的算法可以让我生成所有排列,而不必事后删除循环旋转?

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

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

So I need an algorithm to generate all permutations of a list of numbers excluding cyclic rotations (e.g. [1,2,3] == [2,3,1] == [3,1,2]).

When there is at least 1 unique number in the sequence it is fairly straight forward, take out that unique number, generate all permutations of the remaining numbers (but with a small modification to the 'standard' permutations algorithm) and add the unique number to the front.

For generating the permutations I've found that it's necessary to change the permutations code to:

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

Only using each unique number in options once means that you don't get 322 twice.

This algorithm still outputs rotations when there are no unique elements, e.g. for [1,1,2,2] it would output [1,1,2,2], [1,2,2,1] and [1,2,1,2] and the first two are cyclic rotations.

So is there an efficient algorithm that would allow me to generate all the permutations without having to go through afterwards to remove cyclic rotations?

If not, what would be the most efficient way to remove cyclic rotations?

NOTE: this is not using Python, but rather C++.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

陌伤ぢ 2025-01-05 12:08:17

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

For the case of permutations where all numbers are distinct, this is simple. Say the numbers are 1,2,...,n, then generate all permutations of 1,2,...,n-1 and stick n at the front. This gives all permutations of the full set modulo cyclic rotations. For example, with n=4, you would do

4 1 2 3
4 1 3 2
4 2 1 3
4 2 3 1
4 3 1 2
4 3 2 1

This ensures that some cyclic rotation of each permutation of 1,2,3,4 appears exactly once in the list.

For the general case where you want permutations of a multiset (repeated entries allowed), you can use a similar trick. Remove all instances of the largest letter n (similar to ignoring the 4 in the above example) and generate all permutations of the remaining multiset. The next step is to put the ns back into the permutations in canonical ways (similar to putting the 4 at the beginning in the above example).

This is really just a case of finding all Lyndon words to generate necklaces

墨小墨 2025-01-05 12:08:17

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

选择“唯一”的第一个元素(如果存在)可以帮助您进行优化。您知道,如果您修复了第一个元素,并且它是唯一的,那么您不可能通过旋转来复制它。另一方面,如果没有这样的独特元素,则只需选择出现次数最少的元素即可。这样您只需要检查具有第一个元素的循环旋转。 (例如,当您生成 [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)

Think about testing each of the permutations you output, looking for a cyclic rotation that's "lexically" earlier than the one you've got. If there is one, don't return it - it will have been enumerated somewhere else.

Choosing a "unique" first element, if one exists, helps you optimize. You know if you fix the first element, and it's unique, then you can't possibly have duplicated it with a rotation. On the other hand, if there's no such unique element, just choose the one that occurs the least. That way you only need to check for cyclic rotations that have that first element. (Example, when you generate [1,2,2,1] - you only need to check [1,1,2,2], not [2,2,1,1] or [2,1,1,2]).


OK, pseudocode... clearly O(n!), and I'm convinced there's no smarter way, since the case "all symbols different" obviously has to return (n-1)! elements.

// 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)
一念一轮回 2025-01-05 12:08:17

该解决方案将涉及一些 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)

希望这会对您有所帮助。我愿意接受建议和/或更正。

This solution is going to involve a bit of itertools.permutations usage, set(), and some good ol' fashioned set difference. Bear in mind, the runtime for finding a permutation will still be O(n!). My solution won't do it in-line, either, but there may be a much more elegant solution that allows you to do so (and doesn't involve itertools.permutations). For this purpose, this is the straightforward way to accomplish the task.

Step 1: Algorithm for generating cycles, using the first element given. For a list [1, 1, 2, 2], this will give us [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

Step 2: Importing itertools.permutations to give us the permutations in the first place, then setting up its results into a set.

from itertools import permutations
perm = set(permutations([1, 1, 2, 2]))

Step 3: Using the generator to give us our own set, with the cycles (something we want to rid ourselves of).

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

Step 4: Apply set difference to each and the cycles are removed.

perm = perm.difference(cycles)

Hopefully this will help you out. I'm open to suggestions and/or corrections.

み青杉依旧 2025-01-05 12:08:17

首先,我将展示我们将使用的容器和算法:

#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;

接下来是我们的向量,它将代表排列

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&运算符<<(ostream&os, const Permutation& permutation)。

First I'll show the containers and algorithms we'll be 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;

Next our vector which will represent the Permutation:

typedef vector<unsigned int> Permutation;

We need a comparison object to check whether a permutation is a rotation:

struct CompareCyclicPermutationsEqual
{
    bool operator()(const Permutation& lhs, const Permutation& rhs);
};

And typedef a set which uses the cyclic comparison object:

typedef set<Permutation, CompareCyclicPermutationsEqual> Permutations;

Then the main function is quite simple:

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;
}

Output:

1, 1, 2, 2,
1, 2, 1, 2,

I haven't implemented CompareCyclicPermutationsEqual yet. Also you'd need to implement ostream& operator<<(ostream& os, const Permutation& permutation).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文