希爾伯特曲線及性質的形式化理解


希爾伯特曲線是一條填滿整個平面的神奇曲線, 其構造方式是把前一階的曲線復制四份, 將左下角和右下角的曲線做一個沿對角線的翻轉, 然后增加三條線段把這四份連起來.這些曲線的極限就是希爾伯特曲線.

以前對這個曲線的理解停留在感覺上, 不知道極限是什么樣子, 一直想從formal定義的角度去考察一下.今天在bilibili找到一個科普視頻(https://www.bilibili.com/video/av4201747/, 系列視頻都比較有趣, 推薦!), 搞清了其中的內涵.

n階的希爾伯特曲線是從\([0, 1]\)區間到\([0, 1]\times[0, 1]\)平面區域的映射\(f_n\), 把0和1映射到區域左下角和右下角:

\[f_n(0)=(0, 0), \quad f_n(0)=(1, 0) \]

並且, 通過適當的調整,讓每個1/4的小區間映射到4個區域內.

填充整個區域的希爾伯特曲線是這樣的函數\(f\), 使得函數列\(f_n\)逐點收斂到它. 即:

\[f(x) := \lim_{n\to \infty} f_n(x) \]

下面將說明這樣的定義符合直觀理解——填充區間, 曲線.

1. Hilbert曲線的性質

良定義

首先要說明這個定義是well-defined, 即對於所有的\(x\), \({f_n(x)}\)確實收斂. 我認為這個可以從區間套來說明. 不管\(x\)取定義域中的什么值, 都可以不斷將區間四等分, 用長度為1/4,1/16,1/64的區間套來套住, 由於不同階Hilbert曲線的定義, 對應的函數值也落在相應的區域套內. 這樣形成一系列閉區域的套, 總有一個確定的極限值.

這里有個問題就是,當\(x\)是兩個四等分區間的交點時應該取左邊的區間繼續等分,還是取右邊的區間繼續等分. 這里應該能夠證明取哪個得到的極限都是一樣的, 這也是曲線連續性的要求.

填充整個區間

是的, Hilbert函數的取值遍布整個單位平面區域. 不信的話在\([0, 1]\times[0, 1]\)里面隨便選一個點\((x, y)\), 將平面不斷四等分為上下左右四個閉區域, 用同樣的方法, 能對應到定義域里的閉區間, 最后套出一個自變量\(x_0\)來, 使得\(f(x_0) = (x,y)\).

這里要是選擇的點落在邊界上應該選哪個區域繼續四等分呢? 這時選不同的點就不一樣了. 比如(1/2,0)點,其實會有左右兩個\(x\),都能逼近這個點. 這恰恰說明, Hilbert曲線, 是滿射(映上的), 不是單射(1-1的), 所以也不是雙射.

仍然是曲線

曲線要求是\([0,1]\)\(\mathbb{R}^2\)上的連續映射. 這里的連續性還比較好說. 對於值域中的點\((x,y)\), 選擇一個任意小的\(\epsilon\)鄰域, 都可以在里面找到更小的\(1/4^k \times 1/4^k\)大的(對齊的)閉區域, 對應到定義域是一個閉區間, 然后找到更小的\(\delta\)開區間, 這里的所有點都會映射到\(\epsilon\)領域中.

因為Hilbert曲線不是單射, 故不存在逆映射. 不能說Hilbert曲線讓直線段和平面區域拓撲同胚了.

2. 應用

有了填滿單位區域上的曲線, 將它螺旋填充就能找到填滿整個平面的曲線了.

這里找到了一個滿射, 說明集合\(\mathbb{R}\)的勢至少和\(\mathbb{R}^2\)一樣大. 其實這兩個是等勢的. 不過Hilbert不是一個雙射. 確實存在這兩個集合的雙射, 好像也有人也證明了這兩者的雙射也不會連續.

這里的Hilbert曲線彎曲太多, 有了無窮大的長度, 甚至都占據了面積. 這有點分形的味道. 在上面的視頻里也有更多說明. 感興趣的可以去看看.

視頻里面還說到一種神奇的應用: 通過耳朵來產生視覺, 腦洞很大很有趣.

3. 附錄

把Hlibert曲線着色以后是這樣的, 從紫色到藍色再到綠色和黃色, 說明了自變量\(x\)不斷增大:

本文中兩圖的生成腳本如下:

from matplotlib import pyplot as plt
import numpy as np
import math

# generate psedo Hilbert curve - the function from 1d to 2d
order = 11
phc = [
    [[0,0]]  # order 0
]
for o in range(order):
    new_phc = []
    new_phc += [[y, x] for x, y in phc[o]]  # left bottom
    new_phc += [[x, y + 2**o] for x, y in phc[o]]  # left top
    new_phc += [[x + 2**o, y + 2**o] for x, y in phc[o]]  # right top
    new_phc += [[2**o - 1 - y + 2**o, 2**o - 1 - x] for x, y in phc[o]]  # right bottom
    phc.append(new_phc)

# plot these curves
for o in range(order):
    fig = plt.figure(o, figsize=(6,6))
    plt.axis('off')
    plt.plot(
        list(map(lambda p:p[0], phc[o+1])),
        list(map(lambda p:p[1], phc[o+1]))
    )
    fig.savefig('order_{}.png'.format(o+1))
    if o+1 != order:
        plt.close(fig)
plt.show()

# colorize the pixels in order, for visualization
size = 2**order
imax = size*size
image = [[0]*size for i in range(size)]
i = 0
for x,y in phc[order]:
    image[size-1-y][x] = i
    i += 1

plt.imshow(image)
plt.show()
plt.imsave('hilbert.png', image)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM