希尔伯特曲线及性质的形式化理解

希尔伯特曲线是一条填满整个平面的神奇曲线, 其构造方式是把前一阶的曲线复制四份, 将左下角和右下角的曲线做一个沿对角线的翻转, 然后增加三条线段把这四份连起来.这些曲线的极限就是希尔伯特曲线.

以前对这个曲线的理解停留在感觉上, 不知道极限是什么样子, 一直想从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)

posted @ 2017-10-08 04:35  zzdyyy  阅读(5818)  评论(0编辑  收藏  举报