复杂分形,简单规则:门格海绵世界探秘

article/2025/1/8 5:15:15

连绵的山川、飘浮的云朵、岩石的断裂口、布朗粒子运动的轨迹、树冠、花菜、大脑皮层……这些部分与整体以某种方式相似的形体,可以说,就是“分形”的要义了,也恰恰是这些“不规则的”、“分散的”、“支离破碎的”物体又重新让我们认识了自然。比如,Menger Sponge(Wikipedia),因奥地利数学家卡尔·门格在1926年的描述而得名。它是一个通用曲线,因为它的拓扑维数为一,且任何其它曲线或图都与门格海绵的某个子集同胚。

2017年的夏季学期,金伯顿学校的学生和员工们花费了1000多个小时,建造了一个单人高的3级门格海绵(YouTube 视频)。
Kimbolton School's Level 3 Menger Sponge
下图是纽约数学博物馆提出的展品之一。参观者可以将两块门格海绵分开,发现沿对角线的孔不是正方形,而是六面星形。
“这是一个人们长期研究的众所周知的课题,”博物馆负责人乔治哈特说,“但直到最近,才有人想到用这种有趣的方式来分割它。”
门格海绵在切开时的外观
在国外,门格海绵作为分形世界的 “Super Star”,拥有着独特的理性魅力。数学的智慧真是一座开采不尽的宝藏,它促使人类对身处其中的自然世界产生新的探索与发现。看似复杂的分形图形,实际上的规则却是很简单:

  1. 从一个正方体开始,(第一个图像)把正方体的每一个面分成9个正方形。这将把正方体分成27个小正方体,像魔方一样。
  2. 把每一面的中间的正方体去掉,把最中心的正方体也去掉,留下20个正方体(第二个图像)。
  3. 把每一个留下的小正方体都重复第1-3个步骤。
    门格海绵的迭代过程。图源:Wikipedia
    举个栗子,二级海绵的生成过程:先将最初的大立方体分成大小相等的27个小立方体,并对其进行编号。然后,我们只需要去除掉部分小立方体即可。为了更方便地理解,我绘制如下的草图:
    这里写图片描述
    比较有趣的是,我在 Google 搜到了一份海绵分形 DIY 的教程。前方高能,手残党绕路。
    这里写图片描述
    当在里面装上小灯泡后……
    这里写图片描述
    了解了这些,那我们该如何用 Processing 编写一个海绵分形呢?答案在于小盒子的生成。我们可以写一个 generate() 方法,也是核心方法——
ArrayList<Box> generate() {ArrayList<Box> boxes = new ArrayList<Box>();for (int x = -1; x <= 1; x++) {for (int y = -1; y <= 1; y++) {for (int z = -1; z <= 1; z++) {int sum = abs(x) + abs(y) + abs(z);float newR = r/3;if (sum > 1) {Box b = new Box(pos.x+x*newR, pos.y+ y*newR, pos.z+z*newR, newR);boxes.add(b);}}}}return boxes;}

比如,编号为“1”的盒子,用(x,y,z)表示,即(-1,-1,-1)。而需要去除的盒子,我们 sum = abs(x) + abs(y) + abs(z) 来找寻它们的共同特征。很明显,他们的共性是 sum<=1。最后,我们返回生成的盒子集。

为了让生成的效果更加炫酷,我给盒子的每一个面贴上我很喜欢的一个电影角色——V。理性、博学、浪漫、绅士。即使屠龙的少年终究已经变成了恶龙。
这里写图片描述
“I have no tree waiting for me。”
这里写图片描述
具体实现代码:

