caesar
new TV
サイクリングと山登りと猫の番組しか映らないTVになっちゃった。そう、10年近く使ったTVが、NHKBSプレミアムしか映らなくなっちゃったんだ。改めて(やむにやまれず)このチャンネルを見てると、いかにNHKが手抜きしてるか、よく分かる。金返せ、再放送を見る為に、視聴税を払ってる積りはないぞ。
ぶつくさ言っててもしょうがない。運よくあべちゃんからおこずかいが出たので、78K(円)のTVを買ったよ。コロナで弱った経済の立て直しです。あぶく銭はさっさと使ってしまいましょう。こういう時は、年金生活者も強気であります。
アンドドロイドTVだった。ぐぐる戦略にしっかりTV屋は協力してるね。トロンはどうしたと言いたいぞ。
電波には、右巻きと左巻きが有る事をセットアップ作業中に知った。空から降ってくる電波は右巻き。ケーブルを流れてくるやつは、左巻きだったかな。どちらにも対応してるとか。そんなの知らなかった。いや、円偏波って事か。昔やったような覚えがあるぞと、電波少年は記憶をたぐっております。
リモコンは、ブルーツゥースで接続。進歩してるね。Wi-Fiも完備。勿論4Kなやつ。これだけ揃って、10年前の約半値だ。女房喜ぶユーチューブも大画面で閲覧可。ただ、USB-HDD内に保管してた秘蔵ビデオは見れない。OSが変わって古いTVのフォーマットを受け付けず。バッサリと初期化ですよ。
記念にpingしてみたぞ。
64 bytes from xxx.xxx.xxx.xxx: icmp_seq=0 ttl=64 time=76.434 ms 64 bytes from xxx.xxx.xxx.xxx: icmp_seq=1 ttl=64 time=10.355 ms 64 bytes from xxx.xxx.xxx.xxx: icmp_seq=2 ttl=64 time=6.086 ms 64 bytes from xxx.xxx.xxx.xxx: icmp_seq=3 ttl=64 time=6.400 ms 64 bytes from xxx.xxx.xxx.xxx: icmp_seq=4 ttl=64 time=10.204 ms 64 bytes from xxx.xxx.xxx.xxx: icmp_seq=5 ttl=64 time=10.579 ms
無線同士なんで、こんなものなのかな。で、次にやる事は port scan かな(をぃ)。何たって相手は、表示器がやたらに大きいパソコンですから。但し入力系は貧弱。
TF-IDF
と思っていたら、ぐぐる様の音声検索が使えるのね。リモコンにマイクが仕込まれていて、PTT(push to talk)宜しく、ボタンを押して、検索語句を喋れば、ぐぐるに思っている事が筒抜けになるぞ。黙っていればしっかりと、証拠に録音までされてしまう。人工知能の恐るべし。
いや、音声認識はまだまだ完成の域に達していない。俺様が喋るズーズー弁風な関西語をちゃんと理解しない。だったら、へいぐぐるとか言った後、クィーンズえんぐりっしゅ語で喋れー。 そんな事したら、尚更混乱して固まってしまうぞ。
手書き文字を認識しましょうとか、猫と犬の写真を区別してみましょうなんて言う、おめでたい例をやってちゃいかんのよ。時代は音声認識、そこから意味の理解へと進んでいるんだな。
んな訳で、人工知能を広く取り扱った本で俯瞰してみた。面白い事例を発見。
スポニチには三振と言う単語の登場回数(TF)が多い。一般の記事には三振なんて単語はあまり登場しない。これをIDFが大きい状態と言うそうな。
よってスポニチ(の野球記事)では、両者の積は大きくなる。それを捉えれば、何も知らないコンピュータでも、ああ野球の記事だと推測出来る。
オイラーに取って目から鱗なのは、IDFの表現。あまり登場しない単語を大きい値で表しておけば、後は掛け算だけで判定出来る。事前に統計を取って計算しておけば、後は演算スピードが速い掛け算だけですんじゃう。解析時間の短縮を狙っているんだな。
そんな事なんで、少し資料を探してみた。
単語の重要度を測る?TF-IDFとOkapi BM25の計算方法とは
これらをちゃんとやろうとしたら、 スーパーコンピュータ「富岳」TOP500、HPCG、HPL-AIにおいて世界第1位を獲得 こんなのを使わないといけないのかなあ? 素人には手が出ないぞ。
ruby cipher
前回やった、String.ordの逆関数は何か? SEE ALSOが出てこないので察してください。
(from ruby core) ------------------------------------------------------------------------ str.ord -> integer ------------------------------------------------------------------------ Returns the Integer ordinal of a one-character string. "a".ord #=> 97
念の為、ri調べって事を強調しておこう。返値はintegerって言ってる。ならば、逆関数はinteger族に有るに違いない。族って言うのはクラスの別称。クラスは大文字で始める約束よと、あの人が言ってた事を思い出す。
=== Implementation from Integer ------------------------------------------------------------------------ int.chr([encoding]) -> string ------------------------------------------------------------------------ Returns a string containing the character represented by the int's value according to encoding. 65.chr #=> "A" 230.chr #=> "\xE6" 255.chr(Encoding::UTF_8) #=> "\u00FF"
それらしいのが出てきた。これを組み合わせれば、数字転換暗号が出来そうだな。やってみるか。
sakae@pen:/tmp$ irb irb(main):001:0> Encoding.find('locale') => #<Encoding:UTF-8> sakae@pen:/tmp$ echo $LANG en_US.UTF-8
ふむ、英語の習慣が身に付いている環境だけど、文字コードは世界標準だ。
それじゃ、簡単な暗号用のスクリプト
sakae@pen:/tmp$ cat enc.rb #!/usr/local/bin/ruby key=12354 hira = gets.chop sz = hira.length for i in 0 .. sz - 1 puts( hira[i].ord - key ) end
そして、こちらは、復号用のスクリプト。両者共、共通鍵を取り合えず内蔵させてる。
sakae@pen:/tmp$ cat dec.rb #!/usr/local/bin/ruby key=12354 an = IO.readlines("|cat") sz = an.length for i in 0 .. sz - 1 printf("%c", (an[i].to_i + key).chr(Encoding::UTF_8) ) end
sakae@pen:/tmp$ cat a.txt 紫陽花や 昨日の誠 今日の嘘
梅雨の季節なので、正岡子規さんの有名な句を借りてきた。(世界一短い三行詩って素敵) これを暗号文にしてみる。
sakae@pen:/tmp$ cat a.txt | ./enc.rb >c.txt sakae@pen:/tmp$ cat c.txt 19689 26171 21103 66 -12322 13798 13731 44 23134 -12322 7816 13731 44 9686
味わいも何も無い、無味乾燥な数値になった。じゃ、復号してみる。
sakae@pen:/tmp$ cat c.txt | ./dec.rb ; echo 紫陽花や 昨日の誠 今日の嘘
ちゃんと復号出来たよ。日本語は、表意文字である漢字を多用するため、文字種が非常に多い。従って、例で挙げたkey値も非常に広い範囲を設定出来る。ちょっとやそっとで復号するのは難しいだろう。ちなみにkey値は、'あ' の文字コードである。
一つ注意が有る。平文にはなるべく半角文字を使うな。敵に解読の手がかりを与えてしまうぞ。 上の例で言うと、語句の区切りに半角のスペースを使っている。それが暗号文では、-12322 って数字になってる。
同じ数値が2箇所に表われている。しかも負の数だ。敵はこれに着目するだろね。何か所も現れるって事は、区切り文字じゃなかろうか。区切り文字と言ったら普通はスペースだろう。後は、これをヒントに辻褄合わせの鍵を推測する。ね、素人にも出来そうででしょ。
だったら、今世間を席巻しちゃった世界標準文字コードのUTFを止めればどうよ。Windowsでお馴染みの SHIFT_JIS
とかUnixで昔流行った EUC_JP
とか、ふみ を書く時に使った ISO_2022
とかでどうよ。
いずれも半角文字はASCIIコードだから、身を隠せないぞ。
そうそう、多用する irb は、裸で使うと ipython の機能に負けている。その最たる機能が補完だ。rubyを離れて20年も経つとクラスやらメソッドなんてすっかり忘れてる。そんな時は補完があれば大助かり。遅ればせながら、下記で起動しよう。冒頭の数文字を入れたらTABすれば良い。忘れてたのを思い出させてくれるぞ。
irb -r irb/completion
caesar
前回やったシーザー暗号。OpenBSDってか *BSD 界隈だけじゃあれなんで、Linux界にも移住と言うか移植してみる事にした。
元のソースはOpenBSDを入れないと手に入らないの? いいえ、そんな事はありません。 src/games/caesar/caesar.c ちゃんと、ピンポイントで、取ってこれます。
移住は、エラーとの闘い(って、程でもないけど)を済ませて
debian:tmp$ cc -g caesar.c -lm debian:tmp$ cat c.txt spwwz pgpcjzyp! hpwnzxp ez esp hzcwo zq nzxafepc dntpynp! debian:tmp$ cat c.txt | ./a.out ; echo hello everyone! welcome to the world of computer science!
どうやら移植完了です。これで終わってはもったいないので、少しコード鑑賞します。
/* adjust frequency table to weight low probs REAL low */ for (i = 0; i < 26; ++i) stdf[i] = log(stdf[i]) + log(26.0 / 100.0);
一見無駄なような変換(しかもlog関数で)してるけど、コメントを読むと、頻度表を調整して、低確率の重みを小さくする なんていう風になってます。
係数の対数を取って、評価値を対数圧縮しるんだな。この係数値は、普通の英文の文字頻度を表している。それと、暗号文の文字頻度を掛け算して、下記のように最大値(のindex)を求めている。
winnerdot = 0; for (try = winner = 0; try < 26; ++try) { /* += 13) { */ dot = 0; for (i = 0; i < 26; i++) dot += obs[i] * stdf[(i + try) % 26]; if (dot > winnerdot) { /* got a new winner! */ winner = try; winnerdot = dot; } }
obsって配列は、暗号文の文字頻度だ。これが大きいとdot値が大きくなり過ぎるんで、係数を控え目にしてるんだな。
ちょっとlogして、係数値を変更してる所に興味があったので、追ってみる。最初は変換前
(gdb) set print array (gdb) p stdf $1 = {8.0999999999999996, 1.3999999999999999, 2.7000000000000002, 3.7999999999999998, 13, :
途中まで変換した図。
(gdb) p stdf $2 = {0.74479041371178378, -1.0106014113453963, -0.35382187495632578, 3.7999999999999998, 13, :
マイナスの値が有るって事は、スレッショルドを log(26.0 / 100.0)で決めてるんだな。
gosh> (log (/ 26.0 100.0)) -1.3470736479666092 gosh> (exp 1.3470736479666092) 3.846153846153846
1以下のログを取ると負数になるんだな。これの絶対値になる元の値を求めると3.8か。と言う事は、3.8以下の頻度はカスです。無視してもいいよって決めてるんだな。いえね、26.0なんて言ういわくありげな数値が有ったので、少々気になったのさ。
このlog関数を使って、演算データが大きくならないようにする技法、それと基準データを乗算して、目的物を見つける方法って、上で出て来た TF-IDF とそっくりだな。IDFの方はめったに出てこない単語を炙り出す為、逆数を取ってるけどね。
こちらは、一番出て来るやつを探したいので、素直に統計データを使えばいいって違いはあるけどね。
haskell cipher
前回、さっと流してしまったhaskellの例題を、咀嚼してみる。ゆっくり噛むと滋養強壮になりますからね。まずは、少し復習をば。なんせ、すぐに忘れる脳になっちゃってますからね。
core technology
例題の中で、重要な役目を果たしているのは、統計データから集めた頻度表と、暗号文字の頻度表が、どれぐらい変位(シフト)してるかを算出する部分。
簡単にカイ二乗検定ってので求まるよって説明してる。統計データには統計ツールで対抗しろって事だな。
chisqr :: [Float] -> [Float] -> Float chisqr os es = sum [((o-e)^2)/e | (o,e) <- zip os es]
暗号文字の頻度配列(os)と統計的な頻度配列(es)を比べてみなさい。誤差を加算して、それが少なければ一致してる可能性が高いとな。
_> chisqr [5,1,2,3,4] [1,2,3,4,5] -- 1項目シフト 17.283335 _> chisqr [1,2,3,4,5] [1,2,3,4,5] -- シフト無し 0.0 _> chisqr [3,4,5,1,2] [1,2,3,4,5] -- 2項目シフト 11.383334
分かり易いように2つの配列の値は同一、但し、項目のシフト有り、無しで試してみた。
現場では、暗号文字の配列がキーの値だけずれた状態になってる。そのずれを、上の関数で見つけようって事だ。
_> chisqr [1,3,3,4,5] [1,2,3,4,5] 0.5 _> chisqr [2,3,3,4,6] [1,2,3,4,5] 1.7
実際は、頻度の傾向が2者共一致するなんて、稀な事だろう。それを想定して、暗号文字頻度を多少いじってみた。項目シフトよりは、検出値が小さいね。値の変動に対しては頑丈な作りになってる事が分かる。
詳しくは下記で。
カイ二乗検定 これは、分かり易い。haskellの関数と全く一緒の事をしてる。
実際の利用場面では、暗号頻度リストを回転して、chisqrを計算。これを繰り変えて、一番少ない値をマークしたシフト数を、鍵として採用してる。上手い方法だね。
main
例題は、実験をやり易いように、関数が置いてあるだけ。こいつにmainを足して、コンパイルすれば、晴れて caesar の haskell版に成るかな。えと、mainでは、crackを呼べばいいんだな。暗号文は外からやって来た汚いやつ。それを平文に変換してから表示すれば良い。
汚いデータは中の方まで波及しない。こんなんでいいのかな。
main :: IO () main = do str <- getContents print $ crack str
そして、haskell-mode中で、コンパイル。
-*- mode: haskell-compilation; default-directory: "/tmp/" -*- HsCompilation started at Fri Jun 26 13:27:15 ghc -Wall -ferror-spans -fforce-recomp -c /tmp/mycipher.hs /tmp/mycipher.hs:36:22-28: warning: [-Wtype-defaults] * Defaulting the following constraints to type `Integer' (Integral b0) arising from a use of `^' at /tmp/mycipher.hs:36:22-28 (Num b0) arising from the literal `2' at /tmp/mycipher.hs:36:28 * In the first argument of `(/)', namely `((o - e) ^ 2)' In the expression: ((o - e) ^ 2) / e In the first argument of `sum', namely `[((o - e) ^ 2) / e | (o, e) <- zip os es]' | 36 | chisqr os es = sum [((o-e)^2)/e | (o,e) <- zip os es] | ^^^^^^^ HsCompilation finished at Fri Jun 26 13:27:16
文句を言われつつも、最終目的地のアプリは作成されず。
-rw-r--r-- 1 sakae wheel 1451 Jun 26 13:09 mycipher.hs -rw-r--r-- 1 sakae wheel 1262 Jun 26 13:27 mycipher.hi -rw-r--r-- 1 sakae wheel 32192 Jun 26 13:27 mycipher.o
ここまでしか、やってくれない仕様なの。ならば、続きはghcでって事かな。
ob$ ghc mycipher.hs [1 of 1] Compiling Main ( mycipher.hs, mycipher.o ) [flags changed] Linking mycipher ... ob$ ls -l mycipher -rwxr-xr-x 1 sakae wheel 2296696 Jun 26 13:34 mycipher*
でけた。
ob$ echo "spwwz pgpcjzyp! hpwnzxp ez esp hzcwo zq nzxafepc dntpynp!" | ./mycipher "hello everyone! welcome to the world of computer science!\n"
動いた。