Arnoldの猫写像

記事の内容


今回はArnoldの猫写像で遊びます. ネコのイラストが雑でイマイチ感情移入できないかもしれませんが, 画像さえあれば計算できるのでぜひ試してみてください. 周期点の証明もそのうち...

Arnoldの猫写像

定義

Arnoldの猫写像は次式で定義されます. 定義式のmod 1は, 実数の小数部分を表します.

【定義: Arnoldの猫写像】

Arnoldの猫写像\(F:\mathbb{R}^2\to \mathbb{R}^2\)を次式で定める:

\begin{equation} F\left( \begin{bmatrix} x_n \\ y_n\end{bmatrix} \right) = \begin{bmatrix}2 & 1 \\ 1 & 1 \end{bmatrix}\begin{bmatrix} x_n \\ y_n\end{bmatrix} \quad (\text{mod} 1) \end{equation}

小数部分が返されるので, \([0,1]\times [0,1]\)に写されます.

画像で遊ぶ

以下の猫のイラストを用います. 猫のイラストが雑でごめんなさい... 是非お好きな画像で試してみてください.

【コード3の実行結果】

まず, 猫写像の"mod 1"の部分を無視して写します. 単純に各点のインデックスを行列で写すだけです. すると, 次のように画像が引き延ばされます.

【コード4の実行結果】

さらに"mod 1"で\([0,1]\times [0,1]\)に写すと, 次のようになります. 猫が... 切り裂かれました...

【コード5の実行結果】

また, 猫写像を何回か作用させると面白い画像になります. 猫写像を383回作用させると, 猫が分身します!!

【コード6の実行結果】

また, 猫写像を767回作用させると, 猫が生き返ります!! よかったね!!

【コード7の実行結果】

周期点

猫は生き返ったということは, 何らかの周期性があるということです.

周期点の確認

実は, 有理数を成分とする点は猫写像の周期点です. 有理数を初期値として何回も作用させると, また戻ってきます. 以下にアニメーションを示します.

【コード8の実行結果】

コード

インポートと関数の定義

【Juliaコード1; インポート】
using Plots
import Images, ImageView, Colors
pyplot()
【Juliaコード2; 関数の定義】
#画像を上下反転
function reversey(arr::Array{Float64,2})
    n,n = size(arr)
    new_arr = zeros(n,n)
    for i in 1:n
        new_arr[n-i+1,:] = arr[i,:]
    end
    new_arr
end

#画像ファイルを読み込む
function cat_gray(filename::String)
    cat_img = ImageView.load(filename)
    gray_img = Colors.Gray.(cat_img);
    img = convert(Array{Float64}, gray_img)
    reversey(img)
end

#Arnoldの猫写像
cat_tmp(x::Union{Int64,Float64}, y::Union{Int64,Float64}) = 2*x+y, x+y

#Arnoldの猫写像
function cat_map(x::Int64, y::Int64, n::Int64)
    x_new, y_new = cat_tmp(x,y)
    1+x_new % n, 1+y_new % n
end

#Arnoldの猫写像
function cat_map2(x::Float64, y::Float64)
    x_new, y_new = cat_tmp(x,y)
    x_new -= floor(x_new)
    y_new -= floor(y_new)
    x_new, y_new
end

#再配置
function new_cat_img(img::Array{Float64,2})
    #画像のサイズ
    n,n = size(img)

    #新たな画像
    new_img = zeros(n,n)

    #画像の点の再配置
    for x in 1:n
        for y in 1:n
            new_x, new_y = cat_map(x,y,n)
            new_img[new_y, new_x] = img[y,x]
        end
    end
    new_img
end

その他のコード

【Juliaコード3; 画像の表示】
#画像の読み込み
img = cat_gray("images/cat2.png")

#元の画像
fig1 = heatmap(img, c=:grays)
#savefig(fig1, "figs-arnoldcat/fig1.png")
【Juliaコード4; 画像の引き延ばし】
#画像のサイズ
n,n = size(img)

#新たな画像
new_img = zeros(2*n,3*n)

#画像の点の再配置
for x in 1:n
    for y in 1:n
        new_x, new_y = cat_tmp(x,y)
        new_img[new_y, new_x] = img[y,x]
    end
end

fig2 = heatmap(new_img, c=:grays)
#savefig(fig2, "figs-arnoldcat/fig2.png")
【Juliaコード5; Arnoldの猫写像の像】
#Arnoldの猫写像
new_img = new_cat_img(img)

fig3 = heatmap(new_img, c=:grays)
#savefig(fig3, "figs-arnoldcat/fig3.png")
【Juliaコード6; 猫の分身】
#Arnoldの猫写像
new_img = new_cat_img(img)

#繰り返し写す
for i in 1:383
    new_img = new_cat_img(new_img)
end

fig4 = heatmap(new_img, c=:grays)
savefig(fig4, "figs-arnoldcat/fig4.png")
【Juliaコード7; 猫の復元】
#Arnoldの猫写像
new_img = new_cat_img(img)

#繰り返し写す
for i in 1:767
    new_img = new_cat_img(new_img)
end

fig4 = heatmap(new_img, c=:grays)
savefig(fig4, "figs-arnoldcat/fig4.png")
【Juliaコード8; 周期点】
#反復回数と保存用配列
n_iter = 30
xs = zeros(n_iter)
ys = zeros(n_iter)

#初期値
xs[1] = 4/7
ys[1] = 5/6

#アニメーション
plot(
    [xs[1]],
    [ys[1]],
    st=:scatter,
    color=:red,
    label=false,
    xlabel="x",
    ylabel="y",
    xlim=[0,1],
    ylim=[0,1],
    markershape=:hex,
    markersize=10,
    markercolor=:red
)
anim = @animate for i in 2:n_iter
    xs[i], ys[i] = cat_map2(xs[i-1], ys[i-1])
    plot!(xs[begin:i], ys[begin:i], label=false, color=:black, markershape=:circle, title="iter=$(i)")
end
gif(anim, "figs-arnoldcat/anim.gif", fps=5)