Joh*_*0te 6 c++ algorithm permutation
我一直在审查练习算法,我现在正在研究一种我非常喜欢的置换算法:
void permute(char* set, int begin, int end) {
int range = end - begin;
if (range == 1)
cout << set << endl;
else {
for(int i = 0; i < range; ++i) {
swap(&set[begin], &set[begin+i]);
permute(set, begin+1, end);
swap(&set[begin], &set[begin+i]);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我实际上想将此应用于会有许多重复字符的情况,所以我需要能够修改它以防止打印重复的排列.
我如何检测我是否生成了重复内容?我知道我可以将它存储在散列或类似的东西中,但这不是最佳解决方案 - 我更喜欢不需要额外存储的解决方案.有人可以给我一个建议吗?
PS:我不想使用STL置换机制,我不想在某处引用另一个"唯一置换算法".我想了解用于防止重复的机制,以便在可能的情况下将其构建到学习中.
有没有通用的方法,以防止随意从产生重复的功能.当然,您可以随时过滤掉重复项,但您不希望这样做,并且有很好的理由.因此,您需要一种特殊的方法来仅生成非重复项.
一种方法是以增加的字典顺序生成排列.然后你可以比较一个"新"的permatutation是否与最后一个相同,然后跳过它.它变得更好:在http://en.wikipedia.org/wiki/Permutations#Generation_in_lexicographic_order给出的增加字典顺序的排列生成算法甚至根本不生成重复项!
但是,这不是您问题的答案,因为它是一种不同的算法(尽管也基于交换).
那么,让我们看一下你的算法.一个关键的观察是:
begin,它将留在那里进行所有嵌套调用permute.我们将这与以下关于排列的一般观察结合起来:
s,但只在具有相同字符的位置,s将保持不变.实际上,所有重复排列对于某些字符c的出现具有不同的顺序,其中c出现在相同的位置.好的,所以我们要做的就是确保每个角色的出现始终与开头的顺序相同.代码如下,但是......我真的不会说C++,所以我会使用Python并希望能够声称它是伪代码.
我们从你的原始算法开始,用'伪代码'重写:
def permute(s, begin, end):
if end == begin + 1:
print(s)
else:
for i in range(begin, end):
s[begin], s[i] = s[i], s[begin]
permute(s, begin + 1, end)
s[begin], s[i] = s[i], s[begin]
Run Code Online (Sandbox Code Playgroud)
以及一个使调用更容易的辅助函数:
def permutations_w_duplicates(s):
permute(list(s), 0, len(s)) # use a list, as in Python strings are not mutable
Run Code Online (Sandbox Code Playgroud)
现在我们通过一些簿记来扩展permute函数,关于某个字符被交换到该begin位置的次数(即已被修复),并且我们还记住每个字符(char_number)的出现的原始顺序.我们尝试交换到该begin位置的每个字符必须是原始顺序中的下一个字符,即字符的修复数量定义了下一个可以修复此字符的原始出现 - 我称之为next_fixable.
def permute2(s, next_fixable, char_number, begin, end):
if end == begin + 1:
print(s)
else:
for i in range(begin, end):
if next_fixable[s[i]] == char_number[i]:
next_fixable[s[i]] += 1
char_number[begin], char_number[i] = char_number[i], char_number[begin]
s[begin], s[i] = s[i], s[begin]
permute2(s, next_fixable, char_number, begin + 1, end)
s[begin], s[i] = s[i], s[begin]
char_number[begin], char_number[i] = char_number[i], char_number[begin]
next_fixable[s[i]] -= 1
Run Code Online (Sandbox Code Playgroud)
我们再次使用辅助函数:
def permutations_wo_duplicates(s):
alphabet = set(s)
next_fixable = dict.fromkeys(alphabet, 0)
count = dict.fromkeys(alphabet, 0)
char_number = [0] * len(s)
for i, c in enumerate(s):
char_number[i] = count[c]
count[c] += 1
permute2(list(s), next_fixable, char_number, 0, len(s))
Run Code Online (Sandbox Code Playgroud)
而已!
几乎.如果您愿意,可以在此处停止并使用C++重写,但如果您对某些测试数据感兴趣,请继续阅读.
我使用了稍微不同的代码进行测试,因为我不想打印所有的排列.在Python中,你将把a替换为printa yield,将函数转换为生成函数,其结果可以用for循环迭代,并且只在需要时才计算置换.这是我使用的真实代码和测试:
def permute2(s, next_fixable, char_number, begin, end):
if end == begin + 1:
yield "".join(s) # join the characters to form a string
else:
for i in range(begin, end):
if next_fixable[s[i]] == char_number[i]:
next_fixable[s[i]] += 1
char_number[begin], char_number[i] = char_number[i], char_number[begin]
s[begin], s[i] = s[i], s[begin]
for p in permute2(s, next_fixable, char_number, begin + 1, end):
yield p
s[begin], s[i] = s[i], s[begin]
char_number[begin], char_number[i] = char_number[i], char_number[begin]
next_fixable[s[i]] -= 1
def permutations_wo_duplicates(s):
alphabet = set(s)
next_fixable = dict.fromkeys(alphabet, 0)
count = dict.fromkeys(alphabet, 0)
char_number = [0] * len(s)
for i, c in enumerate(s):
char_number[i] = count[c]
count[c] += 1
for p in permute2(list(s), next_fixable, char_number, 0, len(s)):
yield p
s = "FOOQUUXFOO"
A = list(permutations_w_duplicates(s))
print("%s has %s permutations (counting duplicates)" % (s, len(A)))
print("permutations of these that are unique: %s" % len(set(A)))
B = list(permutations_wo_duplicates(s))
print("%s has %s unique permutations (directly computed)" % (s, len(B)))
print("The first 10 permutations :", A[:10])
print("The first 10 unique permutations:", B[:10])
Run Code Online (Sandbox Code Playgroud)
结果如下:
FOOQUUXFOO has 3628800 permutations (counting duplicates)
permutations of these that are unique: 37800
FOOQUUXFOO has 37800 unique permutations (directly computed)
The first 10 permutations : ['FOOQUUXFOO', 'FOOQUUXFOO', 'FOOQUUXOFO', 'FOOQUUXOOF', 'FOOQUUXOOF', 'FOOQUUXOFO', 'FOOQUUFXOO', 'FOOQUUFXOO', 'FOOQUUFOXO', 'FOOQUUFOOX']
The first 10 unique permutations: ['FOOQUUXFOO', 'FOOQUUXOFO', 'FOOQUUXOOF', 'FOOQUUFXOO', 'FOOQUUFOXO', 'FOOQUUFOOX', 'FOOQUUOFXO', 'FOOQUUOFOX', 'FOOQUUOXFO', 'FOOQUUOXOF']
Run Code Online (Sandbox Code Playgroud)
请注意,排列的计算顺序与原始算法的顺序相同,只是没有重复.注意37800*2!*2!*4!= 3628800,就像你期望的那样.