'leet' 程序 - 获取所有排列

'leet' program - getting all permutations

我编写了一个简单的函数,可以将单词中的字母转换为 'leet' 对应的数字。

def Leet(word):
    letters = list(word.lower())
    for n, letter, in enumerate(letters):
        if letter == 'o':
            letters[n]= '0'
        elif letter == 'i':
            letters[n]= '1'
        elif letter == 'z':
            letters[n]= '2'
        elif letter == 'e':
            letters[n]= '3'
        elif letter == 'a':
            letters[n]= '4'
        elif letter == 's':
            letters[n]= '5'
        elif letter == 'g':
            letters[n]= '6'
        elif letter == 't':
            letters[n]= '7'
        elif letter == 'b':
            letters[n]= '8'
    return ''.join(letters)

所以当我输入 'zit' 时,程序将 return '217'

我的问题是,如何更改它以提供所有可能的排列('217''2it''z1t''zi7''21t' , ETC。)?我读过 itertools 但我对如何将它应用到我的函数感到困惑。

使用itertools.product。此外,我建议使用 dict 进行映射,而不是 if/elif.

的级联
>>> from itertools import product
>>> LEET = { 'z': '2', 'i': '1', 't': '7' } # and all the others     
>>> word = "zit"
>>> [''.join(letters) for letters in product(*({c, LEET.get(c, c)} for c in word))]
['zit', 'zi7', 'z1t', 'z17', '2it', '2i7', '21t', '217']

注意LEET.get(c, c)会从dict中获取"leet"字母,或者默认使用原始字母。 {...} 用于制作这些对集,因此没有重复的字母没有替换。在 Python 的旧版本中,您可能必须改用 set([...])

那条相当复杂的 product(*...) 行大致如下所示:

    product(*({c, LEET.get(c, c)} for c in 'zit'))
==> product(*({'z', LEET.get('z', 'z')}, {'i', LEET.get('i', 'i')}, {'t', LEET.get('t', 't')}))
==> product(*({'z', '2'}, {'i', '1'}, {'t', '7'}))
==> product(  {'z', '2'}, {'i', '1'}, {'t', '7'} )

生成所有这些字母及其替换的 cartesian product

一种方法是使用 itertools.product,正如您提到的,它将执行列表的笛卡尔积。

问题是要有每个组合的这个列表,例如对于 zit,列表应该是:

[['z', '2'], ['i', '1'], ['t', '7']]

我的代码:

import itertools

def leet(word):
    leet_matches = [['a', '4'],
    ['b' ,'8'],
    ['c'],
    ['d'],
    ['e', '3'],
    ['f'],
    ['g', '6'],
    ['h'],
    ['i', '1'],
    ['j'],
    ['k'],
    ['l'],
    ['m'],
    ['n'],
    ['o', '0'],
    ['p'],
    ['q'],
    ['r'],
    ['s', '5'],
    ['t', '7'],
    ['u'],
    ['v'],
    ['w'],
    ['x'],
    ['y'],
    ['z', '2']]
    l = []
    for letter in word:
        for match in leet_matches:
            if match[0] == letter:
                l.append(match)
    return list(itertools.product(*l))

print leet("zit")

请注意,使用列表(或元组)的列表而不是 Dict 允许您对一个字母进行多次替换,例如"i"可以变成“1”或“!”

首先观察到您可以缩短查找时间,如下所示:

REPLACE = { letter: str(index) for index, letter in enumerate('oizeasgtb') }

def Leet2(word):
    letters = [ REPLACE.get(l, l) for l in word.lower() ]
    return ''.join(letters)

REPLACE 看起来像:

{'a': '4', 'b': '8', 'e': '3', 'g': '6', 'i': '1', 
 'o': '0', 's': '5', 't': '7', 'z': '2'}

REPLACE.get(l,l) 返回替换字母,如果没有替换字母,则返回原始字母。

第二个观察结果是您真的不需要排列,排列是顺序的变化。 '217'的排列是:

>>> [ ''.join(p) for p in permutations('217') ]
['217', '271', '127', '172', '721', '712']

您真正需要的是一个列表的产品,该列表对给定字符位置的所有可能选择进行编码:

[('z', '2'), ('i', '1'), ('t', '7')]

如果我还展示了一个可能的列表,其中包含一些没有有效替换的字符,那么它的工作原理可能会更清楚。对于 'red' 例如:

[('r',), ('e', '3'), ('d',)]

现在我们需要这些选项的字符串连接乘积。把它们放在一起:

from itertools import product

def Leet2Combos(word):
    possibles = []
    for l in word.lower():
        ll = REPLACE.get(l, l)
        possibles.append( (l,) if ll == l else (l, ll) )
    return [ ''.join(t) for t in product(*possibles) ]

print Leet2Combos('zit')
print Leet2Combos('red')

给出:

['zit', 'zi7', 'z1t', 'z17', '2it', '2i7', '21t', '217']
['red', 'r3d']