如何使用numpy数组加速分形生成?

时间:2013-06-30 18:36:06

标签: python numpy

这是我使用牛顿方法制作分形的一个小脚本。

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()中递归的内容,它最终返回步数。复平面中的猜测表示图像中的像素,通过收敛的步数来着色。从一个简单的算法,你得到这些美丽的分形。

4 个答案:

答案 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”

此警告也出现在原始的Justin Peel代码中;我不知道如何解决它。我对这个问题很感兴趣。 Newton's Fractal