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 поддерживает отрицательные индексы, и сложение можно просто убрать.
Это только на беззнаковых типах.
Это только на нормальной реализации.
Only those users with full accounts are able to leave comments. Log in, please.

Please pay attention