zipで関数とグラフ
1.関数に式は必要か?
このワークシートはMath by Codeの一部です。
関数は集合Xの要素xから集合Yの要素yへの対応づけだ。
対応がつけばよいだけなのに、対応を式に表すことができると考えてしまいがちだ。
f :: X→Y
f (x) = expr(x)
中学数学で学んだ1次関数、2次関数のイメージが強いためか、
関数というと、y=xの式という、計算操作のイメージがつきまつ。
しかし、結果としてのグラフイメージで関数を俯瞰してみよう。
すると、関数はXとYの直積空間(X-Y座標平面)の要素の点(x,y)のあつまり、部分集合という風にとらえることもできる。
y=2xという関数は、この関数を満たす点と考えると、順序対、タプル(x,y)のリスト
と考えることもできるね。
連続量に近いイメージがほしければ、xの範囲を細かく区切ると良い。
#[IN]julia
#============================
F=[(x,2*x) for x in 1:10]
G=[(x,2*x) for x in 1:0.001:10]
#============================
[OUT]
10-element Vector{Tuple{Int64, Int64}}:
(1, 2)
(2, 4)
(3, 6)
(4, 8)
(5, 10)
(6, 12)
(7, 14)
(8, 16)
(9, 18)
(10, 20)
[OUT]
91-element Vector{Tuple{Float64, Float64}}:
(1.0, 2.0)
(1.1, 2.2)
(1.2, 2.4)
(1.3, 2.6)
(1.4, 2.8)
(1.5, 3.0)
(1.6, 3.2)
(1.7, 3.4)
(1.8, 3.6)
(1.9, 3.8)
(2.0, 4.0)
(2.1, 4.2)
(2.2, 4.4)
⋮
(8.9, 17.8)
(9.0, 18.0)
(9.1, 18.2)
(9.2, 18.4)
(9.3, 18.6)
(9.4, 18.8)
(9.5, 19.0)
(9.6, 19.2)
(9.7, 19.4)
(9.8, 19.6)
(9.9, 19.8)
(10.0, 20.0)
すると、1対1にこだわらなければ、xの剰余を対応させたり、
xの値と無関係に乱数を作ってzipで順序対(タプル)を作ったりと
関数は自由だ。
zipを使うと、関数も作れるし、グラフもすぐかけるね。
#[IN]julia
#============================
H=[(x,x % 3) for x in 1:10]
#============================
[OUT]
10-element Vector{Tuple{Int64, Int64}}:
(1, 1)
(2, 2)
(3, 0)
(4, 1)
(5, 2)
(6, 0)
(7, 1)
(8, 2)
(9, 0)
(10, 1)
#[IN]julia
#============================
xs=Vector(1:10)
ys=rand(10)
z=[(x,y) for (x,y) in zip(xs,ys)]
#============================
[OUT]
10-element Vector{Tuple{Int64, Float64}}:
(1, 0.20169081026514768)
(2, 0.17422295843712776)
(3, 0.25178803264424066)
(4, 0.5264239612886585)
(5, 0.4820281573212978)
(6, 0.23582173409057716)
(7, 0.06452661349364275)
(8, 0.6707651637338825)
(9, 0.3189198173371426)
(10, 0.4016041332227487)
関数と順序対
mod関数 の視覚化
2.暗号解読は逆関数?
<シーザー暗号とは>
シーザー暗号[Cipher]というものがある。
これって、ルールは単純で、
文字xに対して、文字コードy=f(x)を対応させる。
f(x)は数値だから、それに定数sを足す。
そして、文字コードsに対する文字をf-1(x)で求めたものをzとしよう。
これが暗号化関数Cだ。
これを、入力した文字列Xの文字xすべて対してzを求めて出力したものが文字列Zとなる。
つまり、暗号化はC :: X→Zとかけるね。
とうことは暗号解読はその逆関数C-1 ::Z→Xとかくことができるはずだ。
<暗号化と復号化を比べると>
それでは、暗号を作る関数Cと暗号を解読(復号)する逆関数C-1を作ってみようか?。
しかし、この発想には無駄がある。
暗号化のときに、文字コードを+sしているとしたら、
復号化のときは、文字コードをーsすればよいだけだ。
だったら、暗号と復号の両方に使える関数Ciを作った方がよいことがわかるね。
シフト数をshift 、入力文字をxとすると
コード化::Char→Int x -> y = codepoint(x)
シフト:: Int →Int y -> z = y + shift
デコード化:: Int→Char z -> d=Char(z)
この3ステップを合成した関数がCiになるね。
<実装しよう>
#julia
#============================
Int(codepoint('A'))
#============================
[OUT]
65
#============================
Char(65)
#============================
[OUT]
'A':
このように、コード化とデコード化はできることがわかる。
英数字、つまり、アスキー文字に限った暗号だとすると、
コード番号にshiftを加算してずらそう。
ただ、たすのではなく、Aが0番になるようにして、
からshiftをたして、それでも65を超えた番号は
振り出しのAから順にぐるぐる回るようにしたい。
#julia
#============================
Ci(shift::Int, x::Char)::Char = Char((Int(codepoint(x)) - 65 + shift) % 65 + 65)
Ci(0,'A'),Ci(1,'A'),Ci(2,'A'),Ci(3,'A')
#============================
[OUT]
('A', 'B', 'C', 'D')
では、もっと先の文字も含めて、暗号と復号がうまくいくことを確認しよう。
#julia
#============================
Target = "ABCDE PQRSTU abcde,.xyz";
println(Target)
Ciphered=join([Ci(5,x) for x in Target] )
println(Ciphered)
Recovered=join([Ci(-5,x) for x in Ciphered] )
println(Recovered)
#============================
[OUT]
ABCDE PQRSTU abcde,.xyz
FGHIJ%UVWXYZ%fghij13}~
ABCDE PQRSTU abcde,.xyz
#julia
#============================
Target = "THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG";
println(Target)
Ciphered=join([Ci(5,x) for x in Target] )
println(Ciphered)
Recovered=join([Ci(-5,x) for x in Ciphered] )
println(Recovered)
#============================
[OUT]
THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG
YMJ%VZNHP%GWT\S%KT]%OZRUX%T[JW%YMJ%QF_^%ITL
THE QUICK BROWN FOX JUMPS OVER THE LAZY DOG