暗号化ジェネレータ

先日書かれた記事で、海の泥の網が戻ってきた 、私はウィキペディア頻度辞書へのリンクを与えました。 ダウンロードの数は、私の予想を桁違いに超えました。 私は、Habrの読者と大きな精神的な関係を感じました。 ダウンロードされたもの(私のような!) -それから私たちはそれを理解します!



最初の部分については、リクエストがあります。 辞書に興味深いアプリケーションを見つけた場合、またはそのようなアプリケーションのアイデアがあり、これが商業的な秘密ではない場合は、コメントで共有してください。



そして、第2部では、辞書をダウンロードした人たち、そして今、彼らは自分の落ちた幸せをどうするかを痛々しく考えている人たちのために、いくつかの記事を書きたいと思います。 実際、これから始めます。



暗号は、数学的パズルの一種です。 算術恒等式では、各数字は文字(同じ数字-同じ文字で、異なる-異なる)に置き換えられます。 暗号の解決策は、IDが真になるような置換です。 原則として、暗号には1つのソリューションしかありません。



最も有名な暗号法は、1924年に英国の数学者ヘンリー・デュドニーによって発行された「SEND + MORE = MONEY」です。 彼の(唯一の)ソリューションは9567 + 1085 = 10652です。



特定のパズルによっては、暗号を手動で解決するのは非常に難しい場合があります。 しかし、暗号を作成することはさらに困難であるため、いくつかの関連する単語や意味のあるフレーズにさえなります。 少なくとも3桁の数字を掛けてパズルを思い付くには、人の仕事は非常に困難です。



自動暗号化ソルバーについては多くのことが書かれていますが、最新のマシンでは、愚かなブルートフォースソルバーが数行で記述され、かなり許容できる時間で決定されます。



この記事では、大規模な辞書を手にして、美しい暗号を作成するためのアシスタントプログラムを作成する方法を示します。



プログラムは次のように機能します。

この関数は、等号の前に「SEND + MORE」などの暗号の先頭を取得し、1つのソリューションを持つ完全な暗号のリストを出力します。



アルゴリズムのネタバレ説明の下。
アルゴリズム

受け取った暗号化開始のすべての可能な置換をソートします。 1つ以上の数字がゼロから始まる置換は、不要として破棄されます。 置換ごとに、その使用から生じる算術式の結果を計算します。 この結果に置換からの数字がある場合、それらを対応する文字で置き換え、置換の置換からの文字で置き換えることができなかった数字を忘れないでください。 結果のパターンに適合する単語を辞書で探し、途中で見つかった各単語が順列のリストと一致する辞書に記入します。その結果、この単語が判明する可能性があります。 最後に、1回の置換(暗号には1つの解決策しかありません)から生じる可能性のある単語のみを選択して出力します。 頻度で降順で並べ替えて出力することができ、ごみ(およびWikipediaに多くのゴミ)が結果リストの下部に表示されます。



アルゴリズムの1つのステップを検討してください。



ユーザーが入力-Habr * Habr



アルゴリズムは別の置換を試みます。たとえば、x = 7、a = 6、b = 1、p = 8



最初に計算-7618 * 7618 = 58033924



結果の数字には8があり、これは文字pに対応するため、辞書でパターン5033924に一致するすべての単語(または単純に2番目の文字「p」と4番目と5番目の文字が一致する単語)を見つける必要があります。数字5、0、3、9、2、4は文字x、a、bに対応できないことに注意してください。 これらの文字は、すでにそれぞれ数字7、6、1で占められています。



アルゴリズムが機能する場合、1つの置換でのみ取得できる単語のみを残し、特に、* Habr * Habr = trolling、7618 * 7618 = 58033924注意!プログラムの作成者の意見は、実行の結果と一致しない場合があります暗号化)。



アルゴリズムの重要な部分は、巨大な辞書で特定のプロパティを持つ単語を検索することだけです。 これを行うために、辞書をロードするときに、プログラムはいくつかのインデックスを埋めます。



0) word_list-すべての辞書の単語のリスト



1) pattern_index-構造のインデックス。 単語の構造は、この関数によって決定されます。



def pattern(st): d={} rv=[] for l in st: if not(l in d): d[l]=len(d) rv.append(d[l]) return tuple(rv)
      
      







辞書のインデックスは構造体であり、値はすべての単語のセット(より正確には、単語自体ではなく、word_listのインデックス)であり、類似した構造を持っています。 この辞書の助けを借りて、文字の数と配置が指定された単語またはパターンと一致するすべての単語を即座に見つけることができます。



 for i in pattern_index.get(pattern(u''),set()): print words_list[i],
      
      







カラカスカバカコサックバラバン注文ビリビンガラガン収入ドラムドラム注文ウズラ洪水収入ローターカラカルコサックキリキアキリキア注文注文belebey注文ローター引数



