性别比例与生态模型
创建于:2025年1月21日
创建于:2025年1月21日
While some animal species exist outside of the usual male or female sexes, most species are
substantially either male or female. Although many species exhibit a 1:1 sex ratio at birth, other
species deviate from an even sex ratio. This is called adaptive sex ratio variation. For example,
the temperature of the nest incubating eggs of the American alligator influences the sex ratios at
birth.
The role of lampreys is complex. In some lake habitats, they are seen as parasites with a
significant impact on the ecosystem, whereas lampreys are also a food source in some regions of
the world, such as Scandinavia, the Baltics, and for some Indigenous peoples of the Pacific
Northwest in North America.
The sex ratio of sea lampreys can vary based on external circumstances. Sea lampreys become
male or female depending on how quickly they grow during the larval stage. These larval growth
rates are influenced by the availability of food. In environments where food availability is low,
growth rates will be lower, and the percentage of males can reach approximately 78% of the
population. In environments where food is more readily available, the percentage of males has
been observed to be approximately 56% of the population.
We focus on the question of sex ratios and their dependence on local conditions, specifically for
sea lampreys. Sea lampreys live in lake or sea habitats and migrate up rivers to spawn. The task
is to examine the advantages and disadvantages of the ability for a species to alter its sex ratio
depending on resource availability. Your team should develop and examine a model to provide
insights into the resulting interactions in an ecosystem.
Questions to examine include the following:
1.What is the impact on the larger ecological system when the population of lampreys can
alter its sex ratio?
2.What are the advantages and disadvantages to the population of lampreys?
3.What is the impact on the stability of the ecosystem given the changes in the sex ratios of
lampreys?
4.Can an ecosystem with variable sex ratios in the lamprey population offer advantages to
others in the ecosystem, such as parasites?
对于以上的问题。是用什么样的模型来刻画性别比例以及种群对生态系统的作用呢?
在研究具有可变性别比例(并随资源可用性而动态调整)的种群对生态系统的影响时,通常会采用能够同时体现以下要素的模型:
常见的建模思路包括以下几类,具体选择取决于研究需求、数据可用性以及分析复杂度:
这样便可用一组耦合的微分方程来刻画“资源量—雄性数量—雌性数量”三者之间的动态关系。如果还要考虑其他物种或生态位,可以进一步扩展为多物种耦合方程组。
Leslie矩阵模型常用于年龄(或阶段)结构显著、繁殖有明显世代特征的物种。对于七鳃鳗(包括海洋中的或淡水中的物种),它们有明显的 幼体期—成体期—产卵期 等发育阶段,可以考虑:
如果资源限制导致幼体生长缓慢,则更多个体向雄性阶段转化;若资源丰富,更多个体向雌性阶段转化。通过在Leslie矩阵的转移概率或繁殖参数中包含资源可用性,就可以研究性别比例的动态变化及对整体种群增长和生态系统稳定性的影响。
如果想更精细地刻画:
那么可以使用**基于个体的模型(Agent-Based Model, ABM)**来模拟七鳃鳗种群以及与其他物种、环境资源的交互。核心思路是:
ABM可以很真实地再现生态系统的时空动态和个体差异,但需要更多参数和计算量,适合有大量数据支撑或想进行情景模拟、政策仿真时使用。
在实际研究中,往往会结合多种模型:
无论使用哪种模型,都需要明确以下几部分的功能关系:
性别比例随资源变化的规则
种群内部繁殖与死亡机制
与其他物种或环境的相互作用
反馈回路(反馈机制)
通过对以上机制的定量描述并对模型进行稳定性分析(如特征值分析、平衡点分析,或在ABM中观测长期仿真结果),就可以研究:
在研究七鳃鳗(或其他具有可变性别比例的物种)对生态系统的作用时,需要一个能够同时体现:
的综合模型。
最常见的可选方案:
在具体应用中,往往会融合多种建模方法,并结合实验数据或田野调查的数据来拟合模型参数、验证模型预测。这样才能既保证模型在机制层面有较好解释力,又能在实践中为生态管理或资源调控提供指导。
给帮我给出更具体一些的答案。当种群的性别比例发生变化时,会对更大的生态系统产生什么样的影响?请详细说明说明基于常微分方程 (ODE) 或差分方程的种群动力学模型、Leslie矩阵模型和ABM模型的建模过程,包括但不限于数据的处理、公式的计算以及最后的总结。
下文将分为两个主要部分:
当物种的性别比例(尤其是关键物种,如顶级捕食者或对其它物种具有强影响力的物种)能够随资源或环境变化而动态调整时,这不仅影响该物种本身的繁衍速度,还会对整个生态系统产生一系列连锁反应,主要包括:
种群增长与繁殖成功率的变化
种间相互作用的改变
生态系统稳定性与恢复力
下面将分别讨论三类模型:(A) 基于常微分方程(或差分方程)的种群动力学模型、(B) Leslie矩阵模型以及**(C) 基于个体的模型(Agent-Based Model,ABM)**。每种方法都包含数据处理、公式设定以及结果分析的要点。
物种基本生物学参数
环境与资源数据
性别分配规律
在最简单的框架下,设:
可根据研究需要,写出如下耦合方程:
资源方程(若要显式刻画资源动态)
雄性个体数量方程
雌性个体数量方程
上式中的“”可进一步加入捕食者(如果有更高营养级捕食)、迁移损耗、栖息地竞争等项。在离散时间(差分方程)情况下,则将这些微分方程改写成:
或用直接定义的递推关系。
数值求解
稳定性和平衡分析
生态学解释
阶段划分与过渡概率
在幼体转化为成体时的性别概率
繁殖率
经典Leslie矩阵一般用于单性别模型(通常只考虑雌性),但这里为了体现性别转换,可以做以下设计:
令种群按阶段+性别来划分,如:
或者在幼体期不区分性别,而在成体期区分性别。
建立一个较大的转移矩阵 ,尺寸为 (若同时考虑雄性与雌性的k个年龄/阶段),其中每个子方块矩阵描述从一个阶段到另一个阶段的转移。如果幼体期不区分性别,可能先只设k阶段,最后一两个阶段分雄/雌两列。
资源依赖的性别转换
繁殖输出
这样,就可以得到一个随时间变化的矩阵模型:
其中 包括各阶段(雄+雌)的数量向量,而 矩阵中包含了依赖资源的性别转换概率或繁殖率。
迭代仿真
主导特征值与长程增长率
管理与生态启示
个体层级的生物学特征
空间和时间刻度
交互关系
定义环境(空间网格/网络)
定义个体属性
更新规则
并行或循环仿真
宏观指标汇总
敏感性分析
生态系统层面影响
可视化
ODE/差分方程模型
Leslie矩阵模型
基于个体的模型(ABM)
不论采用哪种模型,都要充分考虑性别比例函数与资源或生长速率之间的关系,并将其与种群增长、死亡、竞争、捕食/寄生等过程紧密地耦合起来。最终,模型结果往往需要与实际生态数据进行比较或校准(如田野调查数据、渔业统计、历史记录等),才能更好地为生态管理和保护决策提供科学指导。
能再详细说说基于常微分方程(或差分方程)的种群动力学模型这部分的内容吗,把公式提出的设想、每一个参数的含义、每个参数求解的过程、拟合的过程都详细的说明以及运算,尽可能完整的告诉我
下面给出一个示例性的基于常微分方程(ODE)的种群动力学模型,来描述“资源—海 lamprey(七鳃鳗)种群”系统中性别比例如何随资源情况而变化,并对各参数的含义、获取/求解过程以及模型拟合步骤做尽可能详细的说明。
我们以一个三变量的ODE系统为例:
假设:
在此框架下,我们可以写出如下微分方程:
下文会详细说明各参数的生物学意义及可能的求解/拟合思路。请注意这是一个简化模型,在实际应用中可能要再加一些项(如雌雄的配对功能、产卵后死亡、幼体发育时长等),但该示例足以展示如何进行ODE模型的建模、参数含义与求解过程。
在大多数情况下,这样的非线性ODE系统难以用解析方法得到封闭形式解,需要使用数值积分方法(如Euler法、Runge-Kutta法等)。典型流程:
在实践中,我们通常有一组野外观测数据 (可能是离散时间点),我们希望用模型去拟合这些数据,从而估计出 以及 的关键参数(如等)。
常见的拟合流程如下:
确定要拟合的参数:在本例中,至少有7-8个参数(见上方列表)。有时可能先固定一部分(通过文献数据或预实验),减少自由度。
定义误差函数/目标函数
迭代寻优
不确定性分析
模型检验
假设我们用Euler法(显式)来做最简示例,离散步长:
通过这种离散迭代,就能得到随时间序列 。再与观测数据比较,做误差分析,即可进行拟合或预测。
性别比例的时间变化
对生态系统稳定性的影响
管理与保护
以上示例展示了一个从模型建立→参数含义→求解与拟合→结果解读的完整思路。实际研究中可以在此基础上扩展(如增加年龄结构、加上更复杂的捕食或竞争关系、考虑季节性/空间异质性等),并根据具体的生物学资料和观测数据进行针对性的修正与细化。这样才能更准确地反映海 lamprey(或其他具有可变性别比例的物种)在真实生态系统中的动态与影响。
请你进一步优化和精确这个模型,要求出生受雌雄配对,个体产卵数和卵成活率影响;且考虑雌性雄性配对繁殖后会死亡
下面给出一个更“精细化”的常微分方程(ODE)种群动力学模型示例,专门引入了 雌雄配对生殖、产卵量与卵成活 以及 繁殖后亲本死亡 的机制。模型依然相对简化,但已经较好地体现了七鳃鳗(或类似“只繁殖一次后死亡”的种群)生命周期关键过程以及资源(或宿主)消耗的相互作用。
群体划分
生殖方式
性别决定
资源消耗
死亡
根据上述思路,可以写出以下四个微分方程:
下面对各项做进一步解释。
:
:
:
:
:
:
:
数值解方法
初始条件
参数估计与拟合
敏感性与不确定性分析
一次性繁殖与群体波动
性别比例适应与资源依赖
对更大生态系统的影响
以上就是一个涵盖“雌雄配对、产卵成活、繁殖后死亡、资源依赖性别分配”等关键要素的常微分方程模型示例,以及在实际使用和拟合时的操作流程。希望对你进一步优化、精确刻画七鳃鳗种群性别比例及其生态系统影响有所帮助。
假设初始条件R(0)=K/2,其他初始条件为0,模型参数请你看情况合理确定,给出使用Euler法求解计算并用matplotlib绘制出雌雄个体数随时间变化的图的python代码
下面给出一个示例 Python 代码,使用最简易的 Euler 法 对文中给出的带有“雌雄配对、一繁殖后亲本死亡、资源依赖性别分配”的 ODE 模型进行数值模拟,并用 matplotlib 绘制雄性与雌性个体数量随时间变化的曲线。
说明:
- 代码中各个参数数值均是“示例性质”,并不代表真实系统的最佳估计。
- 性别分配函数 也选用了一个简单的线性函数示例,读者可根据实际研究需要改成Sigmoid等更符合实测的关系。
- 由于是示范,我们选择了 的较大步长,实际研究中可根据系统敏感度适当缩短步长,或改用更高精度方法(如RK4)。
我们要数值求解如下系统 (假设所有参数均为常数):
其中:
假设初始条件:
pythonimport numpy as np import matplotlib.pyplot as plt #----------------------------- # 1) 参数设置(示例数值) #----------------------------- r = 0.8 # 资源的内禀增长率 K = 1000.0 # 资源环境承载量 alpha = 0.0001 # 七鳃鳗(各阶段)对资源的消耗效率 c = 0.0002 # 雌雄个体配对繁殖接触率 E = 50.0 # 每次成功繁殖产生的幼体数(产卵量*成活率) muJ = 0.1 # 幼体死亡率 muM = 0.05 # 雄性死亡率 muF = 0.05 # 雌性死亡率 gamma = 0.2 # 幼体向成体转变的速率 # p(R) 函数:示例设为资源越多,成为雄性的概率越小/大任选其一 # 这里示例:R=0 时 p=0.2,R=K=1000 时 p=0.8,做一个线性插值: def pR(R): return 0.2 + 0.6*(R/K) # 当 R=0 => 0.2, R=K => 0.8 #----------------------------- # 2) 定义 Euler 法模拟函数 #----------------------------- def euler_simulation(T=300, dt=1.0): """ 在时间范围 [0, T] 用步长 dt 做欧拉法数值求解 返回各个时刻与对应的 R, J, M, F 数组 """ N = int(T/dt) + 1 # 步数(含起始点) # 数组初始化 t_vals = np.linspace(0, T, N) R_vals = np.zeros(N) J_vals = np.zeros(N) M_vals = np.zeros(N) F_vals = np.zeros(N) # 初始条件 R_vals[0] = K/2.0 # R(0)=K/2 J_vals[0] = 0.0 M_vals[0] = 0.0 F_vals[0] = 0.0 # Euler 迭代 for n in range(N-1): Rn = R_vals[n] Jn = J_vals[n] Mn = M_vals[n] Fn = F_vals[n] # 当前时刻的 p(R) p_val = pR(Rn) # 计算微分方程右侧 (dRdt, dJdt, dMdt, dFdt) dRdt = r * Rn * (1 - Rn/K) - alpha * (Jn + Mn + Fn) * Rn dJdt = E * c * Mn * Fn - muJ * Jn - gamma * Jn dMdt = gamma * p_val * Jn - c * Mn * Fn - muM * Mn dFdt = gamma * (1 - p_val) * Jn - c * Mn * Fn - muF * Fn # 欧拉法更新 R_vals[n+1] = Rn + dt * dRdt J_vals[n+1] = Jn + dt * dJdt M_vals[n+1] = Mn + dt * dMdt F_vals[n+1] = Fn + dt * dFdt return t_vals, R_vals, J_vals, M_vals, F_vals #----------------------------- # 3) 运行模拟 & 绘图 #----------------------------- t, R_sol, J_sol, M_sol, F_sol = euler_simulation(T=300, dt=1.0) # 绘制雌雄成体数量随时间的变化 plt.figure(figsize=(8,5)) plt.plot(t, M_sol, label='Male (M)', color='blue') plt.plot(t, F_sol, label='Female (F)', color='red') plt.xlabel('Time') plt.ylabel('Population size') plt.title('Euler method: M(t) and F(t) vs Time') plt.legend() plt.grid(True) plt.show() # 如需查看或比较幼体、资源曲线,可再绘其他子图 plt.figure(figsize=(8,5)) plt.plot(t, J_sol, label='Juveniles (J)', color='green') plt.plot(t, R_sol, label='Resource (R)', color='brown') plt.xlabel('Time') plt.ylabel('Population / Resource') plt.title('Euler method: J(t) and R(t) vs Time') plt.legend() plt.grid(True) plt.show()
参数含义
r, K, alpha
:资源的生长和被消耗参数;c, E
:雌雄配对繁殖时的“接触率”与单次繁殖对幼体的贡献;muJ, muM, muF
:幼体、雄性、雌性的自然死亡率;gamma
:幼体发育率;pR(R)
:根据当前资源量决定幼体变雄性的概率(可根据实际需求改成Sigmoid等更贴近实测曲线)。Euler 法实现
for n in range(N-1):
循环中,先计算当前状态下的微分方程右端,再用 Rn + dt * dRdt
等简单欧拉方式更新。scipy.integrate.solve_ivp
或自己写 RK4。结果解读
r, K, alpha, c, E, muJ, muM, muF, gamma, p(R)
等参数,从而获得更合理的预测和生态学解释。那可否详细说明说明基于个体的模型(ABM)模型的建模思想以及每个参数的含义呢,并且详细说明说明我们要如何进行数据处理、用python画图之类的建模细节
下面给出一个较为详细的基于个体的模型(Agent-Based Model, ABM)的建模思路,用于模拟“海 lamprey(或类似物种)在可变性别比例机制下的种群动态及其与资源环境的交互”。我们将从核心建模思想、关键参数设定、数据处理与建模流程、以及在 Python 中进行实现和可视化等几个角度进行阐述。
**Agent-Based Model(基于个体的模型)**主要思想是:
与常微分方程模型相比,ABM 可以更灵活地处理空间异质性、个体差异以及随机事件,更接近真实的生态过程。
在构造 ABM 时,通常会定义以下几个核心部分。
R(x,y)
(例如底栖生物量、寄主鱼类密度、营养值),或其他环境属性(温度、流速等)。每一个体通常包含如下状态属性(可以根据研究目标进行增减):
life_stage
表示 幼体(Juvenile) / 成体(Adult);Male / Female
,若还未分化则可标记为 Undetermined
;此外还可能包含行为参数(如移动速度、摄食效率、捕食/寄主选择性)以及随机数种子等,用于处理个体间差异。
在每个离散时间步 (tick) 中,一般按照一定顺序执行:
r, K(x,y)
等,可从文献或观测值中估计;若缺乏数据,可做灵敏度分析。random
或 numpy.random
来实现。这里以纯 Python + matplotlib 为例,给一个最小可行的 ABM 代码结构示例(并不完整覆盖所有细节,但足以展示怎么搭建与可视化)。若要更加工程化,可考虑使用 Mesa 等专门的 Python ABM 框架。
pythonimport numpy as np import random import matplotlib.pyplot as plt #======================== # 1) 参数及全局设置 #======================== WIDTH, HEIGHT = 50, 50 # 网格大小 N_init_agents = 50 # 初始个体数量 # 模型参数 (示例) r = 0.1 # 资源再生速率 K_global = 100.0 # 全局最大承载 consumption_rate = 0.5 # 每个个体消耗资源速率 birth_energy_gain = 20.0 # 繁殖时产下幼体的初始能量 sex_ratio_kappa = 0.01 # 控制资源->性别转换概率的参数 # ... 还可以添加更多参数 #======================== # 2) 环境初始化 #======================== # 简化: 用一个 2D array 存储每个格子的资源量 resource_grid = np.ones((HEIGHT, WIDTH)) * (K_global * 0.5) # 初始资源设为承载量一半 #======================== # 3) 个体类定义 #======================== class Lamprey: def __init__(self, x, y): self.x = x self.y = y self.is_adult = False self.sex = None # "M" / "F" / None self.energy = 10.0 self.alive = True def step(self, env): """ 个体在每一步做的事: 1. 移动/摄食 2. 生长及性别判定 3. 繁殖 (若成体且碰到异性) 4. 死亡检查 env: 用于获取/更新环境资源 """ # 1. 移动 (随机上下左右一步) dx, dy = random.choice([(1,0),(-1,0),(0,1),(0,-1),(0,0)]) nx = (self.x + dx) % WIDTH ny = (self.y + dy) % HEIGHT self.x, self.y = nx, ny # 摄食 (从当前格子拿资源, 增加能量) available = env.resource_grid[self.y, self.x] consumed = min(consumption_rate, available) self.energy += consumed env.resource_grid[self.y, self.x] -= consumed # 2. 生长 + 性别判定 (若还没有性别且达到某能量或年龄阈值) # 简化: 当energy > 30时,变为成体 -> 决定性别 if (not self.is_adult) and (self.energy > 30): self.is_adult = True # 根据当前资源网格, 或平均资源水平, 决定性别 # 这里演示: p_M(R) = 0.2 + 0.6*(R / K_global) # 但也可更复杂, 结合 self.energy / averageResource local_r = env.resource_grid[self.y, self.x] p_male = 0.2 + 0.6*(local_r / K_global) if random.random() < p_male: self.sex = "M" else: self.sex = "F" # 3. 繁殖 (需要在控制器中与其他 agent 交互, 这里只是示例) # 4. 死亡检查 # 简单规则: 若能量<0, 或其他条件, 则死亡 if self.energy < 0: self.alive = False def reproduce_with(self, partner): """ 和另一只个体繁殖, 产下一个或者多个幼体, 父母是否死亡? """ # 简化: 产下1个幼体, energy= birth_energy_gain child = Lamprey(self.x, self.y) child.energy = birth_energy_gain # 刚出生的能量 # 父母死亡 (一次性繁殖) self.alive = False partner.alive = False return child class Environment: def __init__(self, width, height): self.width = width self.height = height # 每个cell的资源量 self.resource_grid = np.ones((height, width)) * (K_global * 0.5) def step(self): """ 每一步更新环境资源 (如再生) """ for y in range(self.height): for x in range(self.width): R = self.resource_grid[y, x] # 简易 logistic 再生 dR = r * R * (1 - R/K_global) self.resource_grid[y, x] += dR #======================== # 4) 主循环 + 可视化 #======================== def run_simulation(num_steps=100): env = Environment(WIDTH, HEIGHT) # 初始化若干 Lamprey agents = [] for i in range(N_init_agents): x = random.randint(0, WIDTH-1) y = random.randint(0, HEIGHT-1) ag = Lamprey(x, y) agents.append(ag) # 用于记录随时间变化的雌雄数量 male_count_history = [] female_count_history = [] for step_id in range(num_steps): # 1. 环境更新 env.step() # 2. 个体行动 for ag in agents: if ag.alive: ag.step(env) # 2.5 繁殖 (示例: 在同一 cell 中找异性) # 做法:在 step 后,对所有在同格子的成体(M, F)随机配对 pos_dict = {} for ag in agents: if ag.alive and ag.is_adult: pos_key = (ag.x, ag.y) pos_dict.setdefault(pos_key, []).append(ag) new_agents = [] for cell_key, ag_list in pos_dict.items(): # 将雄性、雌性分开 males = [a for a in ag_list if a.sex == "M"] females = [a for a in ag_list if a.sex == "F"] # 取最小数量来配对 n_pairs = min(len(males), len(females)) random.shuffle(males) random.shuffle(females) for i in range(n_pairs): m = males[i] f = females[i] if m.alive and f.alive: # 繁殖 child = m.reproduce_with(f) new_agents.append(child) # 将新生个体加入,并移除死亡个体 agents.extend(new_agents) agents = [a for a in agents if a.alive] # 3. 记录当前雌雄数量 male_count = sum(1 for a in agents if a.alive and a.sex=="M") female_count = sum(1 for a in agents if a.alive and a.sex=="F") male_count_history.append(male_count) female_count_history.append(female_count) # 绘图: 显示 male_count, female_count 随时间变化 plt.figure(figsize=(8,5)) plt.plot(range(num_steps), male_count_history, label="Males", color='blue') plt.plot(range(num_steps), female_count_history, label="Females", color='red') plt.xlabel("Time step") plt.ylabel("Number of agents") plt.title("Lamprey ABM: Males & Females Over Time") plt.legend() plt.grid(True) plt.show() return agents # 运行模拟 final_agents = run_simulation(num_steps=200)
Environment
resource_grid
是一个 2D 数组,每个 cell 都有资源量;step()
方法中用一个简易的 logistic 进行资源再生,当然你也可以设计更复杂的再生机制。Lamprey (Agent)
(x, y)
位置、energy
、is_adult
、sex
等属性;step()
方法演示了移动、摄食、性别判定和死亡的示例逻辑;reproduce_with()
方法中,当繁殖发生后,父母都 alive=False
(一次性繁殖)并产生一个新生个体。主循环
env.step()
更新环境,再让所有活着的 agent 执行他们的动作;提示:上面示例中,没有对“幼体在同一地点的交配”之类做限制,因为我们只在成体间繁殖。若要模拟“幼体 -> 成体”更细致的过程,可以将“energy>30 变成体”改为“年龄>某值”等等,更贴近生物学事实。
在 ABM 中,除简单的“数量随时间变化”曲线外,常见的可视化手段有:
空间分布动画
(x, y)
位置绘制每个 agent;matplotlib.animation
模块)。热力图
resource_grid
在每个时间步进行 imshow()
显示,观察资源在空间上的分布演变。其他统计指标
模型复杂度
数据同化 / 校正
性能与并行
以上示例和思路可以帮助你构建一个包含“可变性别比例”机制的海 lamprey ABM,并在 Python 中进行基本模拟与可视化。根据实际研究需求和数据可得性,你还可以进一步丰富细节、增强生物学真实性,或者结合真实的地理/水文数据来进行更贴近现实的模拟。
import numpy as np
import matplotlib.pyplot as plt
r = 0.8 # 资源的内禀增长率
K = 10**8 # 资源环境承载量
alphaJ = 0.00 # 七鳃鳗(幼体)对资源的消耗效率
alphaF = 0.03 # 七鳃鳗(雌性)对资源的消耗效率
alphaM = 1.77 * alphaF # 七鳃鳗(雄性)对资源的消耗效率
c = 0.002 # 雌雄个体配对繁殖接触率
E = 60000 * 0.063 * 0.9 # 每次成功繁殖产生的幼体数(产卵量*成活率)
muJ = 0.3 # 幼体死亡率
muM = 0.75 # 雄性死亡率
muF = 0.65 # 雌性死亡率
gamma = 0.2 # 幼体向成体转变的速率
def pR(R):
return 0.8 - 0.6 * (R / K) # 当 R=0 => 0.8, R=K => 0.2
def euler_simulation(T=100, dt=0.1):
"""
在时间范围 [0, T] 用步长 dt 做欧拉法数值求解
返回各个时刻与对应的 R, J, M, F 数组
"""
N = int(T / dt) + 1 # 步数(含起始点)
text# 数组初始化 t_vals = np.linspace(0, T, N) R_vals = np.zeros(N) J_vals = np.zeros(N) M_vals = np.zeros(N) F_vals = np.zeros(N) # 初始条件 R_vals[0] = K / 4.0 # R(0)=K/4 J_vals[0] = 0.0 M_vals[0] = 1000.0 F_vals[0] = 1000.0 # Euler 迭代 for n in range(N - 1): Rn = R_vals[n] Jn = J_vals[n] Mn = M_vals[n] Fn = F_vals[n] # 当前时刻的 p(R) p_val = pR(Rn) # 计算微分方程右侧 (dRdt, dJdt, dMdt, dFdt) dRdt = r * Rn * (1 - Rn / K) - (alphaJ * Jn + alphaM * Mn + alphaF * Fn) * Rn dJdt = E * c * Mn * Fn - muJ * Jn - gamma * Jn dMdt = gamma * p_val * Jn - c * Mn * Fn - muM * Mn dFdt = gamma * (1 - p_val) * Jn - c * Mn * Fn - muF * Fn # 欧拉法更新 R_vals[n + 1] = Rn + dt * dRdt if Rn + dt * dRdt > 0 else 0 J_vals[n + 1] = Jn + dt * dJdt M_vals[n + 1] = Mn + dt * dMdt F_vals[n + 1] = Fn + dt * dFdt return t_vals, R_vals, J_vals, M_vals, F_vals
t, R_sol, J_sol, M_sol, F_sol = euler_simulation(T=20, dt=0.1)
fig, ax = plt.subplots(1, 2,figsize=(8, 5))
ax[0].plot(t, M_sol, label='Male (M)', color='blue')
ax[0].plot(t, F_sol, label='Female (F)', color='red')
ax[0].set_xlabel('Time')
ax[0].set_ylabel('Population size')
ax[0].set_title('Euler method: M(t) and F(t) vs Time')
ax[1].plot(t, R_sol, label='Resource (R)', color='brown')
ax[1].set_xlabel('Time')
ax[1].set_ylabel('Population / Resource')
ax[1].set_title('Euler method: R(t) vs Time')
plt.legend()
plt.grid(True)
plt.show()
这个模型用来描述最开始的问题,似乎还有点欠缺。你有什么办法把它改进改进吗
下面给出几条思路,帮助你在当前这段 ODE 代码的基础上做进一步的改进与完善,从而更贴近“雌雄配对繁殖、一次性繁殖后死亡、幼体发育、资源随时间变化、性别比例可变”等机制,也让模型更符合最初问题提出的需求。
下文从模型机制上的完善和具体公式实现两方面给出建议,你可以根据自己的研究重点与数据可得性,选择性地进行修改或扩展。
在七鳃鳗等生物中(尤其在海水/淡水洄游产卵类型中),常见的是一次性繁殖后,雄雌都迅速死亡。当前模型中,繁殖对雄雌个体的损耗只是通过接触率 c 减少出生(幼体 J)的竞争资源,但并没有让雄雌在繁殖时立即死亡。若想引入“一次性繁殖”的生态特征,可以:
现在你的代码里也有 这一项,但它目前和死亡率 或 并列,最好明确解释:“繁殖”项和“自然死亡”项是怎么叠加的。你可以让 的系数更大,或把 设成较小,以突显繁殖导致的死亡是主要去除机制。
举例:
python# dMdt = gamma * p_val * Jn - c * Mn * Fn - muM * Mn # \________发育成雄性__/ \___繁殖后死亡_/
其中 就表示“发生配对后雄性个体数减少”,雌性方程中也有对应的 。
若想细化“并非所有雄雌都能在一次繁殖中都死亡”,可改成 (0<θ<1),只移除一部分成功交配的个体。
目前模型把新出生的都直接视为“幼体”(J)。在实际生物学中,可能需要区分:卵 → 幼体 → ...。也就是说,刚产卵后,不一定马上算到“幼体”池子里。可以引入一个新变量 表示卵量,再加上卵阶段到幼体阶段的转换(成活率和时间延迟),更接近真实过程。比如:
若不想额外增加方程变量,但想稍微加一些时间延迟,也可用时滞微分方程(delay differential equations)来处理,不过实现会更复杂。
在当前模型中,你使用了线性项 表示七鳃鳗对资源的消耗,这属于简单质量作用(Mass-Action)。但在真实捕食/寄生过程中,常常会存在饱和效应(如 Holling Type II, III Functional Response)。如果你想让模型更生态学一些,可以替换那部分为:
或更常见的是
以此反映“当资源很充足时,也会有捕食/寄生速率的上限,而不是无限增长”。这能避免资源很大时被“线性消耗”带来的不合理波动。
你在代码里把 p(R) 设成了简单线性:当资源从 0 到 K 时,p(R) 线性从 0.8 到 0.2。若你想让“低资源→更多雄性”更平滑,还可使用Sigmoid 或阈值型函数。例如:
pythonimport math def pR(R): # R0是中点, kappa控制陡峭度 # 当 R < R0 => p_M ~ 1, R>R0 => p_M ~ 0 # 这里做一个示例:R0=K/2, kappa=10 R0 = K/2 kappa = 10 return 1 / (1 + math.exp(kappa*(R - R0)/R0))
这样你可以更灵活地控制性别比例如何随资源变化。如果有实验数据(比如在资源=某个值时,雄性概率=多少),可以拟合该函数形状。
若你对年龄结构或不同发育阶段(幼体1/幼体2/成体前期/繁殖成体等)的区分较细,可以把当前“幼体 成体”的转变率 改为一个多阶段结构,用多变量/多方程来表示:
并在每一阶段设置相应的死亡率、资源消耗率、转移率(类似 Leslie 矩阵)。这有助于捕捉七鳃鳗复杂的分期发育过程。但缺点是 ODE 方程会多起来,需要更多参数和数据支持。
在真实情况下,海/湖栖七鳃鳗常常会溯河产卵,然后成体死亡。这一过程具有明显的季节性(繁殖集中在特定季节)和空间异质性(在河口到上游的不同区域)。若你想反映这个过程,可以在模型里:
当然,这会让模型更复杂,但也更贴近实际生态系统。
最后,如果你有野外调查或历史数据(如每年在产卵河段统计到多少雄、多少雌、多少幼体),可以考虑对现有的参数 (alphaJ, alphaM, alphaF, c, E, muJ, muM, muF, gamma, r, K, p(R) 的具体形状...)
做最小二乘或极大似然拟合,从而让模型的时间序列更贴近真实。同时也要做敏感性分析,看模型对哪些参数最敏感,从而指导你未来在数据收集和管理措施上应优先关注哪些因素。
以下给出一个更明确的示例,展示如何将繁殖后“雌雄都死”放大,并减少常规死亡率,使繁殖死亡成为主要去除机制(仅作范例,具体数值可调整)。
pythonimport numpy as np import matplotlib.pyplot as plt # ----------------------------- # 1) 参数设置(示例数值) # ----------------------------- r = 0.8 # 资源的内禀增长率 K = 1e8 # 资源环境承载量 alphaJ = 0.00 alphaF = 0.03 alphaM = 1.77 * alphaF c = 0.002 # 雌雄配对繁殖接触率 E = 60000 * 0.063 * 0.9 muJ = 0.3 # 幼体死亡率(保持原样) muM = 0.2 # 雄性死亡率,改小一点 muF = 0.2 # 雌性死亡率,也改小一点 gamma = 0.2 # 幼体→成体转换率 # 让繁殖接触率 c 更大一点,或者让繁殖死亡更显著 # p(R) 函数(低资源 -> 高雄性概率) def pR(R): # 保持原来的线性示例: R=0 => 0.8, R=K => 0.2 return 0.8 - 0.6 * (R / K) # ----------------------------- # 2) 定义 Euler 法模拟函数 # ----------------------------- def euler_simulation(T=20, dt=0.1): N = int(T / dt) + 1 t_vals = np.linspace(0, T, N) R_vals = np.zeros(N) J_vals = np.zeros(N) M_vals = np.zeros(N) F_vals = np.zeros(N) # 初始条件 R_vals[0] = K / 4.0 J_vals[0] = 0.0 M_vals[0] = 1000.0 F_vals[0] = 1000.0 for n in range(N - 1): Rn = R_vals[n] Jn = J_vals[n] Mn = M_vals[n] Fn = F_vals[n] p_val = pR(Rn) # ---------------------- # 资源变化 # ---------------------- dRdt = r*Rn*(1 - Rn/K) - (alphaJ*Jn + alphaM*Mn + alphaF*Fn)*Rn # ---------------------- # 幼体数量 # ---------------------- # E*c*M*F 表示繁殖产生幼体 # - muJ*J 幼体自然死亡 # - gamma*J 幼体变成成体(离开幼体池) dJdt = (E * c * Mn * Fn) - muJ*Jn - gamma*Jn # ---------------------- # 雄性数量 # ---------------------- # + gamma*p(R)*J => 幼体变成雄性 # - c*M*F => 繁殖后雄性死亡(一次性繁殖) # - muM*M => 雄性的自然死亡 dMdt = gamma*p_val*Jn - c*Mn*Fn - muM*Mn # ---------------------- # 雌性数量 # ---------------------- # + gamma*(1 - p(R))*J # - c*M*F => 一次性繁殖死亡 # - muF*F => 自然死亡 dFdt = gamma*(1 - p_val)*Jn - c*Mn*Fn - muF*Fn # ---------------------- # 欧拉法更新 # ---------------------- R_next = Rn + dt * dRdt J_next = Jn + dt * dJdt M_next = Mn + dt * dMdt F_next = Fn + dt * dFdt # 防止资源出现负数 if R_next < 0: R_next = 0 R_vals[n+1] = R_next J_vals[n+1] = J_next if J_next > 0 else 0 M_vals[n+1] = M_next if M_next > 0 else 0 F_vals[n+1] = F_next if F_next > 0 else 0 return t_vals, R_vals, J_vals, M_vals, F_vals # ----------------------------- # 3) 运行模拟 & 绘图 # ----------------------------- t, R_sol, J_sol, M_sol, F_sol = euler_simulation(T=20, dt=0.1) fig, ax = plt.subplots(1,2, figsize=(10,4)) # (a) M(t), F(t), J(t) ax[0].plot(t, M_sol, label='Male (M)', color='blue') ax[0].plot(t, F_sol, label='Female (F)', color='red') ax[0].plot(t, J_sol, label='Juvenile (J)', color='green') ax[0].set_xlabel('Time') ax[0].set_ylabel('Population size') ax[0].set_title('M(t), F(t), J(t) over Time') ax[0].legend() ax[0].grid(True) # (b) R(t) ax[1].plot(t, R_sol, label='Resource (R)', color='brown') ax[1].set_xlabel('Time') ax[1].set_ylabel('Resource') ax[1].set_title('R(t) over Time') ax[1].grid(True) plt.tight_layout() plt.show()
在这个示例中:
这样就能一步步让你的模型更贴近题目中所说的“雌雄配对繁殖(一次性)、幼体发育、可变性别比例以及资源消耗”等复杂生态过程。最终,你还可以将这个 ODE 模型扩展为ABM(若需要模拟空间异质性和个体差异),或在多种环境场景/管理策略下进行仿真预测。
还有一个问题,根据生态系统的稳态调节,一个种群的生存率和死亡率不可能一成不变,所以你应当做出调整,当种群数量过大时,应当增大种群的死亡率;反之,种群数量减小,应当减小种群的死亡率。
在生态系统中,种群的死亡率往往并非一个固定常数,而会随着种群密度或资源条件发生变化(这称为密度制约或密度依赖调节**,density-dependent regulation**)。
当种群数量过大时,个体间的资源竞争会加剧、疾病传染率也可能增高,从而使死亡率升高;反之,当种群数量很小、资源相对充沛时,个体死亡率往往会变得更低。
下面给出几种将“种群数量影响死亡率”引入当前 ODE 模型的常见方法,并以示例公式或代码片段进行说明。
我们先以七鳃鳗(Lamprey)的雄性个体为例,原本在方程里有一项 ,其中 是常数死亡率。如果想让它随总种群数量(或随雄性数量本身)增加,可以把 写成一个随种群规模变化的函数,例如:
线性密度依赖
抛物线或S型
资源依赖
以你的 ODE 模型为例,原本对于雄性数量 的方程是:
如果我们使用线性密度依赖,令:
则可改写成:
雌性数量 同理,可以写:
或使用另一组参数 表示雌性特有的死亡率常量与系数。
假设我们把“雄雌成体总数”记为 ,并让雄性的死亡率变为
雌性的死亡率变为
那么在 Python 中的欧拉迭代示例,可做如下调整(伪代码):
pythondef euler_simulation(T=20, dt=0.1): ... # 假设线性密度依赖: muM(A)=muM0 + a*(M+F), muF(A)=muF0 + b*(M+F) muM0 = 0.2 muF0 = 0.2 a = 0.001 b = 0.001 for n in range(N-1): Rn = R_vals[n] Jn = J_vals[n] Mn = M_vals[n] Fn = F_vals[n] A = Mn + Fn # 成体总数 # 动态计算死亡率 muM_dynamic = muM0 + a*A muF_dynamic = muF0 + b*A p_val = pR(Rn) # 资源依赖性别分配 # 资源更新方程 (不变) dRdt = r*Rn*(1 - Rn/K) - (alphaJ*Jn + alphaM*Mn + alphaF*Fn)*Rn # 幼体 J dJdt = E*c*Mn*Fn - muJ*Jn - gamma*Jn # 雄性 M dMdt = gamma*p_val*Jn - c*Mn*Fn - muM_dynamic*Mn # 雌性 F dFdt = gamma*(1 - p_val)*Jn - c*Mn*Fn - muF_dynamic*Fn # Euler 更新 ... ...
这样就实现了一个随成体总数增大而增加死亡率的机制,从而当 多到一定程度时, 和 就变大,会自然抑制成体数量的爆发。
- 如果想让幼体也有密度依赖死亡,可将 similarly 设为 。
- 也可让死亡率依赖资源 而不直接依赖于 —— 例如当资源低时,死亡率提升。具体如何设定要看你对生物学机制的理解,以及你有没有观测数据来支持这样的函数形式。
避免不受控的“人口爆发”
与资源消耗/生长结合
真实数据校准
通过这些改动,你就能让模型在种群数量过高时自动提高死亡率、在数量较低时回落,从而更符合生态系统中“稳态调节”或“负反馈”规律。
能否给出更加完整的python代码,并更加详细的解释解释每个变量的来源以及含义,并指出他们是如何计算的?
下面给出一个更加完整、易于扩展的 Python 代码示例,演示如何在现有模型中引入:
并对每个主要变量与参数的生物学含义和来源作详细解释。请注意,这只是一个“教学/示范”性质的代码,具体数值需要根据你的研究对象或数据校准。
我们用四个状态变量:
这些变量的随时间变化由下式决定(欧拉法离散):
例如让
当越大,越高;当成体数量很少时,则趋近。
下面是一段可直接运行的代码示例。我们添加了足够多的注释来解释每个变量与其计算过程,以及如何在欧拉法中更新。
pythonimport numpy as np import matplotlib.pyplot as plt def sigmoid_pR(R, R_half, steepness, p_low=0.8, p_high=0.2): """ 资源依赖的性别概率函数, 用Sigmoid示例: 当 R < R_half => p(R) ~ p_low, 当 R > R_half => p(R) ~ p_high, steepness 决定曲线陡峭度. 这里 p_low>p_high 表示低资源时倾向雄性(0.8). """ # 范围: p_low ~ p_high # Sigmoid中点 = R_half # 如果 steepness>0, 表示由高到低. 需要具体推导 # 下面是一种简单写法: s = 1 / (1 + np.exp(steepness*(R - R_half)/R_half)) # s在0~1之间变化; 当 R=R_half => s=0.5 # 最终p(R)在 p_low~p_high之间: return p_low + (p_high - p_low)*s def euler_simulation( T=20, dt=0.1, # 资源参数 r=0.8, K=1e8, # 消耗效率 alphaJ=0.00, alphaM=0.05, alphaF=0.03, # 繁殖参数 c=0.002, E=3000, # 死亡率(幼体) muJ=0.3, # 基础死亡率(成体) muM0=0.2, muF0=0.2, # 密度依赖系数(成体) aM=1e-4, aF=1e-4, # 幼体->成体转变速率 gamma=0.2, # 性别依赖函数 p_func=lambda R: 0.8 - 0.6*(R/1e8), # 初始条件 R0=None, J0=0, M0=1000, F0=1000 ): """ 使用欧拉法对上述模型做数值模拟. 参数说明(仅列出关键): ------------------------------------ 1) r, K : 资源 logistic 增长参数 2) alphaJ, alphaM, alphaF : 幼体/雄/雌 对资源的消耗系数 3) c : 雌雄配对速率 4) E : 每次繁殖产生的幼体数 5) muJ : 幼体死亡率(常数; 也可做成密度依赖) 6) muM0, muF0 : 雄性、雌性的基础死亡率(在低密度时) 7) aM, aF : 密度依赖系数. 当 M+F 增大时, 死亡率升高 8) gamma : 幼体向成体的转变率 9) p_func : 性别分配函数 p(R), 给定资源 R 返回成为雄性的概率 10) R0, J0, M0, F0 : 初始条件 返回:t_vals, R_vals, J_vals, M_vals, F_vals """ if R0 is None: R0 = K/4.0 # 默认资源初始值 = K/4 # 时间离散 N = int(T/dt) + 1 t_vals = np.linspace(0, T, N) # 各变量数组 R_vals = np.zeros(N) J_vals = np.zeros(N) M_vals = np.zeros(N) F_vals = np.zeros(N) # 初值 R_vals[0] = R0 J_vals[0] = J0 M_vals[0] = M0 F_vals[0] = F0 for n in range(N-1): Rn = R_vals[n] Jn = J_vals[n] Mn = M_vals[n] Fn = F_vals[n] # 成体总数 (用于密度依赖死亡) A = Mn + Fn # 动态死亡率 muM = muM0 + aM * A # 雄性的实际死亡率 muF = muF0 + aF * A # 雌性的实际死亡率 # 资源->性别概率 p_val = p_func(Rn) # ---- 1) dR/dt ---- dRdt = r*Rn*(1 - Rn/K) - (alphaJ*Jn + alphaM*Mn + alphaF*Fn)*Rn # ---- 2) dJ/dt ---- # E*c*M*F : 繁殖产生的幼体 # - muJ*J : 幼体自然死亡 # - gamma*J: 幼体变成成体 dJdt = E*c*Mn*Fn - muJ*Jn - gamma*Jn # ---- 3) dM/dt ---- # + gamma*p_val*J : 幼体成雄 # - c*M*F : 一次性繁殖死亡 # - muM*M : 密度依赖死亡 dMdt = gamma*p_val*Jn - c*Mn*Fn - muM*Mn # ---- 4) dF/dt ---- # + gamma*(1-p_val)*J : 幼体成雌 # - c*M*F : 繁殖死亡 # - muF*F : 密度依赖死亡 dFdt = gamma*(1 - p_val)*Jn - c*Mn*Fn - muF*Fn # 欧拉更新 R_next = Rn + dt*dRdt J_next = Jn + dt*dJdt M_next = Mn + dt*dMdt F_next = Fn + dt*dFdt # 不允许资源<0, 或者可做 max(0, R_next) R_next = max(R_next, 0) J_next = max(J_next, 0) M_next = max(M_next, 0) F_next = max(F_next, 0) R_vals[n+1] = R_next J_vals[n+1] = J_next M_vals[n+1] = M_next F_vals[n+1] = F_next return t_vals, R_vals, J_vals, M_vals, F_vals # =========== 运行与绘图 ============ if __name__ == "__main__": # 这里给一个例子: 使用 Sigmoid 函数 p_func # 低资源 -> p~0.8 (偏雄性), 高资源 -> p~0.2 # R_half=K/2, steepness=5 def my_pR(R): return sigmoid_pR(R, R_half=1e8/2, steepness=5, p_low=0.8, p_high=0.2) t, R_sol, J_sol, M_sol, F_sol = euler_simulation( T=50, dt=0.1, r=0.8, K=1e8, alphaJ=0.0, alphaM=0.045, alphaF=0.03, c=0.001, E=50000, muJ=0.3, muM0=0.1, muF0=0.1, aM=2e-4, aF=2e-4, gamma=0.2, p_func=my_pR, R0=None, # 默认为K/4 J0=0, M0=2000, F0=2000 ) # 可视化 fig, axes = plt.subplots(1,2, figsize=(12,5)) # 左图: J, M, F axes[0].plot(t, J_sol, label='Juvenile (J)', color='green') axes[0].plot(t, M_sol, label='Male (M)', color='blue') axes[0].plot(t, F_sol, label='Female (F)', color='red') axes[0].set_xlabel("Time") axes[0].set_ylabel("Population size") axes[0].set_title("J, M, F vs. Time") axes[0].legend() axes[0].grid(True) # 右图: R axes[1].plot(t, R_sol, label='Resource (R)', color='brown') axes[1].set_xlabel("Time") axes[1].set_ylabel("Resource quantity") axes[1].set_title("Resource R vs. Time") axes[1].legend() axes[1].grid(True) plt.tight_layout() plt.show()
:常见于 Logistic 模型的资源增长;
:对资源的消耗(或寄生)效率;
:雌雄配对速率;
:每次繁殖实际带来的新幼体数量(已考虑卵数、孵化率、早期死亡率);
:基础死亡率;
:成体密度依赖死亡的系数;
:幼体变成成体的速率;
:依赖资源的性别分配函数;
初始条件
该代码与解释展示了一个相对“完整”的 ODE 框架,既包含:
并通过欧拉法做数值模拟和结果绘图。你可以在此基础上根据具体的生物学数据和情景做参数校准、敏感性分析,以及对不同管理策略(如控制繁殖、捕捞等)进行情景预测。
这个模型还是存在问题,种群没办法维持在一个稳定的数量,经常数量爆炸或者灭绝。出生率应当和种群的性别比例有关,你应当再设置相关的模型进行解释。同时,种群的数量增长会导致资源匮乏,资源的匮乏会增大死亡率,你也应当做出相应的模型
下面给出一个进一步扩展的 ODE 模型示例,用以克服“要么爆发要么灭绝”的极端动态,努力让种群在一定区间内保持相对稳定。该模型在原有基础上,额外考虑了以下两点,并给出了对应的完整 Python 代码示例与解释:
我们依然使用四个状态变量:
相对于此前的模型,主要改进点:
此前的繁殖项常写为 。但当 与 相差很大时,这种写法仍可产生非常多幼体。为体现雌雄配对需要“两个数量相当才最有效”,我们采用一种常见处理:
让死亡率随资源水平降低而上升。例如:
同理,给雌性、幼体也设置相似的资源依赖死亡率。根据需求也可针对幼体区分更高的资源敏感度等。
归纳成方程(连续形式)如下,可用欧拉法离散:
\begin{aligned} \frac{dR}{dt} &= r R\!\bigl(1 - \frac{R}{K}\bigr) \;-\;\bigl(\alpha_J J + \alpha_M M + \alpha_F F\bigr)\cdot R,\\[6pt] \frac{dJ}{dt} &= \underbrace{E \,c\,\frac{2MF}{\,M+F\,}}_{\text{出生(若 }M+F>0)} -\; \mu_J(R)\,J \;-\; \gamma\,J,\\[6pt] \frac{dM}{dt} &= \underbrace{\gamma\,p(R)\,J}_{\text{幼体→雄}} \;-\;\underbrace{c\,\frac{2MF}{\,M+F\,}}_{\text{繁殖死亡}} \;-\; \mu_M(R)\,M,\\[6pt] \frac{dF}{dt} &= \underbrace{\gamma\,\bigl[1-p(R)\bigr]\,J}_{\text{幼体→雌}} \;-\; c\,\frac{2MF}{\,M+F\,} \;-\; \mu_F(R)\,F. \end{aligned}其中:
下面是一份可直接运行的示例,数值仅用于演示,需根据实际数据进行校准或敏感性分析。
pythonimport numpy as np import matplotlib.pyplot as plt def phi(M, F): """ 性别比例相关的繁殖效率函数: phi(M,F) = 2*M*F/(M+F). 若 M+F=0, 返回0. """ denom = M+F return 0 if denom<=0 else 2.0*M*F/denom def resource_dependent_mortality(R, K, mu0, a): """ 当 R < K 时, (K-R)/K 为正, 表示资源不足, 额外提高死亡率. 当 R>=K 时, 额外项=0, 死亡率=mu0. """ short = max(0, K - R) # (K-R)_+ return mu0 + a*(short/K) def pR(R, K): """ 资源依赖的性别比例函数 p(R): 这里示例:当资源很低 -> p(R) ~ 0.8(偏雄), 当资源=K -> p(R) ~ 0.2(偏雌). 做一个线性插值(可改成 Sigmoid). """ return 0.8 - 0.6*(R/K) # R=0 =>0.8, R=K=>0.2 def euler_simulation( T=50, dt=0.1, # 资源及其增长 r=0.8, K=1e6, # 消耗效率 alphaJ=0.01, alphaM=0.03, alphaF=0.02, # 繁殖参数 c=0.001, E=500, # 幼体死亡 muJ0=0.2, aJ=0.4, # 雄性死亡 muM0=0.1, aM=0.6, # 雌性死亡 muF0=0.1, aF=0.6, # 幼体发育 gamma=0.2, # 初值 R0=None, J0=0, M0=1000, F0=1000 ): """ 使用欧拉法对上述改进模型数值模拟: 1) 出生取决于性别比例: c*E*phi(M,F) 2) 死亡率随资源短缺而升高: mu(R)= mu0 + a*(K-R)/K 3) 幼体->成体: gamma*p(R), gamma*(1-p(R)) 4) 一次性繁殖后死亡: - c*phi(M,F) 返回: t, R_vals, J_vals, M_vals, F_vals """ if R0 is None: R0 = K/2 # 默认资源一半 N = int(T/dt)+1 t_vals = np.linspace(0, T, N) R_vals = np.zeros(N) J_vals = np.zeros(N) M_vals = np.zeros(N) F_vals = np.zeros(N) # 初始 R_vals[0] = R0 J_vals[0] = J0 M_vals[0] = M0 F_vals[0] = F0 for n in range(N-1): Rn = R_vals[n] Jn = J_vals[n] Mn = M_vals[n] Fn = F_vals[n] # 资源logistic + 消耗 dRdt = r*Rn*(1 - Rn/K) - (alphaJ*Jn + alphaM*Mn + alphaF*Fn)*Rn # 出生(幼体增加) births = E * c * phi(Mn, Fn) # c*E*(2*M*F/(M+F)) # 资源依赖死亡率 muJ = resource_dependent_mortality(Rn, K, muJ0, aJ) muM = resource_dependent_mortality(Rn, K, muM0, aM) muF = resource_dependent_mortality(Rn, K, muF0, aF) # 幼体 J # + births - muJ*J - gamma*J dJdt = births - muJ*Jn - gamma*Jn # 雄性 M # + gamma*p(R)*J - c*phi(M,F) - muM*M p_val = pR(Rn, K) dMdt = gamma*p_val*Jn - c*phi(Mn, Fn) - muM*Mn # 雌性 F # + gamma*(1-p(R))*J - c*phi(M,F) - muF*F dFdt = gamma*(1-p_val)*Jn - c*phi(Mn, Fn) - muF*Fn # 欧拉更新 R_next = Rn + dt*dRdt J_next = Jn + dt*dJdt M_next = Mn + dt*dMdt F_next = Fn + dt*dFdt # 不允许出现负数 R_next = max(0, R_next) J_next = max(0, J_next) M_next = max(0, M_next) F_next = max(0, F_next) R_vals[n+1] = R_next J_vals[n+1] = J_next M_vals[n+1] = M_next F_vals[n+1] = F_next return t_vals, R_vals, J_vals, M_vals, F_vals # ---------- 测试与画图 ---------- if __name__=="__main__": t, R_sol, J_sol, M_sol, F_sol = euler_simulation( T=100, dt=0.2, K=1e6, R0=5e5, J0=0, M0=2000, F0=2000, r=0.5, alphaJ=0.01, alphaM=0.03, alphaF=0.02, c=0.0005, E=2000, muJ0=0.2, aJ=0.4, muM0=0.1, aM=0.8, muF0=0.1, aF=0.8, gamma=0.2 ) fig, axes = plt.subplots(1,2, figsize=(12,5)) # 左图:种群 axes[0].plot(t, J_sol, label='Juvenile J', color='green') axes[0].plot(t, M_sol, label='Male M', color='blue') axes[0].plot(t, F_sol, label='Female F', color='red') axes[0].set_xlabel("Time") axes[0].set_ylabel("Population size") axes[0].set_title("J, M, F vs Time") axes[0].legend() axes[0].grid(True) # 右图:资源 axes[1].plot(t, R_sol, label='Resource R', color='brown') axes[1].set_xlabel("Time") axes[1].set_ylabel("Resource") axes[1].set_title("R(t)") axes[1].grid(True) plt.tight_layout() plt.show()
phi(M,F)
resource_dependent_mortality(R,K,mu0,a)
pR(R,K)
出生项:.
一次性繁殖后死亡:
资源消耗:
参数
即便引入了繁殖效率受性别比例限制、资源匮乏提高死亡率等机制,系统仍可能在某些参数下出现大幅波动甚至混沌。因为生态系统本身就具有非线性和时滞特征,而“一次性繁殖”的脉冲式行为也常导致显著振荡。
通常要想在某个范围内维持相对平稳,需要精细调整:
并在实际研究中用实验或田野数据校正,做敏感性分析。这正是生态模型常见的工作流程。
通过上述改动,你的模型就能够在较广范围内(并非一定,但会更容易地)出现“在一定水平上下波动或逐渐稳定”,而不会总是无限制爆发或快速灭绝,从而更符合真实生态系统常见的“稳态调节”特征。
模型依旧存在问题,或许你应当适当修改其中的数值、机制等。现在种群数量并不能维持稳态。七鳃鳗在种群数量减少的时候,应当会调节性别比例增大出生率,把濒临灭绝的种群拉回正常的数量,而不是像现在这样容易灭绝。针对模型方程应当继续做出更改
下面给出一个进一步改进的示例模型与对应的 Python 实现思路,目的是在原有“雌雄配对繁殖、资源依赖死亡率、可变性别分配”等机制上,引入更能防止种群过快灭绝或极端失衡的调节方法,使模型更容易在一定范围内维持相对稳态或可持续振荡。
关键改进点主要体现在两部分:
以下示例仍是一个演示性质的 ODE 模型,实际项目中需根据生物学事实和数据做参数校准与敏感性分析。
我们继续使用四个变量:
核心方程仍类似先前,但做了三处强化/调整:
性别决定函数
繁殖函数 的改动
资源依赖死亡率 仍保留
令
当种群小到 时,这个函数会倾向维持一个基础值 ;如果此时资源还很高,则 可能非常低(或高),从而有机会让幼体大量变雌(或变雄)。
这样,即便某一时刻 非常少,下一代有更大概率出生雄性,从而避免雌性“独木难支”而灭绝的情况;同理如果雌性过少也会做相反调整。
示例:在 中加一个小的 baseline,保证极端失衡时也有微量繁殖:
继续采用类似:
雌、幼体同理。
若想在种群很小时进一步降低死亡率,还可在函数中结合 “” 等更复杂形式;具体看需求。
以下是一段可执行的演示代码,尝试实现上述改动。请注意这是“原型”示例,数值参数只是为了展示思路;你需要根据实际生物学数据进行反复调参。
pythonimport numpy as np import matplotlib.pyplot as plt # ========== 1. 改进的繁殖函数 ========== def phi(M, F, l=1e-3, delta=1e-3): """ 带有小baseline的配对效率函数: phi(M,F) = l + 2*M*F/(M+F+delta) 当M或F很小也不至于 =0; l是非常微小的baseline """ return l + 2.0*M*F/(M+F+delta) if (M+F>0) else 0 # ========== 2. 资源依赖死亡率 ========== def resource_mortality(R, K, mu0, a): """ 当资源不足(K-R>0)时, 死亡率 = mu0 + a*(K-R)/K """ short = max(0, K-R) return mu0 + a*(short/K) # ========== 3. 资源 + 性别修正的p(M,F,R) ========== def sex_ratio(M, F, R, K, base_low=0.8, # 资源极端匮乏 -> p=0.8(偏雄) base_high=0.2, # 资源极端丰盈 -> p=0.2(偏雌) d=0.3, # 平衡雌雄的灵敏度 eps=1e-5): """ p(M,F,R) = clamp( p_R(R) + d*(F-M)/(M+F+eps) ) 其中 p_R(R) = base_low - (base_low-base_high)*(R/K) 如果 (F>>M), 则该项>0 => p增大 => 更多幼体变雄性; 反之 (M>>F), p减少 => 更多幼体变雌性 """ # 先做简单线性资源影响 pR # R=0 => p=base_low, R=K => p=base_high p_r = base_low - (base_low - base_high)*(R/K) # 雌雄不平衡修正 diff_term = 0.0 denom = M+F+eps if denom>0: diff_term = d*((F - M)/denom) raw = p_r + diff_term # clamp到[0.01, 0.99] 避免绝对0或1 p_val = max(0.01, min(0.99, raw)) return p_val def euler_model( T=100, dt=0.5, # 资源Logistic r=0.8, K=1e6, # 消耗效率 alphaJ=0.01, alphaM=0.03, alphaF=0.02, # 繁殖率 c=0.0005, E=2000, # baseline繁殖的参数 l, delta => 内置到phi里 l_phi=1e-3, delta_phi=1e-3, # 幼体->成体 gamma=0.2, # 死亡率参数 muJ0=0.2, aJ=0.6, muM0=0.1, aM=0.8, muF0=0.1, aF=0.8, # 性别修正 d_sex=0.3, # 初值 R0=None, J0=100, M0=200, F0=200 ): """ 改进版示例: 1) phi(M,F)带小baseline, 避免一方极少时彻底无繁殖. 2) p(M,F,R)=p_r(R)+d*(F-M)/(M+F) => 如果F>>M, 幼体更多变雄. 3) 资源依赖死亡率 + 一次性繁殖死亡. """ if R0 is None: R0 = K/2.0 N = int(T/dt) + 1 t_vals = np.linspace(0, T, N) R_vals = np.zeros(N) J_vals = np.zeros(N) M_vals = np.zeros(N) F_vals = np.zeros(N) R_vals[0] = R0 J_vals[0] = J0 M_vals[0] = M0 F_vals[0] = F0 for n in range(N-1): Rn = R_vals[n] Jn = J_vals[n] Mn = M_vals[n] Fn = F_vals[n] # 资源增长 - 消耗 dRdt = r*Rn*(1 - Rn/K) - (alphaJ*Jn + alphaM*Mn + alphaF*Fn)*Rn # 幼体出生: E*c*phi(M,F) births = E*c*phi(Mn, Fn, l=l_phi, delta=delta_phi) # 资源依赖死亡率 muJ = resource_mortality(Rn, K, muJ0, aJ) muM = resource_mortality(Rn, K, muM0, aM) muF = resource_mortality(Rn, K, muF0, aF) # 幼体方程 # + births - muJ*J - gamma*J dJdt = births - muJ*Jn - gamma*Jn # 计算本时刻的性别转化概率: p_val = sex_ratio(Mn, Fn, Rn, K, d=d_sex) # 雄性方程 # + gamma*p_val*J - c*phi(M,F) - muM*M dMdt = gamma*p_val*Jn - c*phi(Mn, Fn, l=l_phi, delta=delta_phi) - muM*Mn # 雌性方程 # + gamma*(1-p_val)*J - c*phi(M,F) - muF*F dFdt = gamma*(1-p_val)*Jn - c*phi(Mn, Fn, l=l_phi, delta=delta_phi) - muF*Fn # 欧拉法 R_next = Rn + dt*dRdt J_next = Jn + dt*dJdt M_next = Mn + dt*dMdt F_next = Fn + dt*dFdt # 防止出现负数 R_next = max(R_next, 0) J_next = max(J_next, 0) M_next = max(M_next, 0) F_next = max(F_next, 0) R_vals[n+1] = R_next J_vals[n+1] = J_next M_vals[n+1] = M_next F_vals[n+1] = F_next return t_vals, R_vals, J_vals, M_vals, F_vals # ---- 运行模拟 & 绘图 ---- if __name__=="__main__": t, R_sol, J_sol, M_sol, F_sol = euler_model( T=200, dt=1.0, K=1e6, R0=5e5, J0=50, M0=100, F0=100, r=0.5, alphaJ=0.01, alphaM=0.03, alphaF=0.02, c=0.0005, E=2000, l_phi=1e-3, delta_phi=1e-3, gamma=0.2, muJ0=0.2, aJ=0.6, muM0=0.1, aM=0.8, muF0=0.1, aF=0.8, d_sex=0.3 ) fig, axes = plt.subplots(1,2, figsize=(12,5)) # (1) 种群变化 axes[0].plot(t, J_sol, label='Juvenile(J)', color='green') axes[0].plot(t, M_sol, label='Male(M)', color='blue') axes[0].plot(t, F_sol, label='Female(F)', color='red') axes[0].set_xlabel("Time") axes[0].set_ylabel("Population") axes[0].set_title("J, M, F over time") axes[0].legend() axes[0].grid(True) # (2) 资源 axes[1].plot(t, R_sol, label='Resource R', color='brown') axes[1].set_xlabel("Time") axes[1].set_ylabel("R") axes[1].set_title("Resource over time") axes[1].grid(True) plt.tight_layout() plt.show()
即便上述模型已经做了如下改进:
系统仍可能出现振荡、准周期或在某些参数下依然出现灭绝/爆发,因为自然生态系统本身就是高度非线性且容易出现复杂动力学。
如果模拟中你发现种群依然会有大幅波动或灭绝,通常可从以下方面微调:
最终要通过对参数做灵敏度分析和与真实数据的比较来找到一组让模型“既不会轻易灭绝,也不会疯狂爆发”的折中点。
这样修改后,你会发现种群在数量偏低时会倾向产生更多短缺性别、并有一定小概率仍可繁殖,从而有更大机会维持在非零水平;当数量偏高、资源紧缺时,死亡率上升而抑制种群。尽管如此,由于生态动力学的复杂性,此模型仍然可能出现周期波动,需进一步做敏感性分析或在更精细的机制(如个体行为、空间结构)上做深化。
在你的模型里似乎资源都是一次性的?你应当做出调整,资源的量应当和种群的数量成负相关的关系,也就是当种群数量很多的时候,资源的量应当适当减少;种群数量很小的时候,资源的量应当增多
下面给出几种让资源量与种群数量呈负相关的思路,并附带可以在 Python 模型中实现的方式示例。总体而言,你想强调的是:** 当种群数量很多时,资源应该显著减少**;当种群数量较少时,资源应该有机会恢复或变得相对充足。这在生态学中常被称作**“负反馈”或密度制约**的一种体现。
模型实现上有多种做法,这里先介绍三类常见思路:
这是最直接的做法:资源量 并不作为一个独立随时间变动的变量,而是由“总种群数量”决定。例如:
示例(伪代码):
python# 在计算 dMdt, dFdt, 等等时: N = Jn + Mn + Fn R_effective = max(0, K - a*N) # 资源对人口的负相关 # 然后在死亡率 / 繁殖概率 / 消耗等地方 用 R_effective
优点:实现简单;体现“越多种群,越少资源”。
缺点:没有体现资源的再生或时间动态,比较“静态”。
如果你需要“资源会随时间再生”,常见的办法是给资源量写一个类似 Logistic 或 线性恢复 的方程,再减去种群消耗项。例如:
当种群 增加时,消耗项 会更大,导致 变负、 减少。反之,若种群很小,消耗项就小, 主要受再生项影响而回升。
如果你是用离散时间(差分),也可以写:
优点:生态学上很常见;让资源随时间有独立的动力学,能更真实地模拟“资源恢复或衰减”过程。
缺点:方程多一个维度,需要更多参数 (),也更复杂。
实际上,这类做法已经是“捕食者-被捕食者”模型的思想:当“捕食者”=七鳃鳗大量出现时,对“被捕食者”=资源 就是较大的消耗项。
也可以考虑把资源 当成“被捕食者种群”,七鳃鳗(或其中成体)当成“捕食者种群”,写成典型的Lotka-Volterra类型:
优点:有丰厚的生态学理论基础;能呈现捕食/被捕食的周期振荡等典型现象。
缺点:如果还要耦合幼体与雄雌分化,会比较多方程;需要较多参数估计。
假设你要“最小改动”原有的 ODE 模型,把资源 改成一个非常简单的负相关——例如:
这样你就不再写 ;而在代码中任何使用 的地方,都先计算:
pythondef effective_resource(J, M, F, K=1e6, a=10.0): R = K - a*(J+M+F) return max(R, 0)
然后在死亡率或繁殖函数里把 R
替换成 effective_resource(J, M, F, K, a)
。
M+F+J
很大 ⇒ R
接近 0;R
接近 K
。示例代码片段(简化):
pythondef euler_simulation(T=50, dt=1.0, K=1e6, a=10.0): # J, M, F 作为状态变量, R 不再是独立状态 # ... for n in range(N-1): # 计算 R Rn = max(0, K - a*(Jn + Mn + Fn)) # 后续在计算 dJdt, dMdt, dFdt 时, 用这个 Rn # e.g. muJ = muJ0 + aJ*(K - Rn)/K # births = c*E*(some function)* Rn # 或者别的 # ...
这样就大大简化了资源端。但如果你想要资源可以在时间上自己慢慢增长或衰退,就得回到ODE 的再生-消耗做法。
无论是哪种思路,核心是:** 当种群数增加** ⇒ 资源量减少(或资源增速变慢/死亡率上升) ⇒ 抑制种群无限膨胀;当种群数减少 ⇒ 资源量反弹(或死亡率回落/繁殖更易成功) ⇒ 避免种群很快灭绝。这样就能体现“种群与资源负相关”的调节关系。
将资源当成“被捕食者”,七鳃鳗为“捕食者”,用Lotka-Volterra或其变体模型描述二者互相作用。也是一种显式“负相关”的典型方式。请用这种方式,进行详细的刻化与描述,列出方程并仔细介绍各个参数的含义以及计算,最后给出合理的python代码
下面给出一个Lotka-Volterra(捕食者-被捕食者)风格的模型示例,把资源(如小鱼、底栖生物等)视为“被捕食者”,把七鳃鳗(在此分为幼体/成体雄/成体雌三个阶段)视为“捕食者”。这能显式地体现“种群数量多时资源减少、种群数量少时资源相对恢复”的负相关关系。模型方程、参数含义及 Python 实现都在下文给出。
经典的 Lotka-Volterra 捕食—被捕食模型往往是两条方程:(1) 被捕食者按自身增长 - 被捕食消耗;(2) 捕食者按消耗收益 - 自然死亡。这里我们做了拓展:
资源 —— 被捕食者
七鳃鳗捕食者:为了体现七鳃鳗的幼体、成体雄、成体雌三个群体结构,我们设三条方程:
幼体由捕食收益而“出生”,然后在发育后依照某个资源依赖的性别概率 分化为雄或雌。每个群体都有其死亡率或其他损耗。
注:我们此处示范一个简化版本,暂未加入“一次性繁殖后死亡”等复杂机制,便于突出“Lotka-Volterra式资源消耗”的核心思路。若需要更复杂(如雌雄交配产生幼体后父母死亡、性别比动态修正等),可在本框架基础上进一步扩展。
可根据生理实验或文献:当资源较少时,更偏向雄性,当资源充足时更偏向雌性。示例:
以下是一段可运行的 Python 代码,用欧拉法实现该 4 方程模型,并演示如何绘图。你可基于它做进一步扩展(如雌雄配对繁殖、一次繁殖后死亡等)。
pythonimport numpy as np import matplotlib.pyplot as plt def pR_linear(R, K, p_low=0.8, p_high=0.2): """ 简单线性函数: R=0 => p(R)=p_low(偏雄), R=K => p(R)=p_high(偏雌). """ # clamp to avoid overshoot val = p_low + (p_high - p_low)*(R/K) if val<0: return 0 if val>1: return 1 return val def euler_simulation( T=100, dt=0.5, # 资源Logistic r=0.8, K=1e5, # 捕食率与转化效率 c=0.00001, e=0.01, # 死亡率 muJ=0.1, muM=0.05, muF=0.05, # 幼体发育 gamma=0.2, # 性别比函数 p_low=0.8, p_high=0.2, # 初始条件 R0=None, J0=0, M0=100, F0=100 ): """ Lotka-Volterra 扩展模型: dR/dt = rR(1-R/K) - c R (M+F) dJ/dt = e c R (M+F) - muJ*J - gamma*J dM/dt = gamma * p(R)*J - muM*M dF/dt = gamma * [1 - p(R)]*J - muF*F 参数含义详见上文注释。 """ if R0 is None: R0 = K*0.5 N = int(T/dt) + 1 t_vals = np.linspace(0, T, N) R_vals = np.zeros(N) J_vals = np.zeros(N) M_vals = np.zeros(N) F_vals = np.zeros(N) # 初值 R_vals[0] = R0 J_vals[0] = J0 M_vals[0] = M0 F_vals[0] = F0 for n in range(N - 1): Rn = R_vals[n] Jn = J_vals[n] Mn = M_vals[n] Fn = F_vals[n] # 1) dR/dt dRdt = r*Rn*(1 - Rn/K) - c * Rn * (Mn + Fn) # 2) dJ/dt births = e * c * Rn * (Mn + Fn) # 从捕食收益转换为幼体 dJdt = births - muJ*Jn - gamma*Jn # 3) dM/dt p_val = pR_linear(Rn, K, p_low=p_low, p_high=p_high) dMdt = gamma * p_val * Jn - muM*Mn # 4) dF/dt dFdt = gamma * (1 - p_val) * Jn - muF*Fn # 欧拉更新 R_next = Rn + dt*dRdt J_next = Jn + dt*dJdt M_next = Mn + dt*dMdt F_next = Fn + dt*dFdt # 不允许负值 R_next = max(R_next, 0) J_next = max(J_next, 0) M_next = max(M_next, 0) F_next = max(F_next, 0) R_vals[n+1] = R_next J_vals[n+1] = J_next M_vals[n+1] = M_next F_vals[n+1] = F_next return t_vals, R_vals, J_vals, M_vals, F_vals # ------------ 运行测试并作图 --------------- if __name__=="__main__": t, R_sol, J_sol, M_sol, F_sol = euler_simulation( T=200, dt=1.0, K=1e5, r=0.8, c=0.00002, e=0.05, muJ=0.15, muM=0.05, muF=0.05, gamma=0.2, p_low=0.9, p_high=0.1, R0=5e4, J0=0, M0=500, F0=500 ) fig, axes = plt.subplots(1,2, figsize=(12,5)) # (1) J, M, F axes[0].plot(t, J_sol, label='Juvenile (J)', color='green') axes[0].plot(t, M_sol, label='Male (M)', color='blue') axes[0].plot(t, F_sol, label='Female (F)', color='red') axes[0].set_xlabel("Time") axes[0].set_ylabel("Population") axes[0].set_title("Lamprey: J, M, F vs. Time") axes[0].legend() axes[0].grid(True) # (2) Resource R axes[1].plot(t, R_sol, label='Resource (R)', color='brown') axes[1].set_xlabel("Time") axes[1].set_ylabel("Resource") axes[1].set_title("Resource vs. Time") axes[1].grid(True) plt.tight_layout() plt.show()
Lotka-Volterra 核心:
.
e * c * R (M+F) → 幼体
性别分配 p(R)
pR_linear
函数做线性插值,参数 (p_low, p_high)
表示资源极端匮乏或极端富足时的雄性概率。死亡/发育
数值方法
dt
计算 ,再更新 。对动力学剧烈的系统,需选择较小 dt
或改用 RK4 (scipy.integrate
等) 提高精度。资源当“被捕食者”
捕食者(七鳃鳗)多阶段
常用参数
可扩展
通过上述示例,你即可在Lotka-Volterra式的“捕食-被捕食”框架下,利用资源(被捕食者)与七鳃鳗(捕食者)间的相互作用来实现“种群数量多时资源减少、种群数量少时资源恢复”的负相关关系,并在此基础上考虑七鳃鳗的幼体发育与雌雄分化,从而得到一个相对完整、可扩展的生态动力学模型。
你的模型已经颇具雏形了,但还是存在相应的问题:在资源充足的情况下,雄性占比大约在58%;在资源不充足的情况下,雄性约占比78%。所以对于雌雄性别比例,还需要做一些约束,不应当出现雌性比雄性多很多的情况。
下面给出一个在“资源依赖”基础上额外加入“性别平衡”约束的示例模型,从而避免出现极端雌雄失衡(如雌性远多于雄性,或雄性比例过低)的情况。这个方法兼顾了“资源匮乏 → 高雄性比例”和“资源充足 → 雄性比例下降”这一总体趋势,同时又在数值上对最终雌雄比例进行一定限制,不会让雌性永远压倒性地多于雄性。
思路: 在原先 “ = 基于资源的雄性概率” 之上,引入一个附加的雌雄平衡修正项,当雌性数量显著多于雄性时,则提高新的雄性概率;当雄性数量过多时,则反向调整,保证雌雄不会相差太离谱。
我们定义一个函数
这样就保证:
pythondef sex_ratio_correction(M, F, R, K, p_low=0.78, # 资源极低时,雄性概率 p_high=0.56, # 资源极高时,雄性概率 d=0.3, # 雌雄平衡修正力度 pmin=0.4, # 保证雄性概率不会低于 0.4 pmax=0.7, # 保证雄性概率不会高于 0.7 eps=1e-6): """ 在“资源依赖” + “雌雄数量对比” 基础上调整雄性概率。 p(R)= p_low + (p_high - p_low)*(R/K), # 简单线性示例 再加上 d*(F-M)/(M+F+eps). 最终clamp到 [pmin, pmax] 区间,避免极端。 """ # 基于资源 (线性插值) p_r = p_low + (p_high - p_low)*(R/K) # 雌雄差异修正 denom = M + F + eps diff_term = d*(F - M)/denom raw = p_r + diff_term # 限制在 [pmin, pmax] 之内 p_val = max(pmin, min(pmax, raw)) return p_val
在此示例中:
- 当 ⇒
p_r= p_low=0.78
;当 ⇒p_r= p_high=0.56
;- 修正项
diff_term
>0 会把p_val
拉大(更多雄性),<0 则把p_val
拉小(更偏雌);- 最后结果不超过
[0.4, 0.7]
,也就是雄性概率始终在 40%~70% 范围内,不会出现 80%或 20%之类极端值。
解释:
diff_term
是负值,会将 0.78 往下拉,但不会低于 pmin=0.4
;以我们之前的 Lotka-Volterra + 幼体/成体模型为例,假设其方程是:
只要把**** 改成 ,在每一步计算 前,先根据当前 调用那个 sex_ratio_correction(...)
函数,就能得到带有雌雄平衡限制的雄性概率。
下面是一段可执行的 Python 代码,演示欧拉法模拟。它与先前大同小异,唯一区别就是性别概率函数使用了“资源依赖 + 雌雄修正 + clamp”版本。
pythonimport numpy as np import matplotlib.pyplot as plt def sex_ratio_correction(M, F, R, K, p_low=0.78, # 资源极低时的雄性概率 p_high=0.56, # 资源极高时的雄性概率 d=0.3, # 雌雄平衡修正系数 pmin=0.4, # clamp 下限 pmax=0.7, # clamp 上限 eps=1e-6): """ 资源+雌雄差异双重影响的雄性概率函数。 """ # 1) 基于资源 p_r = p_low + (p_high - p_low)*(R/K) # linear interpolation # 2) 雌雄修正 denom = M+F+eps diff = (F - M)/denom # F>>M => diff>0 =>增加p raw = p_r + d*diff # 3) clamp p_val = max(pmin, min(pmax, raw)) return p_val def euler_simulation( T=100, dt=1.0, # 资源Logistic参数 r=0.5, K=1e5, # 捕食率与转化效率 c=2e-5, e=0.05, # 幼体->成体 gamma=0.2, # 死亡率 muJ=0.15, muM=0.05, muF=0.05, # 性别比相关参数 p_low=0.78, p_high=0.56, d_sex=0.3, pmin=0.4, pmax=0.7, # 初值 R0=None, J0=0, M0=500, F0=500 ): """ 改进版Lotka-Volterra模型: dR/dt = rR(1-R/K) - c R(M+F) dJ/dt = e c R(M+F) - muJ J - gamma J dM/dt = gamma * p(M,F,R) * J - muM M dF/dt = gamma * [1 - p(M,F,R)] * J - muF F 其中 p(M,F,R) = clamp( p(R)+ d*(F-M)/(M+F) ), 限定在 [pmin, pmax]. """ if R0 is None: R0 = K*0.5 N = int(T/dt) + 1 t_vals = np.linspace(0, T, N) R_vals = np.zeros(N) J_vals = np.zeros(N) M_vals = np.zeros(N) F_vals = np.zeros(N) R_vals[0] = R0 J_vals[0] = J0 M_vals[0] = M0 F_vals[0] = F0 for n in range(N-1): Rn = R_vals[n] Jn = J_vals[n] Mn = M_vals[n] Fn = F_vals[n] # 1) dR/dt dRdt = r*Rn*(1 - Rn/K) - c * Rn*(Mn + Fn) # 2) dJ/dt births = e * c * Rn*(Mn + Fn) # 由捕食产生的幼体 dJdt = births - muJ*Jn - gamma*Jn # 3) dM/dt & dF/dt (使用改进后的性别分配) p_val = sex_ratio_correction(Mn, Fn, Rn, K, p_low=p_low, p_high=p_high, d=d_sex, pmin=pmin, pmax=pmax) dMdt = gamma*p_val*Jn - muM*Mn dFdt = gamma*(1 - p_val)*Jn - muF*Fn # 欧拉更新 R_next = Rn + dt*dRdt J_next = Jn + dt*dJdt M_next = Mn + dt*dMdt F_next = Fn + dt*dFdt # 防止负数 R_next = max(R_next, 0) J_next = max(J_next, 0) M_next = max(M_next, 0) F_next = max(F_next, 0) R_vals[n+1] = R_next J_vals[n+1] = J_next M_vals[n+1] = M_next F_vals[n+1] = F_next return t_vals, R_vals, J_vals, M_vals, F_vals # 测试与作图 if __name__=="__main__": t, R_sol, J_sol, M_sol, F_sol = euler_simulation( T=200, dt=1.0, r=0.5, K=1e5, c=2e-5, e=0.05, gamma=0.2, muJ=0.15, muM=0.05, muF=0.05, p_low=0.78, p_high=0.56, d_sex=0.3, pmin=0.4, pmax=0.7, R0=5e4, J0=0, M0=500, F0=500 ) fig, axes = plt.subplots(1,2, figsize=(12,5)) # 左图:J, M, F axes[0].plot(t, J_sol, label='Juvenile(J)', color='green') axes[0].plot(t, M_sol, label='Male(M)', color='blue') axes[0].plot(t, F_sol, label='Female(F)', color='red') axes[0].set_title("Population dynamics: J, M, F") axes[0].set_xlabel("Time") axes[0].set_ylabel("Number") axes[0].legend() axes[0].grid(True) # 右图:Resource R axes[1].plot(t, R_sol, label='Resource(R)', color='brown') axes[1].set_title("Resource R over time") axes[1].set_xlabel("Time") axes[1].set_ylabel("R") axes[1].grid(True) plt.tight_layout() plt.show()
在上面示例里,我们把雄性概率限缩在区间 [0.4, 0.7]
,即当资源极端匮乏+雌性远多于雄性时也最多升到 0.7;当资源极端充足+雄性过多时,也不会掉到 0.3 以下 —— 保证不会出现“雌性显著多于雄性”的极端失衡局面。
pmin=0.35, pmax=0.65
或者让 d_sex
更大(更快修正)或更小(平衡性别的倾向更弱),视你的需求。若观察过程中发现雌性始终比雄性略多或相反,这可能正是系统在此参数组合下的稳定或周期解;但由于 clamp 限制,不会再出现 80% vs. 20% 之类极端比率。
p_low, p_high, pmin, pmax, d_sex
等,就能满足“资源足 → 雄性约 56-58%,资源缺 → 雄性 70%上下,但不会出现雌性极度过量”。这样一来,你就兼顾了“资源主导的总体趋势”(缺→偏雄,足→偏雌)和“不可极端失衡”的数值限制,达成“在资源充足下,雄性比例约 56%~58%;在资源不充足下,雄性也不会无限升高,但大约 70%上下;雌性数量再也无法过分高于雄性”。这便满足你提出的“不应当出现雌性比雄性多很多”的需求。
这个模型非常好!最后,我们再加一些随机扰动,比如说,在某个时间点,种群数量忽然减小了一部分(模拟人类的捕猎),让这个模型更加真实
下面给出一个在Lotka-Volterra扩展模型 + 雌雄平衡修正的基础上,加入突发随机扰动(比如模拟在人类捕猎等干预时刻,种群数量突然减少)的示例 Python 代码。这样能更贴近真实生态系统中可能发生的“意外事件”,并观察系统如何在扰动后逐渐恢复或继续波动。
时间序列中途突然削减种群数量
在每个时间步中加入一些随机波动
这里,为了示范**“突然减少种群”**这种更直观的人为捕猎/干预事件,我们会在离散时间步里,当到达某个时刻 (比如 ) 时,让 减少固定比例(或减去某个数值),再继续欧拉迭代。
以下代码基于之前的“资源—捕食者”模型 + “雌雄平衡性别函数”,并在模拟循环里加一个shock_event判断:当时间步到达某时刻,就对种群做突发削减。
pythonimport numpy as np import matplotlib.pyplot as plt def sex_ratio_correction(M, F, R, K, p_low=0.78, # 资源极低时的雄性概率 p_high=0.56, # 资源极高时的雄性概率 d=0.3, # 雌雄平衡修正系数 pmin=0.4, # clamp 下限 pmax=0.7, # clamp 上限 eps=1e-6): # 1) 基于资源线性插值 p_r = p_low + (p_high - p_low)*(R/K) # 2) 雌雄差距修正 denom = M+F+eps diff = (F - M)/denom raw = p_r + d*diff # 3) clamp p_val = max(pmin, min(pmax, raw)) return p_val def euler_simulation( T=100, dt=1.0, # 资源参数 r=0.5, K=1e5, # 捕食-转化 c=2e-5, e=0.05, # 幼体->成体 gamma=0.2, # 死亡率 muJ=0.15, muM=0.05, muF=0.05, # 性别分配修正 p_low=0.78, p_high=0.56, d_sex=0.3, pmin=0.4, pmax=0.7, # 初值 R0=None, J0=0, M0=500, F0=500, # 干预时间 / 干预比例 shock_time=50.0, # 在t=50时进行干预 shock_scale=0.5 # 将种群减少为原来的0.5(50%) ): """ Lotka-Volterra扩展+雌雄平衡+突然的捕猎干预(shock_event) shock_time: 在哪个时间点进行干预 shock_scale: 干预时将 J,M,F 乘以shock_scale (减少部分种群) """ if R0 is None: R0 = K*0.5 N = int(T/dt) + 1 t_vals = np.linspace(0, T, N) R_vals = np.zeros(N) J_vals = np.zeros(N) M_vals = np.zeros(N) F_vals = np.zeros(N) R_vals[0] = R0 J_vals[0] = J0 M_vals[0] = M0 F_vals[0] = F0 # 为了只触发一次shock,可以记录一下是否已经执行 shock_done = False for i in range(N-1): t_current = t_vals[i] Rn = R_vals[i] Jn = J_vals[i] Mn = M_vals[i] Fn = F_vals[i] # ---- (1) dR/dt dRdt = r*Rn*(1 - Rn/K) - c*Rn*(Mn + Fn) # ---- (2) dJ/dt births = e*c*Rn*(Mn + Fn) dJdt = births - muJ*Jn - gamma*Jn # ---- (3) dM/dt, dF/dt p_val = sex_ratio_correction(Mn, Fn, Rn, K, p_low=p_low, p_high=p_high, d=d_sex, pmin=pmin, pmax=pmax) dMdt = gamma*p_val*Jn - muM*Mn dFdt = gamma*(1 - p_val)*Jn - muF*Fn # 欧拉更新 R_next = Rn + dt*dRdt J_next = Jn + dt*dJdt M_next = Mn + dt*dMdt F_next = Fn + dt*dFdt # 防止负数 R_next = max(R_next, 0) J_next = max(J_next, 0) M_next = max(M_next, 0) F_next = max(F_next, 0) # ---- 检查是否到达shock_time (捕猎干预) ---- # 如果 t_current < shock_time <= t_current+dt, 就执行shock # 或者可以用 i*dt == shock_time(若整除) 之类 if (not shock_done) and (t_current < shock_time <= t_current + dt): # 在这里让 J,M,F 乘以 shock_scale J_next *= shock_scale M_next *= shock_scale F_next *= shock_scale shock_done = True # 防止重复执行 R_vals[i+1] = R_next J_vals[i+1] = J_next M_vals[i+1] = M_next F_vals[i+1] = F_next return t_vals, R_vals, J_vals, M_vals, F_vals # ------------------- 运行并可视化 ------------------- if __name__=="__main__": t, R_sol, J_sol, M_sol, F_sol = euler_simulation( T=200, dt=1.0, r=0.5, K=1e5, c=2e-5, e=0.05, gamma=0.2, muJ=0.15, muM=0.05, muF=0.05, p_low=0.78, p_high=0.56, d_sex=0.3, pmin=0.4, pmax=0.7, R0=5e4, J0=0, M0=500, F0=500, shock_time=50.0, # 在t=50时进行一次干预 shock_scale=0.5 # 种群减少到原来的一半 ) fig, axes = plt.subplots(1,2, figsize=(12,5)) # 左图:J, M, F axes[0].plot(t, J_sol, label='Juvenile(J)', color='green') axes[0].plot(t, M_sol, label='Male(M)', color='blue') axes[0].plot(t, F_sol, label='Female(F)', color='red') axes[0].axvline(50, color='black', linestyle='--', label='Shock @ t=50') axes[0].set_title("Population: J, M, F (shock at t=50)") axes[0].set_xlabel("Time") axes[0].set_ylabel("Population") axes[0].legend() axes[0].grid(True) # 右图:资源 R axes[1].plot(t, R_sol, label='Resource(R)', color='brown') axes[1].axvline(50, color='black', linestyle='--') axes[1].set_title("Resource R over time (shock at t=50)") axes[1].set_xlabel("Time") axes[1].set_ylabel("R") axes[1].grid(True) plt.tight_layout() plt.show()
运行后,你会看到在 附近,曲线上雄雌幼体会突然往下跳,然后模型会继续按照自身动力学去恢复或继续波动。
这样,就能使模型更真实地模拟人类捕猎或环境事故等突发事件对七鳃鳗种群和资源的冲击,并观察系统能否在负反馈机制下逐渐恢复,或在参数不利时走向灭绝或长期低谷。
axvline()
或在结果数组中查找时刻点来显示干预位置,观察干预前后曲线如何变化。这样你就得到了一个更加真实且能够容纳突发事件的生态模型。你可根据需要进一步调整干预方式(削减幅度、时刻、次数)和基础动力学参数()来试验不同场景下种群与资源的长期演化。
我的建议是把这个扰动改得周期性一点,另外,对七鳃鳗的捕食不应当影响到资源,资源的增长可以变快,但不是这样的直线型增长
下面给出一个更加独立的“资源-种群”模型示例,满足以下需求:
我们设:
传统的 Logistic 为 。
若要“增长变快/变慢”,可改成**-Logistic**(也称广义Logistic):
幼体方程:
雄性成体方程
雌性成体方程
根据需求:
并限制到某区间 [pmin, pmax]
,保证不出现极端失衡。
设一个周期 ,每过 个时间单位,就把七鳃鳗种群(幼体或成体或全部)削减一定比例(或一定数值)。这样模拟“每隔 时间人类就来捕一次”。
pythonimport numpy as np import matplotlib.pyplot as plt def resource_growth(R, r, K, theta=1.5): """ theta-Logistic: dR/dt = r * R^theta * (1 - R/K) """ return r * (R**theta) * (1 - R/K) def sat_function(R, a=1000.0): """ 资源对繁殖的影响因子: Phi(R)= R/(R+a). 当R>>a时 ~1, 当R~0时 ~0. """ return R/(R + a) def sex_ratio(M, F, R, K, # 资源极低=>p=0.78, 资源极高=>p=0.56 p_low=0.78, p_high=0.56, # 雌雄修正 d=0.3, # clamp pmin=0.4, pmax=0.8, eps=1e-6): """ p_base(R) = p_low + (p_high - p_low)*(R/K), 再加上 d*(F-M)/(M+F). 最后clamp到 [pmin,pmax]. """ # 1) base p_r = p_low + (p_high - p_low)*(R/K) # 2) diff denom = M+F+eps diff_term = d*(F - M)/denom raw = p_r + diff_term # 3) clamp p_val = max(pmin, min(pmax, raw)) return p_val def euler_model( T=200, dt=1.0, # 资源参数 r=0.5, K=1e5, theta=1.5, # 繁殖(幼体出生) b=0.02, # 资源影响饱和函数sat_function(R,a) a_sat=1e3, # 死亡&发育 muJ=0.15, muM=0.05, muF=0.05, gamma=0.2, # 性别比 p_low=0.78, p_high=0.56, d_sex=0.3, pmin=0.4, pmax=0.8, # 初值 R0=None, J0=0, M0=500, F0=500, # 周期扰动参数 shock_period=50, # 每隔多少时间进行一次捕猎 shock_fraction=0.5, # 每次捕猎削减到多少比例 reduce_which="All" # "All"/"Adult"/"MOnly"/"M+F"等 ): """ 1) dR/dt = r*R^theta*(1 - R/K) 2) dJ/dt = b*(M+F)* Phi(R) - muJ*J - gamma*J 3) dM/dt = gamma*p(M,F,R)*J - muM*M 4) dF/dt = gamma*(1 - p(M,F,R))*J - muF*F 周期性捕猎: 每shock_period时间, 把七鳃鳗数量缩减shock_fraction倍. reduce_which 指定对哪些群体操作. """ if R0 is None: R0 = K*0.2 # 初始资源, 自定 N = int(T/dt)+1 t_vals = np.linspace(0, T, N) R_vals = np.zeros(N) J_vals = np.zeros(N) M_vals = np.zeros(N) F_vals = np.zeros(N) R_vals[0] = R0 J_vals[0] = J0 M_vals[0] = M0 F_vals[0] = F0 # 用一个简单方法: 看当前整型时间 i, 若 i!=0 且 i % shock_period==0 => 捕猎 for i in range(N-1): Rn = R_vals[i] Jn = J_vals[i] Mn = M_vals[i] Fn = F_vals[i] # 1) dR/dt dRdt = resource_growth(Rn, r, K, theta=theta) # 2) dJ/dt births = b*(Mn+Fn)*sat_function(Rn, a=a_sat) dJdt = births - muJ*Jn - gamma*Jn # 3) p(M,F,R) p_val = sex_ratio(Mn, Fn, Rn, K, p_low=p_low, p_high=p_high, d=d_sex, pmin=pmin, pmax=pmax) dMdt = gamma*p_val*Jn - muM*Mn dFdt = gamma*(1-p_val)*Jn - muF*Fn # Euler R_next = Rn + dt*dRdt J_next = Jn + dt*dJdt M_next = Mn + dt*dMdt F_next = Fn + dt*dFdt # 不允许负数 R_next = max(R_next, 0) J_next = max(J_next, 0) M_next = max(M_next, 0) F_next = max(F_next, 0) # 周期性捕猎 (若 i+1为下个整点时刻) t_next = (i+1)*dt i_next = i+1 if i_next>0 and (i_next % shock_period==0): if reduce_which == "All": J_next *= shock_fraction M_next *= shock_fraction F_next *= shock_fraction elif reduce_which == "Adult": # 只削减成体(雄雌) M_next *= shock_fraction F_next *= shock_fraction elif reduce_which == "MOnly": M_next *= shock_fraction elif reduce_which == "M+F": M_next *= shock_fraction F_next *= shock_fraction # ...可根据需求再扩展 R_vals[i+1] = R_next J_vals[i+1] = J_next M_vals[i+1] = M_next F_vals[i+1] = F_next return t_vals, R_vals, J_vals, M_vals, F_vals # ------------------- 运行与可视化 ------------------- if __name__=="__main__": t, R_sol, J_sol, M_sol, F_sol = euler_model( T=300, dt=1.0, r=0.6, K=1e5, theta=1.8, # 资源更快增长 b=0.02, a_sat=1000.0, # 幼体出生与资源相关 muJ=0.15, muM=0.05, muF=0.05, gamma=0.2, p_low=0.78, p_high=0.56, d_sex=0.3, pmin=0.4, pmax=0.75, R0=None, J0=100, M0=500, F0=500, shock_period=50, # 每50个时间单位捕猎一次 shock_fraction=0.5, # 削减为50% reduce_which="Adult" # 只削减成体 ) fig, axes = plt.subplots(1,2, figsize=(12,5)) axes[0].plot(t, J_sol, label='Juvenile', color='green') axes[0].plot(t, M_sol, label='Male', color='blue') axes[0].plot(t, F_sol, label='Female', color='red') for i in range(len(t)): # 在每个 shock_step 处画竖线 if i>0 and i%50==0: axes[0].axvline(i, color='gray', linestyle='--') axes[0].set_xlabel("Time") axes[0].set_ylabel("Population") axes[0].set_title("Lamprey population with periodic hunting") axes[0].legend() axes[0].grid(True) # 资源 axes[1].plot(t, R_sol, color='brown', label='Resource R') for i in range(len(t)): if i>0 and i%50==0: axes[1].axvline(i, color='gray', linestyle='--') axes[1].set_xlabel("Time") axes[1].set_ylabel("Resource R") axes[1].set_title("Resource (theta-logistic, unaffected by lamprey)") axes[1].grid(True) plt.tight_layout() plt.show()
资源方程
pythondef resource_growth(R, r, K, theta=1.5): return r * (R**theta)*(1 - R/K)
七鳃鳗繁殖
pythonbirths = b*(M+F)*sat_function(Rn, a=a_sat)
sat_function(R,a)= R/(R+a)
:当资源远大于 a
时,返回 ~1;当资源很低时,返回近似 R/a
。R
里减去任何量。性别概率 sex_ratio(...)
p_r ~ p_low=0.78
;当资源=K ⇒ p_r ~ p_high=0.56
;再加**(F-M)**的平衡修正 + clamp到 [pmin, pmax]
。周期捕猎
pythonshock_period=50 shock_fraction=0.5 reduce_which="Adult"
资源更快增长
theta=1.8
(>1) + r=0.6
使资源在中等水平时有一段较陡峭的生长。你可调大 r
或更大 theta
来让其增长更快。Phi(R)
影响幼体出生率,但并不真正消耗 。shock_period
个时间步,对种群做一次大幅削减,模拟反复的人类捕猎/干预。theta>1
⇒ 若想资源在中等水平时增长更快,可以尝试 1.5、2.0、甚至 3.0;也可 <1 模拟不同增长形态;b, a_sat
控制繁殖对资源的敏感度;shock_period, shock_fraction
决定捕猎频次与力度;reduce_which
决定捕猎对象:仅成体、仅雄、或所有阶段等;通过以上改动,你就得到一个资源自身可快速增长(不被七鳃鳗消耗),但对七鳃鳗繁殖成功有影响的生态模型;再加上周期性干预,就能呈现更丰富、更接近现实管理或捕捞场景下的种群动力学演化。
import numpy as np
import matplotlib.pyplot as plt
def sex_ratio_correction(M, F, R, K,
p_low=0.78, # 资源极低时的雄性概率
p_high=0.56, # 资源极高时的雄性概率
d=0.3, # 雌雄平衡修正系数
pmin=0.4, # clamp 下限
pmax=0.7, # clamp 上限
eps=1e-6):
# 1) 基于资源线性插值
p_r = p_low + (p_high - p_low)(R/K)
# 2) 雌雄差距修正
denom = M+F+eps
diff = (F - M)/denom
raw = p_r + ddiff
# 3) clamp
p_val = max(pmin, min(pmax, raw))
return p_val
def euler_simulation(
T=100, dt=1.0,
# 资源参数
r=0.5, K=1e5,
# 捕食-转化
c=2e-5, e=0.05,
# 幼体->成体
gamma=0.2,
# 死亡率
muJ=0.15, muM=0.05, muF=0.05,
# 性别分配修正
p_low=0.78, p_high=0.56, d_sex=0.3, pmin=0.4, pmax=0.7,
# 初值
R0=None, J0=0, M0=500, F0=500,
# 干预时间 / 干预比例
shock_time=50.0, # 在t=50时进行干预
shock_scale=0.5 # 将种群减少为原来的0.5(50%)
):
"""
Lotka-Volterra扩展+雌雄平衡+突然的捕猎干预(shock_event)
shock_time: 在哪个时间点进行干预
shock_scale: 干预时将 J,M,F 乘以shock_scale (减少部分种群)
"""
if R0 is None:
R0 = K*0.5
textN = int(T/dt) + 1 t_vals = np.linspace(0, T, N) R_vals = np.zeros(N) J_vals = np.zeros(N) M_vals = np.zeros(N) F_vals = np.zeros(N) R_vals[0] = R0 J_vals[0] = J0 M_vals[0] = M0 F_vals[0] = F0 # 为了只触发一次shock,可以记录一下是否已经执行 shock_done = False for i in range(N-1): t_current = t_vals[i] Rn = R_vals[i] Jn = J_vals[i] Mn = M_vals[i] Fn = F_vals[i] # ---- (1) dR/dt dRdt = r*Rn*(1 - Rn/K) - c*Rn*(Mn + Fn) # ---- (2) dJ/dt births = e*c*Rn*(Mn + Fn) dJdt = births - muJ*Jn - gamma*Jn # ---- (3) dM/dt, dF/dt p_val = sex_ratio_correction(Mn, Fn, Rn, K, p_low=p_low, p_high=p_high, d=d_sex, pmin=pmin, pmax=pmax) dMdt = gamma*p_val*Jn - muM*Mn dFdt = gamma*(1 - p_val)*Jn - muF*Fn # 欧拉更新 R_next = Rn + dt*dRdt J_next = Jn + dt*dJdt M_next = Mn + dt*dMdt F_next = Fn + dt*dFdt # 防止负数 R_next = max(R_next, 0) J_next = max(J_next, 0) M_next = max(M_next, 0) F_next = max(F_next, 0) # ---- 检查是否到达shock_time (捕猎干预) ---- # 如果 t_current < shock_time <= t_current+dt, 就执行shock # 或者可以用 i*dt == shock_time(若整除) 之类 if (not shock_done) and (t_current < shock_time <= t_current + dt): # 在这里让 J,M,F 乘以 shock_scale J_next *= shock_scale M_next *= shock_scale F_next *= shock_scale shock_done = True # 防止重复执行 R_vals[i+1] = R_next J_vals[i+1] = J_next M_vals[i+1] = M_next F_vals[i+1] = F_next return t_vals, R_vals, J_vals, M_vals, F_vals
if name=="main":
t, R_sol, J_sol, M_sol, F_sol = euler_simulation(
T=200, dt=1.0,
r=0.5, K=1e5,
c=2e-5, e=0.05,
gamma=0.2,
muJ=0.15, muM=0.05, muF=0.05,
p_low=0.78, p_high=0.56, d_sex=0.3,
pmin=0.4, pmax=0.7,
R0=5e4, J0=0, M0=500, F0=500,
shock_time=50.0, # 在t=50时进行一次干预
shock_scale=0.5 # 种群减少到原来的一半
)
textfig, axes = plt.subplots(1,2, figsize=(12,5)) # 左图:J, M, F axes[0].plot(t, J_sol, label='Juvenile(J)', color='green') axes[0].plot(t, M_sol, label='Male(M)', color='blue') axes[0].plot(t, F_sol, label='Female(F)', color='red') axes[0].axvline(50, color='black', linestyle='--', label='Shock @ t=50') axes[0].set_title("Population: J, M, F (shock at t=50)") axes[0].set_xlabel("Time") axes[0].set_ylabel("Population") axes[0].legend() axes[0].grid(True) # 右图:资源 R axes[1].plot(t, R_sol, label='Resource(R)', color='brown') axes[1].axvline(50, color='black', linestyle='--') axes[1].set_title("Resource R over time (shock at t=50)") axes[1].set_xlabel("Time") axes[1].set_ylabel("R") axes[1].grid(True) plt.tight_layout() plt.show()
在这个代码的基础上再做一些改进,比如说,你可以实时的把出生率设置一个随机波动,死亡率也设置一个随机波动,使得曲线不那么平滑
下面给出一个在原模型基础上引入随机波动的示例代码:我们将对「出生率」和「死亡率」在每个时间步做小范围的随机扰动,使得模拟曲线不那么光滑,更接近实际系统中的不确定性或环境噪声。这样,每次迭代时都稍微改变繁殖与死亡系数,观察系统在多重随机干扰下的演化。
随机波动对象
births = e*c*R*(M+F)
表示。我们可以在此基础上添加一个随机因子,比如在每一步时令其乘上 ,其中 来自一个小均值0的正态或其他分布。随机分布的选择
np.random.normal(0, sigma)
)或者均匀分布(np.random.uniform(-a, a)
)。保证参数合理
保留冲击事件(shock)
t=shock_time
时让种群削减一部分,这依旧保留。下面的例子示范5% 标准差的正态噪声(可以自行调整),并进行一次捕猎冲击。
pythonimport numpy as np import matplotlib.pyplot as plt def sex_ratio_correction(M, F, R, K, p_low=0.78, p_high=0.56, d=0.3, pmin=0.4, pmax=0.7, eps=1e-6): # 1) 基于资源线性插值 p_r = p_low + (p_high - p_low)*(R/K) # 2) 雌雄差距修正 denom = M + F + eps diff = (F - M)/denom raw = p_r + d*diff # 3) clamp p_val = max(pmin, min(pmax, raw)) return p_val def euler_simulation( T=100, dt=1.0, # 资源参数 (Logistic) r=0.5, K=1e5, # 捕食转化 c=2e-5, e=0.05, # 幼体->成体 gamma=0.2, # 基础死亡率 muJ=0.15, muM=0.05, muF=0.05, # 性别分配修正 p_low=0.78, p_high=0.56, d_sex=0.3, pmin=0.4, pmax=0.7, # 初值 R0=None, J0=0, M0=500, F0=500, # 干预时间 / 干预比例 shock_time=50.0, shock_scale=0.5, # 随机波动幅度 (标准差), 例如0.05表示 ~5% sigma_birth=0.05, sigma_death=0.05, seed=None ): """ 在原有模型基础上,给出生率(或birth factor)与死亡率增加随机扰动: births = e*c*R*(M+F) * [1 + normal(0, sigma_birth)] muJ_random = muJ * [1 + normal(0, sigma_death)] (再clamp>=0) ... 对 muM, muF 同理 shock_time: 在该时刻削减种群 shock_scale: 削减比例 sigma_birth, sigma_death: 随机波动标准差 """ if R0 is None: R0 = K*0.5 # 若需可重复实验,可设随机种子 if seed is not None: np.random.seed(seed) N = int(T/dt) + 1 t_vals = np.linspace(0, T, N) R_vals = np.zeros(N) J_vals = np.zeros(N) M_vals = np.zeros(N) F_vals = np.zeros(N) R_vals[0] = R0 J_vals[0] = J0 M_vals[0] = M0 F_vals[0] = F0 shock_done = False for i in range(N-1): t_current = t_vals[i] Rn = R_vals[i] Jn = J_vals[i] Mn = M_vals[i] Fn = F_vals[i] # 1) 资源 Logistic (不加随机) dRdt = r*Rn*(1 - Rn/K) - c*Rn*(Mn + Fn) # 2) 出生率随机波动 # e*c*Rn*(Mn+Fn) * (1 + epsilon_b) # 其中 epsilon_b ~ Normal(0, sigma_birth) eps_b = np.random.normal(0, sigma_birth) # 波动 birth_mult = 1 + eps_b if birth_mult < 0: birth_mult = 0 # 避免负值 births = e*c*Rn*(Mn + Fn)*birth_mult # 3) 死亡率随机波动 => clamp >= 0 epsJ = np.random.normal(0, sigma_death) muJ_rand = muJ*(1 + epsJ) muJ_rand = max(muJ_rand, 0) # 保证不为负 epsM = np.random.normal(0, sigma_death) muM_rand = muM*(1 + epsM) muM_rand = max(muM_rand, 0) epsF = np.random.normal(0, sigma_death) muF_rand = muF*(1 + epsF) muF_rand = max(muF_rand, 0) # --- dJ/dt, dM/dt, dF/dt --- dJdt = births - muJ_rand*Jn - gamma*Jn p_val = sex_ratio_correction(Mn, Fn, Rn, K, p_low=p_low, p_high=p_high, d=d_sex, pmin=pmin, pmax=pmax) dMdt = gamma*p_val*Jn - muM_rand*Mn dFdt = gamma*(1 - p_val)*Jn - muF_rand*Fn # 欧拉更新 R_next = Rn + dt*dRdt J_next = Jn + dt*dJdt M_next = Mn + dt*dMdt F_next = Fn + dt*dFdt # clamp R_next = max(R_next, 0) J_next = max(J_next, 0) M_next = max(M_next, 0) F_next = max(F_next, 0) # shock if (not shock_done) and (t_current < shock_time <= t_current + dt): # 在这里让 J,M,F 乘以 shock_scale J_next *= shock_scale M_next *= shock_scale F_next *= shock_scale shock_done = True # 防止重复执行 R_vals[i+1] = R_next J_vals[i+1] = J_next M_vals[i+1] = M_next F_vals[i+1] = F_next return t_vals, R_vals, J_vals, M_vals, F_vals # ------------------- 测试并可视化 ------------------- if __name__=="__main__": t, R_sol, J_sol, M_sol, F_sol = euler_simulation( T=200, dt=1.0, r=0.5, K=1e5, c=2e-5, e=0.05, gamma=0.2, muJ=0.15, muM=0.05, muF=0.05, p_low=0.78, p_high=0.56, d_sex=0.3, pmin=0.4, pmax=0.7, R0=5e4, J0=0, M0=500, F0=500, shock_time=50.0, shock_scale=0.5, sigma_birth=0.05, # 5%出生波动 sigma_death=0.05, # 5%死亡波动 seed=42 # 固定随机种子以便重现 ) fig, axes = plt.subplots(1,2, figsize=(12,5)) # 左:J, M, F axes[0].plot(t, J_sol, label='Juvenile', color='green') axes[0].plot(t, M_sol, label='Male', color='blue') axes[0].plot(t, F_sol, label='Female', color='red') axes[0].axvline(50, color='black', linestyle='--', label='Shock @ t=50') axes[0].set_title("Population (random births & deaths, shock at t=50)") axes[0].set_xlabel("Time") axes[0].set_ylabel("Population") axes[0].legend() axes[0].grid(True) # 右:资源 axes[1].plot(t, R_sol, label='Resource(R)', color='brown') axes[1].axvline(50, color='black', linestyle='--') axes[1].set_title("Resource R with random-lv + shock") axes[1].set_xlabel("Time") axes[1].set_ylabel("R") axes[1].grid(True) plt.tight_layout() plt.show()
在每一步中,我们对出生项(births) 和 死亡率()添加随机因子:
pythoneps_b = np.random.normal(0, sigma_birth) # 出生波动 birth_mult = 1 + eps_b if birth_mult < 0: birth_mult = 0 # 避免为负 births = e*c*Rn*(Mn+Fn)* birth_mult ... epsJ = np.random.normal(0, sigma_death) muJ_rand = max(0, muJ*(1 + epsJ))
max(0, ...)
避免负值。随机种子
seed=42
用于确保每次运行都得到同样的随机序列,便于调试。若不需要可去掉 seed
。冲击事件(shock)依旧保留在 t=50
,使 乘以 0.5。
执行后,你会看到更“抖动”的种群曲线,尤其是 J、M、F 不像之前那样光滑。每次 step 都可能略高或略低的出生、死亡,使曲线带有随机波动。
sigma_birth=0.05
表示在出生项附近约 随机起伏;sigma_death=0.05
表示死亡率也约 波动;np.random.uniform(-0.05, 0.05)
等。max(mu_rand,0)
、if birth_mult<0:birth_mult=0
处理。若波动太大可能要“截断”很多次。这样,你就能在同一个模型框架下同时模拟“出生死亡的日常随机波动”和“在特定时刻或周期出现的大幅捕猎事件”,从而使模拟曲线呈现出更多现实生态系统中常见的随机性与不可预测性特征。