实现Mandelbrot集PPM图像时math.sin()出现Math Domain Error
核心问题
math.sin()抛出定义域错误的根本原因是传入了**无穷大(inf)**参数。当迭代中z的实部趋向无穷大时,log10(z.real)返回无穷大,log2(inf)也返回无穷大,最终v = n - inf得到负无穷大,而Python的math.sin()不接受无穷大作为参数,因此报错。
导致z趋向无穷大却未及时终止迭代的原因是错误的逃逸条件:你用z.real < 10作为迭代终止条件,而Mandelbrot集的正确逃逸条件是z的模长大于2(abs(z) > 2)。当z的模超过2后,后续迭代会迅速发散到无穷大,应该立即终止迭代。
此外,归一化迭代计数的公式也有误:应该使用z的模长而非实部来计算,否则会出现对数参数为负或无穷大的情况。
修正步骤
修正逃逸条件
将迭代循环的条件从i < maxIts and z.real < 10改为i < maxIts and abs(z) <= 2,确保z模长超过2时立即停止迭代。修正归一化迭代计数公式
正确的基础归一化迭代计数公式为:def getV(z, n): return n + 1 - math.log2(math.log(abs(z)))这里使用z的模长
abs(z),且公式调整为标准形式(n+1是为了让未逃逸点的归一化值为0,与你的逻辑兼容)。修正maxIts类型
迭代次数必须是整数,将maxIts = ask("max iterations? ")改为maxIts = int(ask("max iterations? "))。修复文件关闭操作
文件关闭需要调用方法:newFile.close()(原代码缺少括号,文件无法正确刷新关闭),更推荐使用with语句自动管理文件。
完整修正代码
import math def ask(question): return float(input(question)) maxIts = int(ask("max iterations? ")) size = int(ask("image side length? ")) sideTuple = (round(-size/2), round(size/2)) ppmFile = ["P3", str(size), str(size), "255"] def getV(z, n): # 标准归一化迭代计数公式,使用z的模长 return n + 1 - math.log2(math.log(abs(z))) def addValue(v): # 将sin(v)映射到0-255的RGB值(PPM格式要求整数范围) val = int((math.sin(v) + 1) * 127.5) for _ in range(3): ppmFile.append(str(val)) def iterate(x, y): coord = complex(x, y) i = 0 z = coord # 修正逃逸条件:判断z的模长是否超过2 while i < maxIts and abs(z) <= 2: z = z * z + coord i += 1 # 当迭代未到最大次数时,说明点已逃逸,使用归一化计数 if i < maxIts: addValue(getV(z, i)) else: ppmFile.append("0 0 0") def render(x, y, scale): for j in range(*sideTuple): for i in range(*sideTuple): iterate(4*i/(scale*size)+x, 4*j/(scale*size)-y) render(ask("x position? "), ask("y position? "), ask("zoom? ")) with open("brotPPM.ppm", "w") as newFile: newFile.write(" ".join(ppmFile))
额外说明
原代码中addValue直接存储math.sin(v)的结果,而PPM格式要求RGB值为0-255的整数,因此修正时添加了映射逻辑,将sin(v)的结果从[-1,1]转换为0-255的整数,避免生成无效的PPM文件。
内容的提问来源于stack exchange,提问作者TheGuy888

