Расшифровка Python Caesar Cipher

Учитывая этот зашифрованный текст из шифра Цезаря:

fxeyaxklqxhkltkxqebobxtbobxplxjykvxfaflqpxfkxqebxtloiaxrkqfixfxpqyoqbaxrpfkdxqebxfkqbokbq

Задача – расшифровать его без предоставления ключа.

Мое решение:

napis = "fxeyaxklqxhkltkxqebobxtbobxplxjykvxfaflqpxfkxqebxtloiaxrkqfixfxpqyoqbaxrpfkdxqebxfkqbokbq".upper()
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
napis = [ALPHABET.index(i) for i in napis]

for x in range(0,4):
    wynik = [ALPHABET[i+x] if i+x<len(ALPHABET) else ALPHABET[i+x-len(ALPHABET)] for i in napis]
    print("".join(wynik))

вывод:

FXEYAXKLQXHKLTKXQEBOBXTBOBXPLXJYKVXFAFLQPXFKXQEBXTLOIAXRKQFIXFXPQYOQBAXRPFKDXQEBXFKQBOKBQ
GYFZBYLMRYILMULYRFCPCYUCPCYQMYKZLWYGBGMRQYGLYRFCYUMPJBYSLRGJYGYQRZPRCBYSQGLEYRFCYGLRCPLCR
HZG CZMNSZJMNVMZSGDQDZVDQDZRNZL MXZHCHNSRZHMZSGDZVNQKCZTMSHKZHZRS QSDCZTRHMFZSGDZHMSDQMDS
I HAD NOT KNOWN THERE WERE SO MANY IDIOTS IN THE WORLD UNTIL I STARTED USING THE INTERNET

3 ответа
3

  • Не используйте бессмысленные имена, такие как napis и wynik.
  • x и i значимы в некоторых контекстах, но не для того, что вы их используете, поэтому используйте более подходящие названия для них.
  • Вы можете воспользоваться отрицательными индексами, т. Е. Удалить ALPHABET[i+x] if i+x<len(ALPHABET) else и просто используйте ALPHABET[i+x-len(ALPHABET)]. Или, в более общем смысле, ALPHABET[(i+x) % len(ALPHABET)].
  • Вы можете предположить, что пробелы являются наиболее частыми символами, и x = ALPHABET.index(' ') - max(napis, key=napis.count) вместо петли.

  • 4

    Польские слова для вас «бессмысленны», но, вероятно, передают гораздо больше автору (предположительно, польский?). Я не думаю, что справедливо настаивать на том, чтобы все кодили на английском, даже если ключевые слова английские, если только они не собираются делиться этим кодом среди англоговорящих. Для остальных это даже не так сложно: Викисловарь говорит, что они имеют в виду string и result.

    – Тоби Спейт


  • 1

    @TobySpeight Они сделал поделитесь этим кодом между носителями английского языка.

    – отличный дождь

  • 3

    Было бы лучше сказать «Используйте английские имена», чем называть польский «бессмысленным». Просто кажется оскорбительным принижать такой язык в целом.

    – Тоби Спейт


  • 1

    @TobySpeight Я не умаляю культуру. Я не вижу смысла в этих словах и даже проверил их на dictionary.com (потому что я не являюсь носителем английского языка), и есть никаких результатов. Они просят обзора, то есть того, что мы думаем об их коде, и я так думаю.

    – отличный дождь


  • 1

    @TobySpeight Но не для английского языка, это англоязычный сайт, и им не следует ожидать, что мы будем знать польский или узнаем его.

    – отличный дождь

Похоже, что ваше текущее решение потребует, чтобы человек просмотрел все возможные расшифровки и решил, какое из них правильное. Это может сработать, но может и не понадобиться. Программа могла бы это выяснить сама.

Вот пример превосходной идеи Rain предположить, что дешифрование с наибольшим количеством пробелов является правильным:

import string

encrypted_text = "fxeyaxklqxhkltkxqebobxtbobxplxjykvxfaflqpxfkxqebxtloiaxrkqfixfxpqyoqbaxrpfkdxqebxfkqbokbq"

