2015-03-19 2 views
1

Я написал простую функцию, которая преобразует буквы в слова в их «литовые» числовые копии.'leet' program - получение всех перестановок

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', программа будет возвращать '217'.

Мой вопрос, как я могу изменить его, чтобы дать мне все возможные перестановки ('217', '2it', 'z1t', 'zi7', '21t' и т.д.)? Я читал о itertools, но я в тупике, как применить его к моей функции.

+0

Обратите внимание, что словарь '{ 'о': '0', ...}' сделает это ** ** много аккуратнее. – jonrsharpe

ответ

4

Первое наблюдение состоит в том, что вы можете сократить поиск, например:

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'] 
3

Использование 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, или использовать оригинальные письма по умолчанию. {...} используется для создания этих пар, поэтому дубликатов для букв без замены. В старых версиях 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 все эти буквы и их замены.

+0

Хороший ответ. Интересно, что с '*', в 'product (* [(...'? Я еще не на 100% удобнее с Python). – streppel

+0

Это отлично подходит для таких слов, как '' zit'', для которых есть два варианта для каждой буквенной позиции.Это работает немного менее хорошо для таких слов, как «красный», для которых он возвращает «[» красный, красный, красный, красный, r3d ',' r3d '] '- список с дубликатами. Предложите обернуть либо результат, либо внутренние кортежи списка с помощью операции' set() 'для компенсации. –

0

Один из способов сделать это - использовать itertools.product, как вы упомянули, который будет выполнять декартовое произведение списка.

Проблема будет иметь этот список, из каждой combinaison например, для прыщик список должен быть:

[['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» или «!»