Pull to refresh

Comments 18

Код может быть вы и понятно написали «даже для самого начинающего в Python», но вот сам алгоритм я так и не вспомнил по коду. Вы бы побольше комментировали, поменьше приводили куски кода. Код всегда можно самому написать, если алгоритм понятен.
Я решил что можно будет немножко усложнить задачу по взлому, если взять определенный набор символов (диапазон ASCII). Тут конечно придется учитывать еще что вы хотите зашифровать — если только англ. текст то сойдет, если еще и русские символы — брать больший диапазон. Ну это сугубо в качестве дополнительной плюшки.
Простите, но если мы все же пишем на питоне, то давайте писать на питоне:
from itertools import cycle
def form_dict():
    return dict([(i, chr(i)) for i in range(128)])

def comparator(value, key):
    return dict([(idx, [ch[0], ch[1]])
                for idx, ch in enumerate(zip(value, cycle(key)))])

def encode_val(word):
    d = form_dict()
    return [k for c in word for k,v in d.items() if v == c]

def full_encode(value, key):
    d = comparator(value, key)
    l = len(form_dict())
    return [(v[0] + v[1]) % l for v in d.values()]

def decode_val(list_in):
    l = len(list_in)
    d = form_dict()
    return [d[i] for i in list_in if i in d]

def full_decode(value, key):
    d = comparator(value, key)
    l = len(form_dict())
    return [(v[0] - v[1]) % l for v in d.values()]

… а не изображать basic-style.
А давайте писать все однострочниками на перле.
Вопрос не столько в однострочниках, сколько в неправильном использовании языковых средств.

Не зачем обращаться к символам строки по индексу, чтобы сравнить два символа, когда можно итерировать строку посимвольно и сравнивать символы. Не зачем итерировать ключи словаря, чтобы проверить, что искомое значение есть в их списке — hash-lookup будет быстрее и правильней и т.д.

То, что после устранения этих проблем все функции прекрасно описались через list comprehensions — случайность =)
Или так:

from itertools import cycle

LEN = 127

def full_encode(value, key):
    return ''.join(map(lambda x: chr((ord(x[0]) + ord(x[1])) % LEN), zip(value, cycle(key))))

def full_decode(value, key):
    return ''.join(map(lambda x: chr((ord(x[0]) - ord(x[1]) + LEN) % LEN), zip(value, cycle(key))))

if __name__ == "__main__":

    word = 'Hello world'
    key = 'key'
    
    print 'Слово: '+ word
    print 'Ключ: '+ key

    shifre = full_encode(word, key)
    print 'Шифр=', shifre

    decoded = full_decode(shifre, key)
    print 'Word=', decoded
Как-то мало тут всего. О чем статья — о том как (Pi+Ki) mod 26 на питоне написать? Да и «данный алгоритм шифрования больше не является на 100% надежным» — явное преуменьшение.

Вот если будет вторая часть — как ломать шифр Виженера — тогда да, уже поинтересней будет.
Если копируете куски вместе с формулами из википедии, то добавили бы от себя, что есть Ci, Pi и Ki в формулах.
Алгоритмический смысл шифрования заключается в индивидуальном сдвиге для каждого символа исходного текста. А величина этого сдвига берется из символа словаря (его позиции, кода), соотвествующего позиции рассматриваемого символа текста. Вот и получается, что результирующий символ это наш исходный плюс смещение, задаваемое позицией символа из словаря. При этом учитываем размер словаря и не выходим за его пределы. И получаем итоговые формулы.

Да и код получился очень запутанным. Можно было подобное сделать как-то так:
# -*- coding: utf-8 -*-

d = [chr(i) for i in range(127)]
dl = len(d)

prepval = lambda val: zip( range(0,len(val)), val )

enc = lambda ch,key: (ch+key) % dl
dec = lambda ch,key: (ch-key+dl) % dl

def vigenere(value, key, func):
    kl = len(key)
    value = prepval( value )
    e = [ func( ord(c), ord(key[i%kl]) ) for (i,c) in value ]
    return ''.join( [ d[c] for c in e ] )

src = 'Hello world'
key = 'key'

tmp = vigenere( src, key, enc )
print tmp
print vigenere( tmp, key, dec )
prepval(value) можно заменить на enumerate(value).
range(0, x) эквивалентно range(x).
Плюс вы упростили исходную задачу, предположив, что мы используем ASCII (это следует из того, что номер символа вы получаете с помощью ord, а не из словаря d).

Мой вариант:
# -*- coding: utf-8 -*-

from itertools import cycle, count
from functools import partial

def get_cypher(my_ord, my_chr, al_size):
    def process(func, value, key):
        key = cycle(map(my_ord, key))
        value = map(my_ord, value)
        result = map(func, zip(value, key))
        return ''.join(map(my_chr, result))
    encrypt = lambda x: (x[0] + x[1]) % al_size
    decrypt = lambda x: (x[0] - x[1] + al_size) % al_size
    return partial(process, encrypt), partial(process, decrypt)

# Используем ASCII в качестве алфавита
encrypt, decrypt = get_cypher(ord, chr, 256)

# Либо предоставляем свой словарь d
d = map(chr, range(128))
rd = dict(zip(d, count()))
encrypt, decrypt = get_cypher(rd.get, d.__getitem__, len(d))
Немного странно выглядит формула P = (C — K + 26) mod 26.
Почему не написать P = C — K mod 26?
Это не важно. Для деления по модулю есть замечательное свойство a ≡ a ± n (mod n)
Да, но мы определили лишь соответствие чисел от 0 до 26 некоторым буквам. Любое другое число просто выходит за рамки условия задачи. Хотя в целом вы правы, python поддерживает отрицательные индексы, и сложение можно просто убрать.
Sign up to leave a comment.

Articles