zoukankan      html  css  js  c++  java
  • 希尔伯特曲线及性质的形式化理解

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

    以前对这个曲线的理解停留在感觉上, 不知道极限是什么样子, 一直想从formal定义的角度去考察一下.今天在bilibili找到一个科普视频(https://www.bilibili.com/video/av4201747/, 系列视频都比较有趣, 推荐!), 搞清了其中的内涵.

    n阶的希尔伯特曲线是从([0, 1])区间到([0, 1] imes[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 o infty} f_n(x) ]

    下面将说明这样的定义符合直观理解——填充区间, 曲线.

    1. Hilbert曲线的性质

    良定义

    首先要说明这个定义是well-defined, 即对于所有的(x), ({f_n(x)})确实收敛. 我认为这个可以从区间套来说明. 不管(x)取定义域中的什么值, 都可以不断将区间四等分, 用长度为1/4,1/16,1/64的区间套来套住, 由于不同阶Hilbert曲线的定义, 对应的函数值也落在相应的区域套内. 这样形成一系列闭区域的套, 总有一个确定的极限值.

    这里有个问题就是,当(x)是两个四等分区间的交点时应该取左边的区间继续等分,还是取右边的区间继续等分. 这里应该能够证明取哪个得到的极限都是一样的, 这也是曲线连续性的要求.

    填充整个区间

    是的, Hilbert函数的取值遍布整个单位平面区域. 不信的话在([0, 1] imes[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 imes 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)
    
    
  • 相关阅读:
    PetaLinux 生成 Zynq 操作系统
    单片机、微控制器和微处理器有什么区别
    嵌入式基础概念系列(1) —— GPIO
    学中杂记
    Spring学习笔记
    jdbc一点小笔记
    JSP学习
    Servlet学习的一些笔记
    接触Struts2的ModelDriven<>接口
    android-dialog的位置
  • 原文地址:https://www.cnblogs.com/zzdyyy/p/7636474.html
Copyright © 2011-2022 走看看