绘制函数的n次迭代-Python

绘制函数的n次迭代-Python,python,plot,function-composition,Python,Plot,Function Composition,我在研究动力系统,特别是逻辑逻辑族g(x)=cx(1-x),我需要迭代这个函数任意次数来理解它的行为。在给定一个特定点x_0的情况下,迭代函数没有问题,但我想再次绘制整个函数及其迭代,而不仅仅是一个点。对于绘制单个函数,我有以下代码: import numpy as np import scipy as sp import matplotlib.pyplot as plt def logplot(c, n = 10): dt = .001 x = np.arange(0,1.0

我在研究动力系统,特别是逻辑逻辑族g(x)=cx(1-x),我需要迭代这个函数任意次数来理解它的行为。在给定一个特定点x_0的情况下,迭代函数没有问题,但我想再次绘制整个函数及其迭代,而不仅仅是一个点。对于绘制单个函数,我有以下代码:

import numpy as np
import scipy as sp
import matplotlib.pyplot as plt

def logplot(c, n = 10):
    dt = .001
    x = np.arange(0,1.001,dt)
    y = c*x*(1-x)
    plt.plot(x,y)
    plt.axis([0, 1, 0, c*.25 + (1/10)*c*.25])
    plt.show()
我想我可以通过使用如下方法显式创建每个迭代范围的列表来解决这个问题:

def log(c,x0):
    return c*x0*(1-x)

def logiter(c,x0,n):
    i = 0
    y = []
    while i <= n:
        val = log(c,x0)
        y.append(val)
        x0 = val
        i += 1
    return y
logiter(3.2, linspace(0,1,1000), 10000)
def日志(c,x0):
返回c*x0*(1-x)
def登录器(c、x0、n):
i=0
y=[]

而我有一些不同的选择

这确实是一个风格问题。您的解决方案有效,不难理解。如果你想继续讲这些话,我会稍微调整一下:

def logiter(c, x0, n):
    y = []
    x = x0
    for i in range(n):
        x = c*x*(1-x)
        y.append(x)

    return np.array(y)
这些变化:

  • for循环比while循环更容易阅读
  • x0
    未在迭代中使用(这增加了一个变量,但在数学上更容易理解;x0是一个常数)
  • 函数是写出来的,因为它是一个非常简单的单行程序(如果不是,它的名称应该更改为
    log
    ,这很容易与对数混淆)
  • 结果将转换为
    numpy
    数组。(如果我需要策划一些事情,我通常会这么做)
在我看来,这个功能现在已经足够清晰了

您也可以采用面向对象的方法,创建逻辑功能对象:

class Logistics():
    def __init__(self, c, x0):
        self.x = x0
        self.c = c

    def next_iter(self):
        self.x = self.c * self.x * (1 - self.x)
        return self.x
然后,您可以使用:

 def logiter(c, x0, n):
    l = Logistics(c, x0)
    return np.array([ l.next_iter() for i in range(n) ])
或者,如果您可以将其作为发电机:

def log_generator(c, x0):
    x = x0
    while True:
        x = c * x * (1-x)
        yield x

def logiter(c, x0, n):
    l = log_generator(c, x0)
    return np.array([ l.next() for i in range(n) ])
如果您需要性能,并且有大桌子,那么我建议:

def logiter(c, x0, n):
    res = np.empty((n, len(x0)))
    res[0] = c * x0 * (1 - x0)
    for i in range(1,n):
        res[i] =  c * res[i-1] * (1 - res[i-1])
    return res    
这避免了缓慢地转换成
np.array
和复制周围的内容。内存只分配一次,并且避免了从列表到数组的昂贵转换

(顺便说一句,如果返回的数组的第一行是初始的
x0
,那么最后一个版本看起来会更干净。如果希望避免复制向量,那么必须单独计算第一个版本。)


哪一个最好?我不知道。在我看来,所有这些都是可读和合理的,这是风格的问题。然而,我只会说非常蹩脚的Pythonic,所以可能有很好的理由解释为什么还有其他更好的东西,或者为什么上面的东西不好

性能

关于性能:使用我的机器,我尝试了以下方法:

def log(c,x0):
    return c*x0*(1-x)

def logiter(c,x0,n):
    i = 0
    y = []
    while i <= n:
        val = log(c,x0)
        y.append(val)
        x0 = val
        i += 1
    return y
logiter(3.2, linspace(0,1,1000), 10000)
对于前三种方法,时间基本相同,约为1.5秒。对于最后一种方法(预分配阵列),运行时间为0.2秒。但是,如果删除了从列表到数组的转换,则第一个转换将在0.16秒后运行,因此转换过程实际上花费了时间

可视化

我可以想出两种有用但完全不同的方法来可视化函数。您提到您将有,比如说,100或1000个不同的x0开始。您没有提到您想要有多少次迭代,但也许我们将从100次开始。因此,让我们创建一个数组,其中包含100个不同的x0和100个迭代,c=3.2

在某种程度上,可视化函数的标准方法是绘制100条线,每条线代表一个起始值。这很容易:

import matplotlib.pyplot as plt

plt.plot(data)
plt.show()
这使得:

嗯,似乎所有的值最终都会在某个地方振荡,但除此之外,我们只有一堆颜色。如果x0的值范围较窄,则此方法可能更有用:

data = logiter(3.6, np.linspace(0.8,0.81,100), 100)
您可以对起始值进行颜色编码,例如:

color1 = np.array([1,0,0])
color2 = np.array([0,0,1])
for i,k in enumerate(np.linspace(0, 1, data.shape[1])):
    plt.plot(data[:,i], '.', color=(1-k)*color1 + k*color2)
这将以红色绘制第一列(对应于x0=0.80),以蓝色绘制最后一列,并在两者之间使用渐变颜色。(请注意,点越蓝,绘制越晚,因此蓝色与红色重叠。)

然而,可以采取完全不同的方法

data = logiter(3.6, np.linspace(0,1,1000), 50)
plt.imshow(data.T, cmap=plt.cm.bwr, interpolation='nearest', origin='lower',extent=[1,21,0,1], vmin=0, vmax=1)
plt.axis('tight')
plt.colorbar()
给出:


这是我个人的最爱。我不会因为解释得太多而破坏任何人的快乐,但在我看来,这很容易显示出这种行为的许多特点。

以下是我的目标;(通过可视化)理解函数g(c,x)=cx(1-x)初始条件行为的间接方法:


您正在运行哪一版本的Python?你可以在Python中使用
itertools.accumulate
=3.3。谢谢,我对Python一窍不通,所以这是相当有见地的。您建议如何绘制整个迭代函数?ie如何生成和绘制100-1000个不同的x0?这是一个有趣的问题。请看答案末尾的编辑。好吧,我还没有真正理解您编写的函数的更深层次的方面(我想我的意思是,我还没有理解我们正在使用的库的核心),但我确实做到了我最初希望做的事情,我将在“我自己的问题的答案”中附加这些内容,但是谢谢,DrV,就你的贡献而言,它真的很美。事实上,你写的东西是我的“同事”真正想让我写的,我认为它比我刚才写的更有洞察力,所以再次感谢你。