/** Menger Sponge* By Hewes* Further reading: Hewes 的编程艺术(https://zhuanlan.zhihu.com/c_123529691)**/float a = 0;
ArrayList<Box> sponge;
PImage tex;void setup() {size(500, 500, P3D);noStroke();textureMode(NORMAL);tex = loadImage("image.png");sponge = new ArrayList<Box>();Box b = new Box(0, 0, 0, 250);sponge.add(b);
}void mousePressed() {// 生成下一个盒子集ArrayList<Box> next = new ArrayList<Box>();for (Box b : sponge) {ArrayList<Box> newBoxes = b.generate();next.addAll(newBoxes);}sponge = next;
}void draw() {background(50);// 光线设置lights();pointLight(0, 0, 139, 0, 0, 0);translate(width/2, height/2);rotateX(a);rotateY(a*0.4);rotateZ(a*0.1);// 显示每一个盒子for (Box b : sponge) {b.show();}a += 0.01;
}void keyPressed(){saveFrame("file_##.png");  // 按下任意键,保存图片
}// 盒子类
class Box {PVector pos;  // 盒子的中心位置float r;  // 盒子的大小Box(float x, float y, float z, float r_) {pos = new PVector(x, y, z);r = r_;}ArrayList<Box> generate() {ArrayList<Box> boxes = new ArrayList<Box>();for (int x = -1; x <= 1; x++) {for (int y = -1; y <= 1; y++) {for (int z = -1; z <= 1; z++) {int sum = abs(x) + abs(y) + abs(z);float newR = r/3;if (sum > 1) {Box b = new Box(pos.x+x*newR, pos.y+ y*newR, pos.z+z*newR, newR);boxes.add(b);}}}}return boxes;}ArrayList<Box> generate() {ArrayList<Box> boxes = new ArrayList<Box>();for (int x = -1; x <= 1; x++) {for (int y = -1; y <= 1; y++) {for (int z = -1; z <= 1; z++) {int sum = abs(x) + abs(y) + abs(z);float newR = r/3;if (sum > 1) {Box b = new Box(pos.x+x*newR, pos.y+ y*newR, pos.z+z*newR, newR);boxes.add(b);}}}}return boxes;}void show() {pushMatrix();translate(pos.x, pos.y, pos.z);box(r);// 如果贴图已经加载,那我们就可以进行盒子的贴图//scale(r/2);//texturedCube(tex);popMatrix();}void texturedCube(PImage tex) {// 构建盒子的形状,并贴图beginShape(QUADS);texture(tex);// +Z 前面vertex(-1, -1, 1, 0, 0);vertex( 1, -1, 1, 1, 0);vertex( 1, 1, 1, 1, 1);vertex(-1, 1, 1, 0, 1);// -Z 后面vertex( 1, -1, -1, 0, 0);vertex(-1, -1, -1, 1, 0);vertex(-1, 1, -1, 1, 1);vertex( 1, 1, -1, 0, 1);// +Y 底面vertex(-1, 1, 1, 0, 0);vertex( 1, 1, 1, 1, 0);vertex( 1, 1, -1, 1, 1);vertex(-1, 1, -1, 0, 1);// -Y 顶面vertex(-1, -1, -1, 0, 0);vertex( 1, -1, -1, 1, 0);vertex( 1, -1, 1, 1, 1);vertex(-1, -1, 1, 0, 1);// +X 右面vertex( 1, -1, 1, 0, 0);vertex( 1, -1, -1, 1, 0);vertex( 1, 1, -1, 1, 1);vertex( 1, 1, 1, 0, 1);// -X 左面vertex(-1, -1, -1, 0, 0);vertex(-1, -1, 1, 1, 0);vertex(-1, 1, 1, 1, 1);vertex(-1, 1, -1, 0, 1);endShape();}
}

效果如下:
这里写图片描述
而就下面的核心代码而言,我们只需要稍加改动…

