Pythonを使用していくつかの高度なパズル(3-4、4-3、5-1、5-2、5-3、6-5)を解決した方法
概要
このガイドでは、Pythonを使用していくつかの高度なパズルを解いた方法を示します。 もともと私は鉛筆と印刷されたスクリーンショットでパズルを解いていました(3-3の画像のように。甘やかされたくない場合はクリックしてズームしないでください)が、その後の問題のために試行錯誤にうんざりしました。 だから私はPythonにそれをやらせました。
注:コードをSteamコミュニティに投稿するには、リストインデックスの変数の名前をiからxに変更して、マークアップタグとして解析されないようにする必要がありました(私の意見では、マークアップはコード要素内で無視する必要があります)。
3-4:頻度分析
from collections import Counter text = """ AJLPNYRJZJFLZYASGSKQGSMME JKEJPFSVLPJLKKEJELNNSPZKY NNYGNSGASGYZJGAKEYZYGASVW NJKSWSKECNLUJZKEJZEYCYZWS VOEKLGAHYKKJAZEJNYJZLKLGU ESPPJLAFHSPZJLFSVGJRJPYTL OYGJALZMJJKJPZUESSGJPLUEY NATYOEKZLYNEJPKMSEVGAPJAK SGZGLTJEYZCLGYSNL """.replace("\n", "") print(Counter(text))
結果(甘やかされたくない場合はズームしないでください):
多表式換字式暗号(Vigenère暗号)を解くために最初に行うことは、鍵の長さを把握することです。 ゲーム内のヒントによると、(少なくとも)3つの繰り返し3文字シーケンスがあります:DUFとLUE。 それらのそれぞれの出現スパンを数え、次にそれらの最大公約数を取ると、キーの最も可能性の高い長さを取得します:XNUMX。
キーの長さが特定されると、モノアルファベットの換字式暗号の場合と同様に、頻度分析手法を使用できます。 以下のコードは、キーのどの文字が適用されているかに応じて、ciphetextを「フェーズ」に分割し、フェーズ内の文字の頻度をカウントします。
from collections import Counter text = """\ LAFLUIWOYWPADUFHSNBVSWVNDZQDUF RBPLUYQPLWLPHZRLUEDUBSYMIPRDIJ HTYQUCUZYLKFRSKHZBUHULUEKPQFOY LYSSAMWOCWHZOLGDTDDPPOFDDTGOPY UDGWOYOSDRYKVVDVLAULRZYGWPLJZY QKYPTWVLJIAFHHSWOMUVDDAPLMJLUE PVLRNPDWFXWMQAFHZSEQCFAGQDFLJF LHLDSWCLMQLFXUBULBDUBVPVWFQHWY UHRHJGSOCUZZXAGFVLILQVAFDARKPQ LZCQAGULJBUCZAMPL\ """.replace("\n", "") phase1 = "".join(text[x] for x in range(len(text)) if x % 3 == 0) phase2 = "".join(text[x] for x in range(len(text)) if x % 3 == 1) phase3 = "".join(text[x] for x in range(len(text)) if x % 3 == 2) triplets = (text[x - 2] + text[x - 1] + text[x] for x in range(2, len(text), 3)) print(Counter(phase1)) print(Counter(phase2)) print(Counter(phase3)) print(Counter(triplets))
コードはまた、DUFやLUEのような繰り返しの3文字のシーケンスを見つけようとします。 1-2-3以外のフェーズでシーケンスを見つける必要はないと思いました。 残念ながら、平文で「THE」とは何かを見つけるにはそれだけでは不十分でした。
4-3:ブルートフォース攻撃
周波数分析に従っていくつかのシフトを試した後、私は自分のコンピューターを試してみることにしました。
コードはソリューションの候補をいくつか出力するため、正しい候補を手動で選択する必要があります。
from string import ascii_uppercase as alphabets from itertools import product text = """\ LAFLUIWOYWPADUFHSNBVSWVNDZQDUF RBPLUYQPLWLPHZRLUEDUBSYMIPRDIJ HTYQUCUZYLKFRSKHZBUHULUEKPQFOY LYSSAMWOCWHZOLGDTDDPPOFDDTGOPY UDGWOYOSDRYKVVDVLAULRZYGWPLJZY QKYPTWVLJIAFHHSWOMUVDDAPLMJLUE PVLRNPDWFXWMQAFHZSEQCFAGQDFLJF LHLDSWCLMQLFXUBULBDUBVPVWFQHWY UHRHJGSOCUZZXAGFVLILQVAFDARKPQ LZCQAGULJBUCZAMPL\ """.replace("\n", "") def char_to_num(char): return alphabets.index(char) + 1 def vigenere_sub(char1, char2): return alphabets[(char_to_num(char1) - char_to_num(char2) - 1) % 26] for key_chars in product(alphabets, repeat=3): decryption = "" for x in range(len(text)): decryption += vigenere_sub(text[x], key_chars[x % 3]) if "THE" in decryption and "AND" in decryption: print(key_chars) print(decryption) print()
画像の右上部分が示唆しているように、ブルートフォース攻撃の結果、周波数分析でパズルをほぼ解いたことはわかっていましたが、先に進むのに十分な自信がありませんでした。 (甘やかされたくない場合は、画像をズームしないでください。)
5-1, 5-2, 5-3
機械化された暗号パズルの場合、エニグマの機能をエミュレートするコードを書くことができます。 同じコードを使用して、以下を除くすべてを解決できます。
- XNUMX番目のパズルでは、ディスクの初期回転を決定するために、試行錯誤、または少しの力が必要です。
- 最後のパズルでは、文字のペアを入れ替える関数が必要です。
from string import ascii_uppercase as keyboard scrambler1 = (keyboard, "UWYGADFPVZBECKMTHXSLRINQOJ") scrambler2 = (keyboard, "AJPCZWRLFBDKOTYUQGENHXMIVS") scrambler3 = (keyboard, "TAGBPCSDQEUFVNZHYIXJWLRKOM") reflector = (keyboard, "YRUHQSLDPXNGOKMIEBFZCWVJAT") def scramble(position, disk, backwards=False): row1, row2 = disk[::-1] if backwards else disk char = row1[position] return row2.index(char) def rotate(disk, n): return (disk[0][n:] + disk[0][:n], disk[1][n:] + disk[1][:n]) def decrypt(char, *disks): x = keyboard.index(char) for disk in disks: x = scramble(x, disk) for disk in disks[-2::-1]: x = scramble(x, disk, True) return keyboard[x] def decrypt_text(text, *disks): decryption = "" disk1 = disks[0] for char in text: disk1 = rotate(disk1, 1) decryption += decrypt(char, disk1, *disks[1:]) return decryption def swap(text, key): result = "" for char in text: result += key.replace(char, "") if char in key else char return result def swap_keys(text, keys): for key in keys: text = swap(text, key) return text def puzzle_5_1(): print(decrypt_text("ZYDNI", scrambler1, reflector)) def puzzle_5_2(): for i in range(26): scrambler = rotate(scrambler1, i) text = decrypt_text("QHSGUWIG", scrambler, reflector) if text[:2] == "XV": print(text) def puzzle_5_3(): text = "GYHRVFLRXY" keys = ("AB", "SZ", "UY", "GH", "LQ", "EN") text = swap_keys(text, keys) d1 = rotate(scrambler2, scrambler2[0].index("A")) d2 = rotate(scrambler1, scrambler1[0].index("E")) d3 = rotate(scrambler3, scrambler3[0].index("B")) text = decrypt_text(text, d1, d2, d3, reflector) text = swap_keys(text, keys) print(text) puzzle_5_1() puzzle_5_2() puzzle_5_3()
6-5
パズルは単純です。アルゴリズムを逆方向に実行するだけです。 しかし、これを手作業で解決すると、XNUMXつの小さな間違いがすべてを台無しにする可能性があります。 これを解決するためにPythonを使用した(または使用しなければならなかった、と告白する)のはそのためです。
アルゴリズムを再解釈したことに注意してください。テキストとキーの両方に行交換を適用してからそれらをXORする代わりに、命令を最初にそれらをXORし、次に行を交換するものとして解釈しました。結果は次のようになります。同じ。
def block(bitstr): return (bitstr[:4], bitstr[4:8], bitstr[8:12], bitstr[12:]) def swap(rows): return tuple(row[1] + row[0] + row[3] + row[2] for row in rows) def xor(rows, key): keybits = "".join(bin(ord(char))[2:].zfill(8) for char in key) if len(keybits) != 16: raise AssertionError textbits = "".join(rows) val = "".join("0" if keybits[x] == textbits[x] else "1" for x in range(16)) return block(val) def shift(rows): result = [] for n in range(len(rows)): row = rows[n] result.append(row[n:] + row[:n]) return tuple(result) def decrypt(cipher, key): return xor(swap(shift(cipher)), key) ciphertext = block("1001011110110101") first = decrypt(ciphertext, "BX") second = decrypt(first, "YS") print(chr(int(second[0] + second[1], 2))) print(chr(int(second[2] + second[3], 2)))