2) letters_order_index-文字の場所のインデックス。 キーは、単語の文字とそのシリアル番号です。 意味-この文字が特定の位置に立っているすべての単語のセット(より正確には...わかります)。



 for i in letters_order_index.get((1,u''),set()): print words_list[i],
      
      







Bykovの声明ダイクは、スキーがスピーチであったように見える速歩者でした。



3) letters_existance_index-単語内の文字を見つけるためのインデックス。 キーは文字です。 意味は、すべての単語のセットです(より正確には... itd。)この文字が含まれています。

 for i in letters_existance_index.get((1,u''), set()): print words_list[i],
      
      







発表された発表された結合共役は、食べられない負荷をドライブするために団結した運転を開始し、入口を持ち上げたボリュームの迂回を組み合わせた不可解なエントリを発表しました



なぜなら リストされたインデックスにはセット(セット)が含まれており、セットを使用して、ユニオン、インターセクション、差分などの操作を簡単に実行できます。



ワイルドカードの例で単語を検索する方法が完全に明らかになりました。



最初に、構造が58033924と同じすべての単語のセットを見つけ、次に2番目の文字「p」を持つ単語のセットとの交点を取得します。 「A」および「b」。 最終的に出てくるのは、x = 7、a = 6、b = 1、p = 8を置き換えるための多くの単語です。





そしてその下はプログラムコードです
 import re, itertools import codecs from collections import defaultdict from copy import copy def pattern(st): d={} rv=[] for l in st: if not(l in d): d[l]=len(d) rv.append(d[l]) return tuple(rv) def substitutions_generator(st): words = re.split('[-*+]',st) letters = list(set(''.join(words))) first_letters = set([w[0] for w in words]) for comb in itertools.combinations(range(10),len(letters)): d = dict(zip(letters,comb)) if not any(d[k] == 0 for k in first_letters): yield d def eval_substitution(st,substitution): reverse_substitution = {} for k in substitution: reverse_substitution[str(substitution[k])] = k st = st.replace(k,str(substitution[k])) result = str(eval(st)) tojd = st + "=" + result forbidden = set([]) #,      substitution for k in reverse_substitution: if not(k in result): forbidden.add(reverse_substitution[k]) else: result = result.replace(k,reverse_substitution[k]) return result,tojd,forbidden def gen_indexes(path, limit = None): freq_dict = {} pattern_index = defaultdict(set) letters_order_index = defaultdict(set) words_list=[] letters_existance_index = defaultdict(set) for i,l in enumerate(codecs.open(path,"r","utf-8-sig")): if limit and i>limit:break w,n=l.split() words_list.append(w) index = len(words_list)-1 freq_dict[index]=int(n) pattern_index[pattern(w)].add(index) for k in list(enumerate(w)): letters_order_index[k].add(index) for l in w: letters_existance_index[l].add(index) return words_list, pattern_index, letters_order_index, letters_existance_index, freq_dict def generate_cryptarithm(st, indexes): words_list, pattern_index, letters_order_index, letters_existance_index, freq_dict = indexes d=defaultdict(list) for sub in substitutions_generator(st): res,tojd,forbidden = eval_substitution(st,sub) cur_indexes=copy(pattern_index.get(pattern(res),set([]))) if not cur_indexes: continue for lk in list(enumerate(res)): if not(lk[1] in '0123456789'): cur_indexes&=letters_order_index.get(lk,set([])) for l in forbidden: cur_indexes-=letters_existance_index[l] if cur_indexes: for w in cur_indexes: d[w].append((sub,tojd)) for k in sorted(d.keys(), key = lambda x:freq_dict[x], reverse = True): if len(d[k]) ==1: tojd=d[k][0][1] print "%s=%s,%s"%(st,words_list[k],tojd)
      
      







このモジュールを使用するには、 gen_indexesgenerate_cryptarithmの 2つの関数をインポートできます。



送信するための適切な応答+ more = moneyを生成します。



 # -*- coding: utf-8 -*- from cryptarithm import generate_cryptarithm,gen_indexes indexes = gen_indexes("wiki_freq.txt", 400000) l1=[u'',u'',u'',u''] l2=[u'',u'',u'',u'', u''] for w1 in l1: for w2 in l2: generate_cryptarithm(w1+u'+'+w2,indexes) generate_cryptarithm(w1+u'*'+w2,indexes) generate_cryptarithm(w1+u'-'+w2,indexes)
      
      







多くのオプションがありますが、それは閲覧と選択だけです。



個人的には、私は最も好感が持てます。 まだ= robs、43198-505 = 42693



コードはライセンスされています!



免許

誰でも好きな人を連れて行く あなたがしたいことをしてください-変更、配布、販売。 著者名が不要であることを示します。 しかし、プログラムで何か面白いことをしたり、その使用の結果として興味深い結果が得られた場合は、コメントを書いてください。



All Articles