ArrayList<Box> generate() {ArrayList<Box> boxes = new ArrayList<Box>();for (int x = -b; x <=b; x++) {for (int y = -b; y <=b; y++) {for (int z = -b; z <=b; z++) {int sum = abs(x) + abs(y) + abs(z);float newR = r/c;if (sum < d) {  // 改变 sum 与 d 的大小,会产生意想不到的视觉效果哟!Box b = new Box(pos.x+x*newR, pos.y+ y*newR, pos.z+z*newR, newR);boxes.add(b);}}}}return boxes;}

参数赋值: int b=1, c=3, d=3;
大小关系:sum < d

这里写图片描述


这有些类似于“Jerusalem cube”。
这里写图片描述

参数赋值: int b=2, c=5, d=6;
大小关系:sum < d

这里写图片描述

参数赋值: int b=3, c=7, d=6;
大小关系:sum > d

这里写图片描述

像花椰菜,有木有。

参数赋值: int b=3, c=7, d=6;
大小关系:sum < d

这里写图片描述
那么,一个漂亮的 Menger Sponge 就这样子完成了。期待后期,我们一起学习如何利用 Processing 渲染这些三维模型。倘若你遇到什么问题,同样欢迎你与我一起讨论。


http://chatgpt.dhexx.cn/article/n7RNmBKL.shtml

相关文章

JQData | 高校版使用教程,30秒安装完成,自带Python环境

本地量化金融数据JQData&#xff0c;是聚宽数据团队专门为金融机构、学术团体和量化研究者们提供的本地量化金融数据服务。自有版权&#xff0c;支持国内多家头部券商实盘交易。历经15万量化研究者与数百家机构使用验证。 JQData目前已支持国内30 高校&#xff0c;本次更新&…

JQData + matplotlib 实现回测日志的交易细节可视化 量化数据接口

原文&#xff1a;https://zhuanlan.zhihu.com/p/49051899 前言&#xff1a; 做量化交易的朋友都知道回测的重要性&#xff0c;回测结果是衡量一个量化交易策略是否靠谱的重要依据。回测平台会按历史行情数据模拟成交&#xff0c;并将回测结果汇总成报告。 在很多时候&#xf…

Note: Python学习笔记 -- Anaconda install jqdata

运行代码下列代码&#xff0c;提示错误 没有安装 jqdata。打开Anaconda Prompt 输入 pip install jqdata 提示cannot find command git然后百度了半天&#xff0c;有人说 pip install git 出错可以使用 conda install git 但是依然出错。最后去官网下载了Git:https://git-scm.…

jq使用教程01_最贴心教程,安装JQData全靠这篇指南

Hi, 各位亲爱的小伙伴们&#xff01; 近来听说有部分小伙伴在安装JQData时遇到了点小麻烦&#xff0c;导致最后没有安装成功&#xff0c;为了帮助小伙伴们快速成功安装JQData&#xff0c;小编今天来为大家排一下“雷”&#xff0c;希望能帮到你们哟 (&#xff65;ω&#xff6…

事件驱动的选股小工具(JQData)

昨天发改委下发了《关于积极推进风电、光伏发电无补贴平价上网有关工作的通知》&#xff0c;也不知道对股市是利空还是利多。连夜做了一个搜索公司经营范围的小工具,看看那些股票受到影响。 以后还可以增加筛选条件&#xff0c;比如财务指标&#xff0c;剔除ST股票&#xff0c;…

股票python量化交易008-JoinQuant中JQData的使用

查阅JoinQuant中JQData的使用文档python代码实现导入JQData,并认证用户身份。认证完毕显示“auth success”后即可使用 from jqdatasdk import *; auth(ID,Password);#ID是申请时所填写的手机号;Password为聚宽官网登录密码 # 查询jqdata的调用次数情况 surplus_count = g…

jqdata pyechart: 用grid双图实现k线带图成交 — by QUANTAXIS

from jqdatasdk import * from pyecharts import Kline,Bar,Grid 首先我们先应JQDATA 的活动演示一下如何调用pyecharts 画图 auth(acc,password) dataget_price(000001.XSHE) auth success先打印下 data 我们可以看到 jqdata返回的格式是 一个单index的Dataframe data.he…

Quant | JQData使用API简单梳理(二)

聚宽平台实际上提供了两种查询数据的方法,第一种是线上在聚宽平台可以使用的API:jqdata,另外一种则是本地的接口:JQData,是的,你没有看错,只是大小写的不同,搞得一开始我以为完全是同一种。jqdata可以线上通过import jqdata来引入数据接口,本地的JQData则是通过import…

jqdata(data是什么文件格式)

期货交易中bar和tick是什么意思 Bar 的概念 在一定时间段内的时间序列就构成了一根 K 线(日本蜡烛图)&#xff0c;单根 K 线被称为 Bar。 如果是一分钟内的 Tick 序列&#xff0c;即构成一根分钟 K 线&#xff0c;又称分钟 Bar; 如果是一天内的分钟序列&#xff0c;即构成一根日…

JQData | 量化界最好用的本地量化金融数据(free free~)

什么是本地量化金融数据 - JQData &#xff1f; 使用JQData本地量化金融数据服务&#xff0c;可快速查看、计算或接入金融数据信息&#xff0c;解决本地、web、自研金融终端调用数据的需求。支持python多版本及多操作系统。为财经类企业、金融机构、学术研究机构和量化爱好者们…

获取股票数据【使用JQData查询行情数据、财务指标、估值指标】

了解股票&#xff1a; 在上一次量化小科普【什么是量化&#xff1f;常用的股票量化指标、如何搭建量化交易系统】对于量化的概念有了一个基本认识&#xff0c;其中量化的主体在这门课程的学习中是“股票”&#xff0c;而当别人问你&#xff1a;“什么是股票&#xff1f;”&…

JQData安装的问题(本地调用的量化金融数据接口-免费)

JQData简介(1)JQData是聚宽数据团队专门为有志于从事量化投资的金融机构、研究人员以及个人量化爱好者提供的本地量化金融数据。用户只需在本地Python环境下安装JQData数据包,输入三行代码,即可调用由聚宽数据团队专业生产的全套量化金融数据,让你轻松告别平台限制,灵活安…

JQData安装(转)

首先&#xff0c;JQData是基于python的一个数据包&#xff0c;所以安装JQData的第一步是安装Python &#xff08;没有接触过python或者python基础不好的小伙伴&#xff0c;可以关注聚宽量化课堂的python讲堂进行python学习&#xff09;。 对于python安装包的选择&#xff0c;…

JQData安装的问题(只解决安装的问题)

1. JQData简介 &#xff08;1&#xff09;JQData是聚宽数据团队专门为有志于从事量化投资的金融机构、研究人员以及个人量化爱好者提供的本地量化金融数据。用户只需在本地Python环境下安装JQData数据包&#xff0c;输入三行代码&#xff0c;即可调用由聚宽数据团队专业生产的…

JQData-本地调用的量化金融数据接口(免费)

什么是聚宽数据-JQData? 使用JQData金融数据服务,可快速查看、计算或接入金融数据信息,解决本地、web、自研金融终端调用数据的需求。支持python多版本及多操作系统。为财经类企业、金融机构、学术研究机构和量化爱好者们提供一站式财经信息服务及数据解决方案。 提供哪些…

JQData安装 | 最贴心教程,安装JQData全靠这篇指南

转自 JQData安装 | 最贴心教程&#xff0c;安装JQData全靠这篇指南 Hi, 各位亲爱的小伙伴们&#xff01; 首先&#xff0c;感谢聚宽小编 JQData01 给我开放了更多的数据访问资源 近来听说有部分小伙伴在安装JQData时遇到了点小麻烦&#xff0c;导致最后没有安装成功&#xff0c…

如何理解敏捷开发

目录 什么是敏捷开发 2.0 常用的 4 种开发模式 瀑布式开发 迭代式开发 螺旋式开发 敏捷软件开发 4 种开发模式总结 什么是 DevOps 精益管理的7个原则 DevOps的开发流程 提交 编译 单元测试 部署到测试环境中 预生产测试 部署到生产环境 敏捷开发 2.0 解决的问…

什么是敏捷?什么是敏捷开发?

什么是敏捷&#xff1f; 显示敏捷相互馈送的各个方面的关系图&#xff0c;例如协作、开发和自动化版本控制和部署。 敏捷是一个术语&#xff0c;描述软件开发方法&#xff0c;强调增量交付、团队协作、持续规划和持续学习。 敏捷术语于 2001 年在敏捷宣言中创造。 宣言旨在制定…

瀑布式开发与敏捷开发的区别是什么

摘要&#xff1a;瀑布模型式是最典型的预见性的方法&#xff0c;严格遵循预先计划的需求、分析、设计、编码、测试的步骤顺序进行。敏捷开发以用户的需求进化为核心&#xff0c;采用迭代、循序渐进的方法进行软件开发。我们将在文章中详细说明两者各自的适合场景。 详细解答&am…

【敏捷式开发公司-禅道使用心得】

前言&#xff1a;本人测试小菜鸡一枚&#xff0c;近期入职了一家六七十人的小公司&#xff0c;在我入职之前&#xff0c;他们一直是用Teambition&#xff08;以下简称TB&#xff09;来做项目管理的&#xff0c;包括提交需求、bug管理都是在TB。入职后我也大概使用了一下&#x…