这是我使用牛顿方法制作分形的一个小脚本。
import numpy as np
import matplotlib.pyplot as plt
f = np.poly1d([1,0,0,-1]) # x^3 - 1
fp = np.polyder(f)
def newton(i, guess):
if abs(f(guess)) > .00001:
return newton(i+1, guess - f(guess)/fp(guess))
else:
return i
pic = []
for y in np.linspace(-10,10, 1000):
pic.append( [newton(0,x+y*1j) for x in np.linspace(-10,10,1000)] )
plt.imshow(pic)
plt.show()
我正在使用numpy数组,但是仍然循环遍历1000 x 1 1 linspaces的每个元素以应用newton()
函数,该函数作用于单个猜测,而不是整个数组。
我的问题是:如何改变我的方法以更好地利用numpy数组的优势?
P.S。:如果您想在不等待太久的情况下尝试代码,最好使用100×100。
额外背景:
请参阅Newton的方法以查找多项式的零
分形的基本思想是测试复平面中的猜测并计算迭代次数以收敛到零。这就是newton()
中递归的内容,它最终返回步数。复平面中的猜测表示图像中的像素,通过收敛的步数来着色。从一个简单的算法,你得到这些美丽的分形。
答案 0 :(得分:5)
我使用Lauritz V. Thaulow的代码工作,并通过以下代码获得了相当显着的加速:
import numpy as np
import matplotlib.pyplot as plt
from itertools import count
def newton_fractal(xmin, xmax, ymin, ymax, xres, yres):
yarr, xarr = np.meshgrid(np.linspace(xmin, xmax, xres), \
np.linspace(ymin, ymax, yres) * 1j)
arr = yarr + xarr
ydim, xdim = arr.shape
arr = arr.flatten()
f = np.poly1d([1,0,0,-1]) # x^3 - 1
fp = np.polyder(f)
counts = np.zeros(shape=arr.shape)
unconverged = np.ones(shape=arr.shape, dtype=bool)
indices = np.arange(len(arr))
for i in count():
f_g = f(arr[unconverged])
new_unconverged = np.abs(f_g) > 0.00001
counts[indices[unconverged][~new_unconverged]] = i
if not np.any(new_unconverged):
return counts.reshape((ydim, xdim))
unconverged[unconverged] = new_unconverged
arr[unconverged] -= f_g[new_unconverged] / fp(arr[unconverged])
N = 1000
pic = newton_fractal(-10, 10, -10, 10, N, N)
plt.imshow(pic)
plt.show()
对于N = 1000,使用Lauritz的代码获得11.1秒的时间,使用此代码获得1.7秒的时间。
这里有两个主要的加速。首先,我使用meshgrid来加速创建numpy输入值数组。当N = 1000时,这实际上是加速的重要部分。
第二次加速来自于仅对未收敛部分进行计算。 Lauritz之前正在使用蒙面阵列,然后才意识到它们正在减慢速度。我在很长一段时间没有看过它们,但我确实记得蒙面阵列在过去是一个缓慢的来源。我相信这是因为它们主要是在纯粹的Python上通过numpy数组实现,而不是几乎完全用C语言写成numpy数组。
答案 1 :(得分:3)
这是我对它的刺痛。它快了大约16倍。
import numpy as np
import matplotlib.pyplot as plt
from itertools import count
def newton_fractal(xmin, xmax, ymin, ymax, xres, yres):
arr = np.array([[x + y * 1j for x in np.linspace(xmin, xmax, xres)] \
for y in np.linspace(ymin, ymax, yres)], dtype="complex")
f = np.poly1d([1,0,0,-1]) # x^3 - 1
fp = np.polyder(f)
counts = np.zeros(shape=arr.shape)
for i in count():
f_g = f(arr)
converged = np.abs(f_g) <= 0.00001
counts[np.where(np.logical_and(converged, counts == 0))] = i
if np.all(converged):
return counts
arr -= f_g / fp(arr)
pic = newton_fractal(-10, 10, -10, 10, 100, 100)
plt.imshow(pic)
plt.show()
我不是一个笨拙的专家,我相信那些可以使它更优化的东西,但已经在速度方面取得了巨大的进步。
编辑:事实证明,蒙面阵列根本没有帮助,删除它们会导致15%的速度增加,所以我删除了蒙面阵列从上面的解决方案。任何人都可以解释为什么蒙面阵列没有帮助吗?
答案 2 :(得分:3)
我对牛顿函数进行了矢量化并获得了约。 200x200点快85倍,500x500点快144倍,1000x1000点快148倍:
import numpy as np
import matplotlib.pyplot as plt
f = np.poly1d([1,0,0,-1]) # x^3 - 1
fp = np.polyder(f)
def newton(i, guess):
a = np.empty(guess.shape,dtype=int)
a[:] = i
j = np.abs(f(guess))>.00001
if np.any(j):
a[j] = newton(i+1, guess[j] - np.divide(f(guess[j]),fp(guess[j])))
return a
npts = 1000
x = np.linspace(-10,10,npts)
y = np.linspace(-10,10,npts)
xx, yy = np.meshgrid(x, y)
pic = np.reshape(newton(0,np.ravel(xx+yy*1j)),[npts,npts])
plt.imshow(pic)
plt.show()
答案 3 :(得分:0)
好的,我已经解决了Justin Peel代码中的无限循环,在代码中添加了最大迭代条件,现在代码绘制了多项式,如z ^ 4-1,并且它不会进入无限循环。如果有人知道如何改进此错误,请告诉我们。我的解决方案,可能会减慢代码的执行速度,但它可以工作。 这是代码:
#!/usr/bin/python
# -*- coding: utf-8 -*-
import numpy as np
import itertools
import matplotlib.pyplot as plt
__author__ = 'Tobal'
__version__ = 1.0
def newton_fractal(f, xmin, xmax, ymin, ymax, xres, yres, tolerance, maxiters):
yarr, xarr = np.meshgrid(np.linspace(xmin, xmax, xres), np.linspace(ymin, ymax, yres) * 1j)
arr = yarr + xarr
ydim, xdim = arr.shape
arr = arr.flatten()
fp = np.polyder(f, m=1)
counts = np.zeros(shape=arr.shape)
unconverged = np.ones(shape=arr.shape, dtype=bool)
indices = np.arange(len(arr))
iters = 0
for i in itertools.count():
f_g = f(arr[unconverged])
new_unconverged = np.abs(f_g) > tolerance
counts[indices[unconverged][~new_unconverged]] = i
if not np.any(new_unconverged) or iters >= maxiters:
return counts.reshape((ydim, xdim))
iters += 1
unconverged[unconverged] = new_unconverged
arr[unconverged] -= f_g[new_unconverged] / fp(arr[unconverged])
pic = newton_fractal(np.poly1d([1., 0., 0., 0., -1.]), -10, 10, -10, 10, 1000, 1000, 0.00001, 1000)
plt.imshow(pic, cmap=plt.get_cmap('gnuplot'))
plt.title(r'$Newton^{\prime} s\;\;Fractal\;\;Of\;\;P\,(z) =z^4-1$', fontsize=18, y=1.03)
plt.tight_layout()
plt.show()
我正在使用Pycharm 5和Anaconda Python 3,这个IDE在代码而不是np.any(new_unconverged)
中报告警告预期'类型联盟[ndarray,iterable]',取而代之的是“bool”