
罗斯勒吸引子(Rössler Attractor)
1. 理论基础与数学模型
1.1 罗斯勒系统简介
罗斯勒吸引子是德国科学家奥托·罗斯勒(Otto Rössler)于1976年提出的一种混沌系统,是继洛伦兹吸引子之后第二个被发现的混沌吸引子。相比洛伦兹吸引子的双涡卷结构,罗斯勒吸引子具有更简洁的数学形式,但同样能产生丰富的混沌动力学行为。
1.2 微分方程描述
罗斯勒系统的控制方程为:
dx/dt = -y - z
dy/dt = x + a*y
dz/dt = b + z*(x - c)
其中:
-
a, b, c 是系统参数
-
**(x, y, z)** 是系统的状态变量
-
经典参数值:a=0.2, b=0.2, c=5.7
1.3 物理意义解读
-
x方程:-y - z 表示x的变化受y和z的负反馈
-
y方程:x + a*y 表示y的变化是x的线性驱动和自身衰减
-
z方程 :b + z*(x - c) 包含常数项b和非线性耦合项z*(x-c),这是系统产生混沌的关键
2. 代码架构分析
2.1 类设计(RosslerAttractor)
代码采用面向对象设计,将罗斯勒系统封装为一个类,主要包含:
2.1.1 核心属性
-
系统参数:a, b, c
-
时间参数:dt(步长), steps(步数)
-
状态变量:x, y, z轨迹数组
-
时间向量:t
2.1.2 核心方法
-
**simulate()** - 数值模拟
-
**calculate_lyapunov()** - 李雅普诺夫指数计算
-
**create_animation()** - 可视化动画
-
**explore_parameters()** - 参数探索
2.2 数值方法实现
2.2.1 欧拉法(Euler Method)
# 一阶精度,计算简单但精度有限
dx = (-y - z) * dt
dy = (x + a*y) * dt
dz = (b + z*(x - c)) * dt
2.2.2 四阶龙格-库塔法(RK4)
# 四阶精度,稳定性好,适用于混沌系统
# 通过四个中间斜率(k1,k2,k3,k4)加权平均
# 显著提高计算精度
比较:
-
欧拉法:计算简单,但dt过大会导致发散
-
RK4法:精度高,适合混沌系统模拟,但计算量较大
-
本代码默认使用RK4法以保证模拟精度
3. 混沌特征分析
3.1 李雅普诺夫指数(Lyapunov Exponent)
代码中实现了李雅普诺夫指数的近似计算:
def calculate_lyapunov(self, epsilon=1e-8, steps=1000):
# 主轨迹和扰动轨迹分离计算
# 计算相邻轨线的指数发散率
# λ = (1/t) * Σ ln(d(t)/d₀)
物理意义:
-
λ > 0:系统对初始条件敏感(混沌)
-
λ = 0:系统处于临界状态
-
λ < 0:系统稳定收敛
3.2 相空间重构
代码展示的多种投影方式:
-
3D完整相空间:展示吸引子全貌
-
XY平面投影:显示螺旋结构
-
XZ平面投影:显示折叠特性
-
YZ平面投影:显示非线性特征
4. 可视化技术详解
4.1 多图布局设计
┌─────────────┬─────────────┬─────────────┐
│ 3D视图 │ XY投影 │ XZ投影 │
├─────────────┼─────────────┼─────────────┤
│ YZ投影 │ 时间序列 │ 动态生成 │
└─────────────┴─────────────┴─────────────┘
4.2 动画实现原理
# 使用FuncAnimation创建帧动画
anim = FuncAnimation(fig, update, frames=..., interval=20, blit=True)
# 更新函数控制轨迹绘制
def update(frame):
idx = min(frame * 20, len(self.x) - 1)
line.set_data(self.x[:idx], self.y[:idx])
line.set_3d_properties(self.z[:idx])
4.3 颜色与视觉效果
-
深色背景:突出轨迹,减少视觉干扰
-
霓虹色彩:增强科技感和可区分性
-
透明度控制:展现轨迹密度
-
网格线:提供空间参考
5. 参数空间探索
5.1 经典参数组合
代码预设了6种参数组合:
| 参数(a,b,c) | 系统行为 | 特征 |
|---|---|---|
| (0.2,0.2,2.0) | 周期轨道 | 稳定极限环 |
| (0.2,0.2,3.5) | 准周期轨道 | 双周期运动 |
| (0.2,0.2,5.7) | 经典混沌 | 标准吸引子 |
| (0.2,0.2,9.0) | 高维混沌 | 复杂轨迹 |
| (0.1,0.1,14.0) | 双涡卷结构 | 类似洛伦兹 |
| (0.2,0.1,5.7) | 非对称吸引子 | 偏斜结构 |
5.2 参数影响规律
-
参数a:影响y方向的阻尼特性
-
参数b:影响z方向的常数驱动
-
参数c:控制非线性强度,决定混沌行为
6. 数值稳定性与精度
6.1 步长选择原则
dt = 0.01 # 经验值,平衡精度与计算量
steps = 8000 # 足够长的轨迹以展示吸引子
步长选择考虑:
-
dt过小:计算量大,累计误差
-
dt过大:数值不稳定,可能发散
-
推荐范围:0.001-0.05
6.2 初始条件敏感性
x0, y0, z0 = 0.0, 1.0, 0.0 # 经典初始条件
混沌系统的特点是对初始条件极度敏感,但吸引子结构是稳定的。
7. 系统特性分析
7.1 固定点分析
令dx/dt = dy/dt = dz/dt = 0:
-y - z = 0
x + a*y = 0
b + z*(x - c) = 0
解得两个固定点,其稳定性由雅可比矩阵特征值决定。
7.2 Poincaré截面
可通过记录轨迹与特定平面的交点来研究系统的截面特征。
7.3 功率谱分析
(代码中未实现)可通过傅里叶变换分析频率特征,混沌系统具有连续宽带谱。
8. 实际应用场景
8.1 教学演示
-
混沌理论教学
-
非线性动力学入门
-
数值方法实践
8.2 科学研究
-
混沌控制与同步研究
-
混沌加密算法测试
-
复杂系统建模参考
8.3 艺术生成
-
生成艺术图案
-
动态视觉效果
-
音乐合成算法
9. 代码优化建议
9.1 性能优化
# 可考虑使用Numba加速数值计算
@jit(nopython=True)
def simulate_jit(...):
# 使用即时编译加速循环
9.2 功能扩展
-
分岔图绘制:研究参数变化时的系统行为
-
庞加莱映射:分析截面特性
-
相关维数计算:定量描述吸引子复杂度
-
实时参数调节:交互式探索
9.3 错误处理增强
# 添加参数合法性检查
if a <= 0 or b <= 0 or c <= 0:
raise ValueError("参数必须为正数")
10. 运行与使用说明
10.1 环境要求
Python 3.6+
NumPy
Matplotlib
可选:Jupyter环境
10.2 运行方式
# 直接运行
python rossler_attractor.py
# 在Jupyter中
%run rossler_attractor.py
10.3 输出结果
-
控制台输出:参数、李雅普诺夫指数、统计信息
-
图形窗口1:完整可视化(3D+2D+时间序列+动画)
-
图形窗口2:参数空间探索
11. 数学与物理意义总结
11.1 混沌特征验证
-
对初始条件敏感:微小的初始差异指数放大
-
有限相空间:轨迹不无限发散
-
有界非周期:既不收敛到固定点,也不形成周期轨道
-
分数维:吸引子具有分数维的几何结构
11.2 与洛伦兹吸引子对比
| 特性 | 罗斯勒吸引子 | 洛伦兹吸引子 |
|---|---|---|
| 方程数 | 3 | 3 |
| 非线性项 | 1个(z*(x-c)) | 2个(xy, xz) |
| 对称性 | 不对称 | 关于z轴对称 |
| 复杂度 | 相对简单 | 更复杂 |
| 应用领域 | 化学、生物 | 气象、流体 |
12. 完整代码
罗斯勒吸引子虽然数学形式简单,但却能产生丰富的复杂行为,是理解混沌理论、非线性动力学的理想模型。这个实现为相关领域的学习和研究提供了一个实用工具。
"""
罗斯勒吸引子(Rössler Attractor)完整实现
无需用户交互,完全自动化运行
"""
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.animation import FuncAnimation
import matplotlib.font_manager as fm
import sys
import warnings
import platform
# 忽略警告
warnings.filterwarnings('ignore')
# 配置使用系统字体
if platform.system() == 'Windows':
# Windows系统使用微软雅黑或黑体
plt.rcParams['font.sans-serif'] = ['Microsoft YaHei', 'SimHei', 'Arial Unicode MS']
elif platform.system() == 'Darwin':
# macOS系统使用PingFang SC或Heiti TC
plt.rcParams['font.sans-serif'] = ['PingFang SC', 'Heiti TC', 'STHeiti']
else:
# Linux系统使用文泉驿微米黑或黑体
plt.rcParams['font.sans-serif'] = ['WenQuanYi Micro Hei', 'SimHei', 'DejaVu Sans']
# 解决负号显示问题
plt.rcParams['axes.unicode_minus'] = False
class RosslerAttractor:
"""
罗斯勒吸引子(Rössler Attractor)模拟类
微分方程:
dx/dt = -y - z
dy/dt = x + a*y
dz/dt = b + z*(x - c)
"""
def __init__(self, a=0.2, b=0.2, c=5.7, dt=0.01, steps=8000):
"""
初始化罗斯勒吸引子参数
参数:
a, b, c: 系统参数(经典值: 0.2, 0.2, 5.7)
dt: 时间步长
steps: 模拟步数
"""
self.a = a
self.b = b
self.c = c
self.dt = dt
self.steps = steps
# 初始条件
self.x0, self.y0, self.z0 = 0.0, 1.0, 0.0
# 轨迹数组
self.x = np.zeros(steps)
self.y = np.zeros(steps)
self.z = np.zeros(steps)
self.t = np.arange(0, steps * dt, dt)
def simulate(self, method='rk4'):
"""
使用数值方法求解微分方程
参数:
method: 数值方法 ('euler' 或 'rk4')
"""
# 设置初始条件
self.x[0], self.y[0], self.z[0] = self.x0, self.y0, self.z0
if method == 'euler':
# 欧拉法
for i in range(1, self.steps):
dx = (-self.y[i - 1] - self.z[i - 1]) * self.dt
dy = (self.x[i - 1] + self.a * self.y[i - 1]) * self.dt
dz = (self.b + self.z[i - 1] * (self.x[i - 1] - self.c)) * self.dt
self.x[i] = self.x[i - 1] + dx
self.y[i] = self.y[i - 1] + dy
self.z[i] = self.z[i - 1] + dz
elif method == 'rk4':
# 四阶龙格-库塔法(更高精度)
for i in range(1, self.steps):
# k1
k1x = -self.y[i - 1] - self.z[i - 1]
k1y = self.x[i - 1] + self.a * self.y[i - 1]
k1z = self.b + self.z[i - 1] * (self.x[i - 1] - self.c)
# k2
x_temp = self.x[i - 1] + 0.5 * self.dt * k1x
y_temp = self.y[i - 1] + 0.5 * self.dt * k1y
z_temp = self.z[i - 1] + 0.5 * self.dt * k1z
k2x = -y_temp - z_temp
k2y = x_temp + self.a * y_temp
k2z = self.b + z_temp * (x_temp - self.c)
# k3
x_temp = self.x[i - 1] + 0.5 * self.dt * k2x
y_temp = self.y[i - 1] + 0.5 * self.dt * k2y
z_temp = self.z[i - 1] + 0.5 * self.dt * k2z
k3x = -y_temp - z_temp
k3y = x_temp + self.a * y_temp
k3z = self.b + z_temp * (x_temp - self.c)
# k4
x_temp = self.x[i - 1] + self.dt * k3x
y_temp = self.y[i - 1] + self.dt * k3y
z_temp = self.z[i - 1] + self.dt * k3z
k4x = -y_temp - z_temp
k4y = x_temp + self.a * y_temp
k4z = self.b + z_temp * (x_temp - self.c)
# 更新位置
self.x[i] = self.x[i - 1] + (self.dt / 6.0) * (k1x + 2 * k2x + 2 * k3x + k4x)
self.y[i] = self.y[i - 1] + (self.dt / 6.0) * (k1y + 2 * k2y + 2 * k3y + k4y)
self.z[i] = self.z[i - 1] + (self.dt / 6.0) * (k1z + 2 * k2z + 2 * k3z + k4z)
def calculate_lyapunov(self, epsilon=1e-8, steps=1000):
"""
计算最大Lyapunov指数(近似值)
参数:
epsilon: 扰动大小
steps: 计算步数
返回:
最大Lyapunov指数近似值
"""
# 主轨迹
x_main, y_main, z_main = self.x0, self.y0, self.z0
# 扰动轨迹
x_pert = x_main + epsilon
y_pert = y_main
z_pert = z_main
divergence_sum = 0
for _ in range(steps):
# 更新主轨迹(简化欧拉法)
dx_main = (-y_main - z_main) * self.dt
dy_main = (x_main + self.a * y_main) * self.dt
dz_main = (self.b + z_main * (x_main - self.c)) * self.dt
x_main += dx_main
y_main += dy_main
z_main += dz_main
# 更新扰动轨迹
dx_pert = (-y_pert - z_pert) * self.dt
dy_pert = (x_pert + self.a * y_pert) * self.dt
dz_pert = (self.b + z_pert * (x_pert - self.c)) * self.dt
x_pert += dx_pert
y_pert += dy_pert
z_pert += dz_pert
# 计算距离
dist = np.sqrt((x_pert - x_main) ** 2 + (y_pert - y_main) ** 2 + (z_pert - z_main) ** 2)
if dist > 0:
divergence_sum += np.log(dist / epsilon)
return divergence_sum / (steps * self.dt)
def create_animation(self, save_gif=False, filename="rossler_attractor.gif"):
"""创建并运行动画"""
# 运行模拟
self.simulate(method='rk4')
# 创建图形
fig = plt.figure(figsize=(15, 10))
fig.patch.set_facecolor('#0a0a0a')
# 3D轨迹图
ax1 = fig.add_subplot(231, projection='3d')
ax1.set_facecolor('#0a0a0a')
ax1.plot(self.x, self.y, self.z, lw=0.5, alpha=0.8, color='#00ffff')
ax1.set_xlabel('X', color='white', fontsize=10)
ax1.set_ylabel('Y', color='white', fontsize=10)
ax1.set_zlabel('Z', color='white', fontsize=10)
ax1.tick_params(colors='white', labelsize=8)
ax1.set_title('Rössler Attractor 3D View', color='white', fontsize=12, pad=10)
ax1.xaxis.pane.fill = False
ax1.yaxis.pane.fill = False
ax1.zaxis.pane.fill = False
ax1.xaxis.pane.set_edgecolor('white')
ax1.yaxis.pane.set_edgecolor('white')
ax1.zaxis.pane.set_edgecolor('white')
ax1.grid(True, alpha=0.2, linestyle=':', color='gray')
# 2D投影图
ax2 = fig.add_subplot(232)
ax2.set_facecolor('#0a0a0a')
ax2.plot(self.x, self.y, lw=0.5, alpha=0.7, color='#ff00ff')
ax2.set_xlabel('X', color='white', fontsize=10)
ax2.set_ylabel('Y', color='white', fontsize=10)
ax2.tick_params(colors='white')
ax2.set_title('XY平面投影', color='white', fontsize=12)
ax2.grid(True, alpha=0.2, linestyle=':', color='gray')
ax3 = fig.add_subplot(233)
ax3.set_facecolor('#0a0a0a')
ax3.plot(self.x, self.z, lw=0.5, alpha=0.7, color='#ffff00')
ax3.set_xlabel('X', color='white', fontsize=10)
ax3.set_ylabel('Z', color='white', fontsize=10)
ax3.tick_params(colors='white')
ax3.set_title('XZ平面投影', color='white', fontsize=12)
ax3.grid(True, alpha=0.2, linestyle=':', color='gray')
ax4 = fig.add_subplot(234)
ax4.set_facecolor('#0a0a0a')
ax4.plot(self.y, self.z, lw=0.5, alpha=0.7, color='#00ff00')
ax4.set_xlabel('Y', color='white', fontsize=10)
ax4.set_ylabel('Z', color='white', fontsize=10)
ax4.tick_params(colors='white')
ax4.set_title('YZ平面投影', color='white', fontsize=12)
ax4.grid(True, alpha=0.2, linestyle=':', color='gray')
# 时间序列图
ax5 = fig.add_subplot(235)
ax5.set_facecolor('#0a0a0a')
ax5.plot(self.t[:1000], self.x[:1000], lw=0.8, label='X(t)', color='#00ffff')
ax5.plot(self.t[:1000], self.y[:1000], lw=0.8, label='Y(t)', color='#ff00ff')
ax5.plot(self.t[:1000], self.z[:1000], lw=0.8, label='Z(t)', color='#ffff00')
ax5.set_xlabel('时间', color='white', fontsize=10)
ax5.set_ylabel('值', color='white', fontsize=10)
ax5.tick_params(colors='white')
ax5.set_title('时间序列(前1000步)', color='white', fontsize=12)
ax5.legend(facecolor='#1a1a1a', edgecolor='white', labelcolor='white', fontsize=8)
ax5.grid(True, alpha=0.2, linestyle=':', color='gray')
# 相位空间图(动态)
ax6 = fig.add_subplot(236, projection='3d')
ax6.set_facecolor('#0a0a0a')
line, = ax6.plot([], [], [], lw=0.8, color='white')
point, = ax6.plot([], [], [], 'o', markersize=5, color='red')
ax6.set_xlim(self.x.min(), self.x.max())
ax6.set_ylim(self.y.min(), self.y.max())
ax6.set_zlim(self.z.min(), self.z.max())
ax6.set_xlabel('X', color='white', fontsize=10)
ax6.set_ylabel('Y', color='white', fontsize=10)
ax6.set_zlabel('Z', color='white', fontsize=10)
ax6.tick_params(colors='white', labelsize=8)
ax6.set_title('动态轨迹生成', color='white', fontsize=12, pad=10)
ax6.xaxis.pane.fill = False
ax6.yaxis.pane.fill = False
ax6.zaxis.pane.fill = False
ax6.xaxis.pane.set_edgecolor('white')
ax6.yaxis.pane.set_edgecolor('white')
ax6.zaxis.pane.set_edgecolor('white')
ax6.grid(True, alpha=0.2, linestyle=':', color='gray')
# 动画更新函数
def update(frame):
idx = min(frame * 20, len(self.x) - 1) # 加速动画
line.set_data(self.x[:idx], self.y[:idx])
line.set_3d_properties(self.z[:idx])
point.set_data([self.x[idx]], [self.y[idx]])
point.set_3d_properties([self.z[idx]])
return line, point
# 创建动画
anim = FuncAnimation(fig, update, frames=range(0, len(self.x) // 20, 5),
interval=20, blit=True, repeat=True)
# 参数说明
param_text = f"参数: a={self.a}, b={self.b}, c={self.c}\n"
param_text += f"步长: dt={self.dt}, 总步数: {self.steps}\n"
param_text += f"模拟时间: {self.t[-1]:.1f} 单位\n"
# 计算Lyapunov指数
try:
lyap_exp = self.calculate_lyapunov()
param_text += f"最大Lyapunov指数(近似): {lyap_exp:.4f}\n"
if lyap_exp > 0:
param_text += "系统呈现混沌行为"
else:
param_text += "系统呈现周期性行为"
except:
param_text += "Lyapunov指数计算失败"
fig.text(0.5, 0.02, param_text, ha='center', color='white', fontsize=10,
bbox=dict(boxstyle='round', facecolor='#1a1a1a', alpha=0.8))
fig.suptitle('罗斯勒吸引子(Rössler Attractor)',
color='white', fontsize=14, y=0.98)
plt.tight_layout()
# 添加窗口大小变化事件处理器
def on_resize(event):
"""窗口大小变化时自动调整布局"""
plt.tight_layout()
fig.canvas.draw_idle()
fig.canvas.mpl_connect('resize_event', on_resize)
# 保存动画
if save_gif:
try:
from matplotlib.animation import PillowWriter
writer = PillowWriter(fps=30)
anim.save(filename, writer=writer)
print(f"动画已保存为: {filename}")
except Exception as e:
print(f"保存动画失败: {e}")
plt.show()
return anim
def explore_parameters(self):
"""探索不同参数对系统行为的影响(自动运行)"""
# 定义要探索的参数组合
param_sets = [
(0.2, 0.2, 2.0, "周期轨道 (c=2.0)"),
(0.2, 0.2, 3.5, "准周期轨道 (c=3.5)"),
(0.2, 0.2, 5.7, "经典混沌 (c=5.7)"),
(0.2, 0.2, 9.0, "高维混沌 (c=9.0)"),
(0.1, 0.1, 14.0, "双涡卷结构"),
(0.2, 0.1, 5.7, "非对称吸引子")
]
fig, axes = plt.subplots(2, 3, figsize=(15, 10))
fig.patch.set_facecolor('#0a0a0a')
for idx, (a, b, c, title) in enumerate(param_sets):
ax = axes[idx // 3, idx % 3]
ax.set_facecolor('#0a0a0a')
# 创建临时吸引子
temp_attractor = RosslerAttractor(a=a, b=b, c=c, dt=0.01, steps=2000)
temp_attractor.simulate(method='rk4')
# 绘制
colors = ['#00ffff', '#ff00ff', '#ffff00', '#00ff00', '#ff6600', '#ff00cc']
ax.plot(temp_attractor.x, temp_attractor.y, lw=0.7, color=colors[idx], alpha=0.8)
ax.set_xlabel('X', color='white', fontsize=9)
ax.set_ylabel('Y', color='white', fontsize=9)
ax.tick_params(colors='white', labelsize=7)
ax.set_title(title, color='white', fontsize=10)
ax.grid(True, alpha=0.1, linestyle=':', color='gray')
# 添加参数文本
param_text = f"a={a}, b={b}, c={c}"
ax.text(0.05, 0.95, param_text, transform=ax.transAxes,
fontsize=8, color='white', verticalalignment='top',
bbox=dict(boxstyle='round', facecolor='#1a1a1a', alpha=0.7))
fig.suptitle('参数探索: 不同参数对罗斯勒吸引子的影响', color='white', fontsize=14, y=0.95)
plt.tight_layout()
# 添加窗口大小变化事件处理器
def on_resize(event):
"""窗口大小变化时自动调整布局"""
plt.tight_layout()
fig.canvas.draw_idle()
fig.canvas.mpl_connect('resize_event', on_resize)
plt.show()
def main():
"""主函数 - 演示罗斯勒吸引子的各种功能"""
print("=" * 60)
print("罗斯勒吸引子(Rössler Attractor)")
print("=" * 60)
# 1. 创建经典罗斯勒吸引子
print("\n1. 创建经典罗斯勒吸引子 (a=0.2, b=0.2, c=5.7)...")
rossler = RosslerAttractor(a=0.2, b=0.2, c=5.7, dt=0.01, steps=8000)
# 2. 运行模拟
print("2. 运行数值模拟(使用RK4方法)...")
rossler.simulate(method='rk4')
# 3. 计算Lyapunov指数
print("3. 计算最大Lyapunov指数...")
try:
lyap_exp = rossler.calculate_lyapunov()
print(f" 最大Lyapunov指数(近似值): {lyap_exp:.6f}")
if lyap_exp > 0:
print(" → 系统呈现混沌行为 (对初始条件敏感)")
else:
print(" → 系统呈现周期性行为")
except Exception as e:
print(f" Lyapunov指数计算失败: {e}")
# 4. 显示基本统计信息
print("\n4. 轨迹统计信息:")
print(f" X范围: [{rossler.x.min():.2f}, {rossler.x.max():.2f}]")
print(f" Y范围: [{rossler.y.min():.2f}, {rossler.y.max():.2f}]")
print(f" Z范围: [{rossler.z.min():.2f}, {rossler.z.max():.2f}]")
print(f" 模拟时间: {rossler.t[-1]:.1f} 单位")
print(f" 总点数: {len(rossler.t)}")
# 5. 创建并显示动画
print("\n5. 创建可视化动画...")
print(" 注意: 这将打开一个包含6个子图的窗口")
print(" - 左上: 3D轨迹图")
print(" - 中上: XY平面投影")
print(" - 右上: XZ平面投影")
print(" - 左下: YZ平面投影")
print(" - 中下: 时间序列")
print(" - 右下: 动态轨迹生成")
# 创建动画(不保存GIF,如需要保存可设置save_gif=True)
anim = rossler.create_animation(save_gif=False, filename="rossler_attractor.gif")
# 6. 自动运行参数探索
print("\n6. 自动运行参数探索...")
rossler.explore_parameters()
print("\n" + "=" * 60)
print("程序执行完成!")
print("=" * 60)
print("\n说明:")
print("1. 如需保存动画,请在create_animation()中设置save_gif=True")
print("2. 如需更改参数,可直接修改RosslerAttractor类的构造函数参数")
print("3. 数值方法支持'euler'(欧拉法)和'rk4'(四阶龙格-库塔法)")
print("4. 所有图形窗口关闭后程序将继续执行")
# 如果直接运行此文件,则执行主函数
if __name__ == "__main__":
# 检查是否在Jupyter环境中
try:
from IPython import get_ipython
in_jupyter = get_ipython() is not None
except:
in_jupyter = False
if in_jupyter:
print("检测到Jupyter环境,已禁用交互式输入")
# 运行主程序
main()