不带 inversions/reversions 的字典顺序字符串排列

String permutations in lexigraphic order without inversions/reversions

问题:

我想生成一个字符串排列的列表,但不包括字符串反转。例如,如果我有以下字符串:abc,我想生成以下列表

abc
acb
bac

而不是典型的

abc
acb
bac
bca
cab
cba

另一个例子看起来像这样:

100
010

而不是

100
010 
001

目前,我可以使用 perl 生成排列,但我不确定如何最好地删除反向重复项。

我曾想过应用如下内容:

create map with the following:
1)   100
2)   010
3)   001
then perform the reversion/inversion on each element in the map and create a new map with:

 1') 001
 2') 010
 3') 100

然后比较,如果primed list值与原始值匹配,则保留原位,如果不同,如果其索引大于中值索引,则保留,否则删除。

问题是,我不确定这是否是一种有效的方法。

任何建议都很好。

建议重复 Generating permutations which are not mirrors of each other 中的答案不处理重复的元素(因为那不是该问题的一部分)所以天真地遵循它会包括例如0100 和 0010。所以这不是完全相同的。但这个想法适用。

生成所有排列,但仅过滤具有 $_ le reverse $_ 的排列。我认为这基本上就是您在问题中提出的建议,但是当应用于每个排列的简单表达式将告诉您是否包含它时,无需计算映射。

示例代表的两种可能性是所有元素都不同的排列 (abcd),或者两个符号的变体,其中一个符号只出现一次 (1000)。还解决了更一般的情况。

Non-repeating个元素(排列)

在这里我们可以利用 Algorithm::Permute 和特定的观察结果: 需要排除第一个元素大于最后一个元素的每个排列。它来自 this post, brought up in the .

这条规则如下。考虑没有第一个和最后一个元素的字符串的子字符串。对于每个这样的子串,该串的所有排列都必须包含它的逆。其中一个用 last 和 first 填充,因此是字符串的倒数。通过构造,对于每个子串,恰好有一个逆。因此需要排除每个字符串的第一个和最后一个元素交换的排列。

use warnings;
use strict;
use feature 'say';

use Algorithm::Permute;

my $size = shift || 4;
my @arr = ('a'..'z')[0..$size-1];  # 1..$size for numbers

my @res;    
Algorithm::Permute::permute { 
    push @res, (join '', @arr) unless $arr[0] gt $arr[-1] 
} @arr; 

say for @arr;

问题与重复元素 (abbcd) 可以用与上述完全相同的方式处理,我们还需要修剪重复项作为 [=17= 的排列] 生成 abbcdabbcd (相同)

use List::MoreUtils 'uniq';
# build @res the same way as above ...
my @res = uniq @res;

在构建过程中这样做既不会降低复杂性也不会加快速度。

permute 被引用为模块中迄今为止最快的方法。它比我测试的其他模块(下面)快一个数量级,在我的系统上 10 个元素大约需要 1 秒。但请注意,此问题的复杂度为阶乘。炸得真快。

两个符号,其中一个只出现一次(变体)

这是不同的,上面的模块不适合它,排除标准也不起作用。还有其他模块,见文末。不过,这里的问题很简单。

从列表中的 (1,0,0,...) 和 'walk' 1 开始,直到 "midpoint" – 这是偶数列表的一半(4 表示 8 长), 或奇数尺寸的下半场(9 长为 5)。这样得到的所有字符串,通过将 1 移动一个位置到中点,形成集合。第二个 "half" 是它们的倒置。

use warnings;
use strict;

my $size = shift || 4;
my @n = (1, map { 0 } 1..$size-1);   

my @res = (join '', @n);       # first element of the result

my $end_idx = ( @n % 2 == 0 ) ? @n/2 - 1 : int(@n/2);

foreach my $i (0..$end_idx-1)  # stop one short as we write one past $i
{
    @n[$i, $i+1] = (0, 1);     # move 1 by one position from where it is
    push @res, join '', @n;
}

print "$_\n" for @res;

我们需要在最后一个索引之前停止,因为它已在上一次迭代中被填充。

如果两个符号 (0,1) 可能重复出现,这可以修改,但使用模块然后排除逆向要简单得多。 Algorithm::Combinatorics 这里有满足所有需求的例程。对于长度 $size01 的所有变体,其中两者可能重复

use Algorithm::Combinatorics qw(variations_with_repetition);

my @rep_vars = variations_with_repetition([0, 1], $size);

然后可以通过 brute-force 搜索排除逆向元素,最坏情况下具有 O(N2) 复杂度。

另请注意 Math::Combinatorics