绘制函数Python的n次迭代

2024-05-16 11:48:58 发布

您现在位置:Python中文网/ 问答频道 /正文

我在研究动力系统,特别是logistic族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

但这看起来真的很麻烦,我想知道是否有更好的方法。谢谢


Tags: 方法函数importlogreturndefasnp
2条回答

一些不同的选项

这真是一个风格问题。你的解决方案很有效,而且不太难理解。如果你想继续说这些话,我会稍微调整一下:

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,那么最后一个版本看起来会更干净。现在,如果要避免复制矢量,则必须单独计算第一个。)


哪一个最好?我不知道。IMO,都是可读的和合理的,这是一个风格问题。然而,我只说非常破碎和可怜的Python,所以可能有很好的理由为什么还有其他的东西是更好的或为什么上面的东西是不好的!

性能

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

logiter(3.2, linspace(0,1,1000), 10000)

对于前三种方法,时间基本相同,大约为1.5秒。对于最后一种方法(预先分配的数组),运行时间为0.2秒。但是,如果从列表到数组的转换被删除,则第一种方法将在0.16秒内运行,因此时间真正花在转换过程中。

可视化

我可以想出两种有用但却截然不同的方法来可视化函数。你提到你将有,比如说,100或1000个不同的x0开始。你没有提到你想要有多少迭代,但是也许我们将从100开始。因此,让我们在c=3.2处创建一个具有100个不同x0和100个迭代的数组。

data = logiter(3.6, np.linspace(0,1,100), 100)

在某种程度上,可视化函数的标准方法是画100行,每行代表一个起始值。这很简单:

import matplotlib.pyplot as plt

plt.plot(data)
plt.show()

这就提供了:

enter image description here

好吧,似乎所有的价值观最终都会在某个地方振荡,但除此之外,我们只有一团颜色。如果对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)用红色绘制,最后一列用蓝色绘制,并在两者之间使用渐变色。(请注意,点越蓝,画得越晚,因此蓝色与红色重叠。)

enter image description here

然而,有可能采取完全不同的方法。

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()

给出:

enter image description here

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

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

def jam(c, n):

    x = np.linspace(0,1,100)
    y = c*x*(1-x)
    for i in range(n):
        plt.plot(x, y)
        y = c*y*(1-y)

    plt.show()

c = 2

c= 3

c= 3.57

c = 4

相关问题 更多 >