根据其中一段视频重建Veritasium的情节
问题描述
灵感来自Veritasium YouTube的一个视频,他在视频中解释了混沌分岔映射(Logistic映射)。数学方程式很简单:X[i+1]=R*X[i](1-X[i])
他绘制的第一个图:这个X[i]
值(y轴值,在0到1的范围内)与迭代时间i
(x轴值,计算次数)具有某个R值(比如R_init=2,我想在代码中加入一个滑块来更改R值)
第二个图(分叉图)他绘制了:值R(x轴值)与X[i]的平衡总体,即:它在其间振荡的X[i]值的数目(取决于R值,在一定/多次迭代i之后,X[i]可以在有限数和无限数之间振荡--混沌!)
最终,在代码中,我想要两个并排的子图,左边是第一个图形";,使用这两个变量的Slider,右边是";第二个图形";。 下面我粘贴了我的入门代码,其中我只尝试使用R的Slider和初始值X[0]来实现绘制第一个图形和X[0](但是,当然,到目前为止它没有显示任何内容。) 如果有人能帮我完成这个项目,或者就我有问题的代码给我一些建议,我将不胜感激。import numpy as np
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
R = 1.5
x = np.linspace(0, 100, 1)
k_init = 0.4 # initial y value
fig = plt.figure(figsize=(8, 8))
ax = plt.axes([0.125, 0.15, 0.775, 0.80])
myplot, = plt.plot(0, 0, c="royalblue") # not really ploting anything first
plt.xlim(0, 100)
plt.ylim(0, 1)
# create slider panel & values sets
slider_r = plt.axes([0.125, 0.03, 0.775, 0.03])
slider_k = plt.axes([0.125, 0.07, 0.775, 0.03])
r_slider = Slider(slider_r, "R", 1, 20, valinit=R, valstep=0.1)
k_slider = Slider(slider_k, "k_init", 0, 1, valinit=k_init, valstep=0.05)
k = [] # y-values starts with List items
def update(*args):
k.append(k_init)
for i in np.arange(1,100,1):
k_new = R*(k[i-1])*(1-(k[i-1]))
k.append(k_new)
y = np.array(k)
myplot.set_xdata(x)
myplot.set_ydata(y)
r_slider.on_changed(update)
k_slider.on_changed(update)
update()
plt.show()
解决方案
简略回答
- 将
x = np.linspace(0, 100, 1)
替换为x = np.arange(100)
- 删除
k = []
- 在
update
函数开头添加k0, R = k_slider.val, r_slider.val
- 将
k.append(k_init)
替换为k = [k0]
详细答案
1.获取运行(没有动画)的模拟
首先,让我们尝试获得一个没有滑块的工作图。以下是一些提示和需要解决的问题:
x = np.linspace(0, 100, 1)
只包含一个元素,而y
将包含100个值!- 由于
y
的最终大小已知,请将其初始化为空数组,而不是向列表k
添加项。这将使代码更具可读性,速度更快。 - 使用变量存储常量值,如纪元数。
- 在问题描述中使用变量
x
,而在实现中使用变量y
ansk
:定义有意义的符号,并坚持使用。 - 定义
r_slider
和slider_r
这样相似的名称比较容易混淆,特别是这两个变量存储的对象完全不同。
## define constants
R = 2.6
X0 = 0.5 # initial value
N = 75 # number of epochs
## logistic map iterates
def get_logistic_map(x0, R, N):
x = np.empty(N)
x[0] = x0
for i in range(1, N):
x[i] = R * x[i-1] * (1 - x[i-1])
return x
x = get_logistic_map(X0, R, N)
## plot
plt.figure(figsize=(7, 4))
plt.plot(x, 'o-', c="royalblue", ms=2)
plt.ylim(0, 1)
plt.margins(x=.01)
plt.grid(c="lightgray")
plt.xlabel(r"$n$")
plt.ylabel(r"$x_n$")
plt.show()
2.让我们引入与滑块的交互
在函数中update
:
- 您应该使用滑块的新值更新显示的数据。通常,当更新滑块时,它会将其新值作为参数提供给回调函数(这里是
update
函数)。因为这里有两个滑块链接到相同的更新函数,所以您不能使用此输入参数。但是,您可以使用每个滑块的属性val
访问这些值。(由于此类回调函数需要参数,因此您可以适当使用*args
;)。 - 我不确定是不是故意的,但是
k
是一个全局列表。因此,每次调用100个update
时,都会添加新的值。
## Prepare figure layout
fig = plt.figure(figsize=(7, 5))
ax = plt.axes([0.125, 0.15, 0.775, 0.80])
line, = plt.plot(range(N), np.zeros(N), 'o-', c="royalblue", ms=2)
# set the layout of the main axes before defining the axes of the sliders
plt.ylim(0, 1)
plt.xlim(0, N-1)
## Create sliders
ax_slider_x0 = plt.axes([0.125, 0.03, 0.775, 0.03])
ax_slider_r = plt.axes([0.125, 0.07, 0.775, 0.03])
slider_x0 = Slider(ax_slider_x0, r"$x_0$", 0, 1, valinit=X0)
slider_r = Slider(ax_slider_r, r"$R$", 0, 4, valinit=R)
# plt.sca(ax) # uncomment to set the main axes as the current one
def update(*args):
x0_val, r_val = slider_x0.val, slider_r.val
x = get_logistic_map(x0_val, r_val, N)
line.set_ydata(x)
# Set the title on the main axes (plt.title would have added a
# title on the current axes (by default the last one to be defined)
ax.set_title(rf"Logistic map with $R$={r_val:.3f} and $x_0={x0_val:.3f}$")
slider_x0.on_changed(update)
slider_r.on_changed(update)
update() # initialize the plot
plt.show()
个人提示-要使用滑块调试回调函数,很容易散布一些
3.下一步是什么?
现在第一个情节是交互式的。对于分叉图,我看不到可以添加的交互性。
相关文章