ch_list = string.ascii_lowercase + ' '

def translation_maker(offset, ch_list):
    
    translation_dict = dict()
    
    for ind, ch in enumerate(ch_list):
        translation_dict[ch] = ch_list[(ind + offset) % len(ch_list)]
    
    return str.maketrans(translation_dict)

def translation_generator(text, ch_list):
    
    for ind in range(len(ch_list)):
        for offset in range(len(ch_list)):
            yield text.translate(translation_maker(offset, ch_list))
    
likely_decryption = max(translation_generator(encrypted_text, ch_list), key=lambda x: x.count(' '))

print(likely_decryption)
# a lot of this could be one-lined, but it might maybe considered less readable
# e.g.:
"""
likely_decryption = max(
    (
        encrypted_text.translate(str.maketrans(
            {
                ch: ch_list[(ind + offset) % len(ch_list)]
                for ind, ch in enumerate(ch_list)
            }))
        for offset in range(len(ch_list))
    ),
    key=lambda x: x.count(' '))
"""

В этом случае он печатает правильную строку.

Другое дело, что вы можете сделать большой набор с каждым словом на английском языке и предположить, что правильная расшифровка – это та, которая имеет наибольшее совпадение с набором после того, как вы разделите его на пробел. В основном это будет то же самое, но лямбда-функция будет изменена на это:
key=lambda x: sum(word in word_set for word in x.split()))

Это может быть медленнее, даже если не учитывать создание набора слов, и, очевидно, потребовалось бы больше памяти, но было бы маловероятно, что это даст неправильный результат.

    Я заметил, что ваш код запускается только 4 раза, это работает для текущего ключа поворота зашифрованного текста, но вам может потребоваться увеличить его, чтобы расшифровать его с использованием другого ключа поворота. Кроме того, он печатает случайный зашифрованный текст, пытаясь его найти.

    Другой способ сделать это – использовать словарь или библиотеку проверки орфографии для грубой силы и определения того, какая величина поворота содержит наибольшее количество слов, написанных правильно, а затем только распечатать результат с использованием этого ключа поворота.

    Вот реализация такой идеи:

    # Written in python 3.9
    import re, enchant
    
    ENG_ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZ "
    
    def getInt(char, alphabet):
        return re.search(f"{char}", alphabet).start()
    
    def decrement(char, amount, alphabet, bounds):
        char = char.upper()
        if char in alphabet:
            v = getInt(char, alphabet)    
            for i in range(0,amount):
                if v > 0:
                    v -= 1
                else:
                    v = bounds - 1
            return alphabet[v]
    
    def c_decipher(ciphertext, rotation_amount):
        string = ""
        for char in ciphertext:
            if re.match(r"w", char):
                char = decrement(char, rotation_amount, ENG_ALPHABET, 27)
                string = string + char
        return(string)
    
    def BruteForce(inputtext):
        d = enchant.Dict("en_US")
        MostWords = 0
        BestResult = ""
    
        for rot in list(range(1,len(ENG_ALPHABET)+1)):
            Result = c_decipher(inputtext, rot)
            Words = Result.split()
            TotalWords = 0
    
            for word in Words:
                if d.check(word):
                    TotalWords += 1
                    
            if TotalWords > MostWords:
                MostWords = TotalWords
                BestResult = Result
            
        print(BestResult)
    
    BruteForce("fxeyaxklqxhkltkxqebobxtbobxplxjykvxfaflqpxfkxqebxtloiaxrkqfixfxpqyoqbaxrpfkdxqebxfkqbokbq")
    
    I HAD NOT KNOWN THERE WERE SO MANY IDIOTS IN THE WORLD UNTIL I STARTED USING THE INTERNET
    

    Добро пожаловать в Code Review! Вы представили альтернативное решение, но не просмотрели код. Пожалуйста, отредактируйте, чтобы показать, какие аспекты кода вопроса побудили вас написать эту версию, и в чем она лучше оригинала. Возможно, стоит (пере) прочитать «Как ответить».

    – Тоби Спейт

    Добавить комментарий

    Ваш адрес email не будет опубликован. Обязательные поля помечены *