Доброго дня!
преамбула...
В телеге состою в группе Питон курилка. Периодически помогаю растущим питонам разбираться с их задачками. Какое-никакое, но развлечение и пища для мозгов. Тут один паренек закинул задачку и попросил помочь решить ее.
Задача 10. Истина
Что нужно сделать
К вам попал зашифрованный текст, означающий большую истину для многих программистов на Python. Напишите программу, которая реализует алгоритм дешифровки этого текста. Расшифруйте текст с помощью своей программы, а затем найдите его в интернете.
Текст тут не привожу, он есть ниже в исходниках ниже.
Причем, спустя 10 минут паренек сказал - спасибо, мне коллега помог ее решить и сверканул скриншотом с исходниками. Я увидел матрешку из трех циклов вложенных друг в друга и решил самоутвердится. Для этого сел написать чего-нть компактное и без "матрешек". Тут-то и начались интересные вещи...
Я не стал переписывать реализацию алгоритма шифрования цезаря, а вот второй алгоритм шифрования сдвигом букв, мне показался интересным и "матрешка" была именно в нем.
Ребятки, а задачка-то оказалась не простая. На коленке ее порешать не вышло.. Присмотритесь.
Я тут даже
видосик записал с рассуждениями о решении и демонстрацией подводных камней.
Буду рад услышать ваше мнение, критику и предложения.
У меня остались вопросы по заданию в целом:
1. Как глядя на исходный текст можно было понять, что к нему нужно применить именно алгоритм цезаря?
2. Для того чтобы подобрать шаг смещения в цезаре нужно было иметь вводную типа:
- текст на английском языке
- расшифрованный текст на 100% должен содержать некое слово, например тут есть слово "better"
при таких условиях я мог бы циклично применять к тексту алгоритм цезаря, меняя шаг смещения и на каждой итерации искать в полученном тексте одно или несколько заведомо присутствующих в нем слов. Но как оказалось расшифрованный цезарем текст оказался зашифрован еще одним способом и те слова, что мне могли бы служить признаком успешной расшифровки могут быть(почти наверняка) искорежены вторым слоем шифрования.
т.е. теперь, чтобы понять, что текст окончательно расшифрован мне пришлось бы после каждой итерации расшифровки цезарем сразу применять цикл дешифровки по второму алгоритму и искать заданные слова... Цикл в цикле...
То что это долго, это одна сторона вопроса. Но ответьте мне, как я должен понять, какой тип второго алгоритма шифрования?!!??!
Решения выложенные на просторах интернета откуда-то сразу знают эту информацию. Ладно. Допустим, у меня было не полное задание и где-то в оригинальном действительно была подсказка.
Вероятно в подсказке еще было дано, что строка начинается с заглавной Буквы и заканчивается символом "/". Хотя у меня об этом ничего сказано не было.
Но подводные камни обнаружились не там где ожидались. Оказалось, что даже разбитый на строки текст при сдвиге букв во всех словах строки одновременно, может выдавать забавные и неожиданные результаты. Т.е. часть слов может быть уже собрана, а другая часть еще нет. Например:
Начинаю расшифровывать двигая буквы в каждом слове на одинаковый шаг:
parseS si etterb ntha ense/d 1:
Sparse is better
anth dense/ 2: *** слово "than" сложено неправильно
eSpars si rbette hant /dense 3:
seSpar is erbett than e/dens 4:
rseSpa si terbet ntha se/den 5:
arseSp is tterbe anth nse/de 6:
parseS si etterb hant ense/d 7:
Sparse is better than dense/ 8: ЕСТЬ!
Условие окончания поиска:
1. В первом слове первая буква должна быть заглавной
2. В последнем слове последним символом должен быть символ "/"
все казалось бы просто, но в автоматическом режиме цикл расшифровки остановился бы на втором шаге. При этом одно слово в строке сложилось бы неправильно!!! Вот те раз!
А вот еще одна строка из текста для правильной сборки всех слов в которой требуется аж 20 итераций. Причем первое совпадение наших условий происходит на 9 итерации, но при этом часть слов исковерканы:
If the ementationimpl is rdha to plain-ex (sit a bad a/ide 1:
fI eth lementationimp si ardh ot xplain-e t(si a dba ea/id 2:
If het plementationim is hard to explain- it(s a adb dea/i 3:
fI the mplementationi si dhar ot -explain sit( a bad idea/ 4:
If eth implementation is rdha to n-explai (sit a dba /idea 5:
fI het nimplementatio si ardh ot in-expla t(si a adb a/ide 6:
If the onimplementati is hard to ain-expl it(s a bad ea/id 7:
fI eth ionimplementat si dhar ot lain-exp sit( a dba dea/i 8:
If het tionimplementa is rdha to plain-ex (sit a adb idea/ 9: *** Хотя первое и последнее слова составлены правильно, остальные слова искорежены
fI the ationimplement si ardh ot xplain-e t(si a bad /idea 10:
If eth tationimplemen is hard to explain- it(s a dba a/ide 11:
fI het ntationimpleme si dhar ot -explain sit( a adb ea/id 12:
If the entationimplem is rdha to n-explai (sit a bad dea/i 13:
fI eth mentationimple si ardh ot in-expla t(si a dba idea/ 14:
If het ementationimpl is hard to ain-expl it(s a adb /idea 15:
fI the lementationimp si dhar ot lain-exp sit( a bad a/ide 16:
If eth plementationim is rdha to plain-ex (sit a dba ea/id 17:
fI het mplementationi si ardh ot xplain-e t(si a adb dea/i 18:
If the implementation is hard to explain- it(s a bad idea/ 19: ЕСТЬ!
В общем, я не придумал универсального способа полностью автоматического декодирования текста. Предложите какие-нть варианты решения задачки. Ниже провожу свой вариант решения задачки с диалоговым режимом. :-)
# Max Chubin 2022-04-16
# алгоритм цезаря
def encrypt(message, key):
translated = ''
for symbol in message:
if symbol in LETTERS:
num = LETTERS.find(symbol)
translated = translated + LETTERS[num - key]
else:
translated = translated + symbol
return translated
# исходный текст
txt = '''
vujgvmCfb tj ufscfu ouib z/vhm jdjuFyqm jt fscfuu uibo jdju/jnqm fTjnqm tj scfuuf ibou fy/dpnqm yDpnqmf jt cfuufs boui dbufe/dpnqmj uGmb tj fuufsc ouib oftufe/ bstfTq jt uufscf uibo otf/ef uzSfbebcjmj vout/dp djbmTqf dbtft (ubsfo djbmtqf hifopv up csfbl ifu t/svmf ipvhiBmu zqsbdujdbmju fbutc uz/qvsj Fsspst tipvme wfsof qbtt foumz/tjm omfttV mjdjumzfyq odfe/tjmf Jo fui dfgb pg hvjuz-bncj gvtfsf fui ubujpoufnq up ftt/hv Uifsf vmetip fc pof.. boe sbcmzqsfgf zpom pof pvt..pcwj xbz pu pe ju/ Bmuipvhi uibu bzx bzn puo cf wjpvtpc bu jstug ttvomf sfzpv( i/Evud xOp tj scfuuf ibou /ofwfs uipvhiBm fsofw jt fopgu cfuufs boui iu++sjh x/op gJ ifu nfoubujpojnqmf tj eibs pu mbjo-fyq tju( b bec /jefb Jg fui foubujpojnqmfn jt fbtz up bjo-fyqm ju znb cf b hppe jefb/ bnftqbdftO bsf pof ipoljoh sfbuh efbj .. fu(tm pe psfn gp tf"uip/
'''
# используемый в шифровании набор символов
LETTERS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'
# удаление дублей в списке
def clean_doubles(param):
clean_list = []
counter = 0
for item in param:
if item in clean_list:
counter += 1
continue
else:
clean_list.append(item)
return clean_list
# дешифровка слов методом сдвига букв по принципу стэка
def shift(text, iterations = 35):
txt = text.split(' ') # разбиваем исходный текст по словам
# ищем слова со слэшами. по условию строка имеет вид "Beautiful is better than ugly/"
# т.е. слэш должен стоять в конце последнего слова. добавляем перевод строки.
txt = ' '.join(map(lambda x: x+'\n' if '/' in x else x, txt))
txt = txt.split('\n') # снова разбиваем текст уже по переводам строк
result = ''
for line in txt: # перебираем строки
words = line.split() # разбиваем строки на слова
new_line = ''
key = 1 # величина сдвига букв в слове
if len(words) == 0:
continue
results_list = []
while key < iterations:
words_new = list(map(lambda x: x[-key%len(x):] + x[:-key%len(x)], words)) # сдвиг букв на шаг key
# print(' '.join(words_new), key, end='')
# if ' ' in input(':'):
# key += 1
# continue
# если в первом слове первая буква заглавная и в последнем слове в конце символ '/'
if words_new[0][0].isupper() and words_new[-1].endswith('/'):
results_list.append(' '.join(words_new)) # добавляем этот вариант строки в список вариантов
key += 1
clean_list = clean_doubles(results_list) # чистим дубли в списке
select = 0
if len(clean_list) != 1:
print('Выберите строку, которая не содержит ошибок:')
for num, item in enumerate(clean_list):
print(num, ':', item)
while True: # добиваемся от пользователя выбора доступного варианта из списка предложенных
select = input(':')
if select.isnumeric():
select = int(select)
if 0 <= select < len(clean_list):
break
result += clean_list[select] + '\n' # собираем новый дешифрованный текст
return result
if __name__ == "__main__":
# дешифровка цезарем
text = encrypt(txt, 1)
print(text)
# дешифровка сдвигом
result = shift(text)
print(result)