从Julia集的生成看csv二维数据结构

Julia 集简介

朱利亚集是分形几何中的一个重要概念,由法国数学家加斯东·朱利亚研究得出,通过迭代复数平面上的函数 \(f_c(z) = z^2 + c\) 来定义,其中 \(c\) 为固定的复数参数。对于每一个给定的 \(c\) 值,从任意初始点 \(z_0\) 开始进行迭代,如果序列 \({zn}\) 保持有界,则初始点属于该朱利亚集;反之,若序列趋向无穷大,则初始点不属于此集。这些集合并呈现出高度复杂和自相似的边界,其形态随不同的 \(c\) 值变化,从连通区域到复杂的尘埃状分布不等,展现了丰富的几何结构和美丽的图案,既是数学研究的对象,也是艺术创作的源泉。

通过计算机绘制 Julia 集,需要指定一个 \(c\) 值, 然后取一个复平面上的一个区域 \(A\), 对于此区域内的每一点按\(f(z)\)进行迭代, 同时设置一个最大迭代次数 \(N\) 和逃逸半径 \(R\). 对于复平面内的点如果\(|z|>R\), 则取强度为\(0\); 如果 \(|z|<R\), 则使用 \(f(z)\) 迭代, 当迭代 \(m\) 次后点 \(|z_m|>R\), 则取强度为\(m\), 如果迭代\(N\)次后仍然不满足\(|z_N|>R\), 则取强度为\(N\). 如此操作后,对于复平面区域\(A\)内的每一点都赋予一个强度值,然后输出为 csv 的2D数据,按强度上色就构成了一个 Julia 集的图形显示。

代码分析

声明脚本为 python3 , 用 utf-8 编码

1
2
#! /usr/bin/env python3
# vim:fenc=utf-8

调用 numpy 库, 同时设置初值 c, 并定义保存文件的名称 picname

1
2
3
4
5
6
import numpy as np
c = complex(-0.70176, -0.3842)
picname = "Nebula"
file_name = f"{picname}.csv"
width, height = 1000, 1000 # 图像的宽度和高度
max_iter = 100 # 最大迭代次数

绘制程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def julia_set(c, width, height, max_iter):
x = np.linspace(-2, 2, width)
y = np.linspace(-2, 2, height)
escape_time = np.zeros((height, width)) # 注意这里的维度顺序
for i in range(width):
for j in range(height):
z = complex(x[i], y[j])
n = 0
while abs(z) <= 2 and n < max_iter:
z = z*z + c
n += 1
escape_time[j, i] = n # 根据像素位置更新逃逸时间

return x, y, escape_time

上述程序中:

  • 第1行:定义程序julia_set, 并设置了四个参数c, width, heightmax_iter.
  • 第2行:定义x数组,将x定义域\(-2\)\(2\)划分成width等份.
  • 第3行:定义y数组,将y定义域\(-2\)\(2\)划分成height等份.
  • 第4行:定义 \(height\times width\) 矩阵escape_time,所有矩阵元默认取 \(0\).
  • 第5行:遍历x数组, 以取得每一个 \(x\)数组元 \(x[i]\).
  • 第6行:遍历y数组, 以取得每一个 \(y\)数组元 \(y[j]\).
  • 第7行:以\(x[i]\) 为实部, \(y[j]\)为虚部,构建复数\(z\).
  • 第8行:对于每一个点\(z\), 默认逃逸次数为\(n=0\).
  • 第9行:使用while对每一个点\(z\)判断,条件为小于逃逸半径2小于最大逃逸次数 max_iter.
  • 第10行:若满足条件,则迭代一次,同时逃逸次数 \(n\) 增加\(1\), 若仍然满足第9行的条件,则继续循环,同时不断将判断的逃逸次数\(n\)赋值给矩阵escape_time[j,i].
  • 第11行:返回最后的x,y,escape_time 数组.

使用上述函数处理具体的参数

1
2
# 计算Julia集
x_coords, y_coords, escape_time = julia_set(c, width, height, max_iter)

将结果保存为 csv 数据

1
2
3
4
5
6
with open(file_name, 'w') as file:
file.write(',' + ','.join(map(str, x_coords)) + '\n')
for j in range(escape_time.shape[0]):
# 写入每行的数据,先写Y坐标,然后是该行的逃逸时间值
file.write(str(y_coords[j]) + ',' + ','.join(map(str, escape_time[j])) + '\n')
print(f"Julia集已成功保存到{file_name}")

上述程序中:

  • 第1行:打开文件file_name, 同时赋予write权限(即选项w), 将其命名为file, 在后续程序中调用.
  • 第2行:写入x坐标行, 数组以逗号,分隔, 行尾添加换行符\n.
  • 第3行:遍历escape_time数组的第一列(也就是所有行数), 写入每一行的数组,仍然以逗号,分隔,行尾添加换行符\n.
  • 第4行:打印处理结果,同时使用f标记来使用变量{file_name}.

注意:通过这一段程序,我们可以判断出2d数据的具体形式构成. 第1行是x坐标点,第1列是y坐标点,由xy决定的点上则(x,y)所对应的强度值,这是一个二维数据表格,和数学上描点绘图的数据一致。