我研究动力系统,尤其是逻辑族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
但这看起来真的很麻烦,我想知道是否有更好的方法。感谢
答案 0 :(得分:0)
一些不同的选项
这真的是一种风格问题。您的解决方案有效并且不是很难理解。如果你想继续这些行,那么我只想稍微调整一下:
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)
变化:
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,都是可读和合理的,这是一个风格问题。但是,我说的只是非常破碎和贫穷的Pythonic,所以可能有充分的理由说明为什么还有其他东西更好或者为什么上面的东西不好呢!
<强>性能强>
关于表现:我的机器尝试了以下内容:
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()
这给出了:
好吧,似乎所有的价值最终都在某个地方振荡,但除此之外我们只有一堆乱七八糟的颜色。如果对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()
给出:
这是我个人的最爱。我通过解释太多而不会破坏任何人的快乐,但IMO很容易表现出这种行为的许多特点。
答案 1 :(得分:0)
这就是我的目标;理解(通过可视化)函数初始条件行为的间接方法g(c,x)= c x (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()