从itertools.product中排除相同元素的运行

时间:2018-02-13 21:05:01

标签: python algorithm python-2.7

我有以下代码:

possible_keys = map(lambda combo: ''.join(combo), itertools.product(LETTERS, repeat=key_len))

它根据我传递的密钥长度生成字母表中的所有可能组合。例如,如果我传入2,它将生成aa,ab,ac等。如果我传入3,它将生成aaa,aab,aac等等。

我想通过删除字符串是完全相同字母的实例来优化我的代码,例如aaa,bbbb,ccccccc。基本上,如果这是一个矩阵,删除对角线。我怎么能用这个或者有更好的方法来实现呢?

编辑:我正在通过字典攻击来处理Vigenere Cipher。我不想透露我正在处理的大问题,因为我担心人们会给出答案而不是哈哈。虽然,现在欢迎任何建议:)这是我的程序的第一次迭代,所以它真的很低效,因为我正在浏览所有可能的键并将其与提供的字典中的内容相匹配..

5 个答案:

答案 0 :(得分:3)

使用相同元素的运行位于

的事实
  

0 + L 1 + ... + L L-1 =(L - 1)/(L-1)

除了元素之外,总共有L个元素,我们可以将它们从最后的product中排除,而不会妨碍内部循环或计算哈希值:

LETTERS='abc'
l = len(LETTERS)
p = [''.join(i) for i in itertools.product(LETTERS,repeat=l)]
step=(l**l-1)/(l-1)
for i in range(l):
    del p[i*step-i]    #allow for the fact that each time we delete an element,
                       #all indices shift backward by one

比较表现:

In [88]: letters=string.ascii_lowercase[:8]    # 8**8=16777216 elements in product

In [89]: timeit ex(letters)           # this solution
1 loop, best of 3: 6.1 s per loop  

In [90]: timeit minus_set(letters)    # subtracting a set at the end
1 loop, best of 3: 28.1 s per loop

In [92]: timeit ck_len(letters)       # checking len(set(i))
1 loop, best of 3: 15.1 s per loop

In [94]: timeit not_set(letters)      # checking `not in exclude'
1 loop, best of 3: 7.54 s per loop

def ex_mod_iter(letters):   # counter in the loop like it'd be done in C
    l = len(letters)
    step=(l**l-1)/(l-1)
    p = [''.join(v) for i,v in enumerate(itertools.product(letters,repeat=l)) if i % step]
    return p

In [5]: timeit ex_mod_iter(letters)
1 loop, best of 3: 6.61 s per loop

答案 1 :(得分:2)

您只需检查第一个字母是否占据整个字符串,并排除符合条件的字符串:

possible_keys = [''.join(x) for x in product(L, repeat=key_len) 
                                      if len(x) != x.count(x[0])]

虽然tuple.count具有与set()相同的O(n)复杂度,但计数相对更便宜,因此可能比构建元组中的 <快> 。< / p>

答案 2 :(得分:1)

使用set(只能保存唯一值),我们可以这样做:

import itertools
possible_keys = [''.join(i) for i in itertools.product('AB', repeat=2) if len(set(i)) !=1]
print(possible_keys)

返回:

['AB', 'BA']

旁注:这里不需要lambda

提高速度: 如果速度更重要,我们也可以先制作一个例外列表。

exclude = {(x,)*3 for x in 'ABC'}
possible_keys= [''.join(i) for i in itertools.product('ABC', repeat=3) if i not in exclude]

对于[A-Z],您可以使用:

import string
n = 4
letters = string.ascii_uppercase
exc = {(x,)*n for x in letters}
l = [''.join(i) for i in itertools.product(letters, repeat=n) if i not in exc]

<强>定时

%timeit l = {(x,)*3 for x in 'ABC'};[''.join(i) for i in itertools.product('ABC', repeat=3) if i not in l ]
%timeit [''.join(i) for i in itertools.product('ABC', repeat=3) if len(set(i)) !=1]
%timeit [''.join(x) for x in itertools.product('ABC', repeat=3) if len(x) != x.count(x[0])]

100000 loops, best of 3: 5.48 µs per loop
100000 loops, best of 3: 10.7 µs per loop
100000 loops, best of 3: 8.19 µs per loop

答案 3 :(得分:0)

不确定这是多么有效,但它应该可以解决问题。

# Remove combos that are composed of the same letter.
possible_keys = map(lambda combo: ''.join(combo) if len(set(combo)) > 1 else False, itertools.product(LETTERS, repeat=key_len))

它也使用set但在lambda表达式中进行过滤。

答案 4 :(得分:0)

解决方案

您可以使用itertools.repeat获取您希望从原始列表中删除的序列列表。

import itertools
import string

# get all sequences with all same letters
all_same_sequences = [''.join(list(itertools.repeat(char, key_len))) for char in string.ascii_lowercase]

# take those sequences out of the original list
possible_keys = sort(list(set(possible_keys ) - set(all_same_sequences)))