优化类模型

线性规划、整数规划、非线性规划、动态规划、多目标规划等。

线性规划 (Linear Programming)

模型描述

在满足一组线性约束的条件下,最大化或最小化一个线性目标函数。适用于资源分配、生产计划等。

使用步骤

  1. 定义决策变量
  2. 列出目标函数(线性)
  3. 列出约束条件(线性等式或不等式)
  4. 调用求解器(如scipy.optimize.linprog)求解
  5. 解释结果
Python 代码(使用 scipy)
import numpy as np
from scipy.optimize import linprog

# 目标函数系数(最小化),例如:c = [c1, c2, ..., cn]
c = [-3, -2]  # 最大化 3x1 + 2x2 等价于最小化 -3x1 -2x2

# 不等式约束矩阵 A_ub * x <= b_ub
A_ub = [[1, 1], [2, 1], [1, 0]]   # 三个约束
b_ub = [10, 12, 4]

# 等式约束矩阵 A_eq * x == b_eq(若无等式约束,设为None)
A_eq = None
b_eq = None

# 变量的上下界
bounds = [(0, None), (0, None)]  # x1 >= 0, x2 >= 0

# 求解线性规划
res = linprog(c, A_ub=A_ub, b_ub=b_ub, A_eq=A_eq, b_eq=b_eq, bounds=bounds, method='highs')

print('最优解:', res.x)
print('最优值:', -res.fun)  # 因为目标函数取了负号
print('求解状态:', res.message)
参数说明示例
c目标函数系数(最小化)[-3, -2]
A_ub, b_ub不等式约束矩阵和右侧向量A_ub=[[1,1]], b_ub=[10]
A_eq, b_eq等式约束矩阵和右侧向量None
bounds变量上下界[(0,None), (0,None)]
method求解算法,推荐'highs''highs'

竞赛技巧

  • 若要求最大化,将目标函数系数取负号转化为最小化。
  • 约束条件中的“≥”需乘以-1转化为“≤”。
  • 变量无界时设为 (None, None)。
  • 若求解失败,尝试更换method(如 'simplex', 'interior-point')。
  • 结果中的slack表示约束的松弛量,正数表示约束未紧。

整数规划 (Integer Programming)

模型描述

决策变量必须取整数值的线性规划。常用于分配、选址、排班等离散决策问题。

使用步骤

  1. 建立线性规划模型
  2. 指定哪些变量为整数(或0-1变量)
  3. 使用整数规划求解器(如 pulp, ortools)
  4. 求解并解释
Python 代码(使用 pulp)
# 安装:pip install pulp
import pulp

# 创建问题(最大化)
prob = pulp.LpProblem('Integer_Programming_Example', pulp.LpMaximize)

# 定义变量(lowBound下限,cat类型:Integer整数,Binary二进制)
x1 = pulp.LpVariable('x1', lowBound=0, cat='Integer')
x2 = pulp.LpVariable('x2', lowBound=0, cat='Integer')

# 目标函数
prob += 3*x1 + 2*x2, 'Objective'

# 约束条件
prob += x1 + x2 <= 10, 'Constraint1'
prob += 2*x1 + x2 <= 12, 'Constraint2'
prob += x1 <= 4, 'Constraint3'

# 求解
prob.solve(pulp.PULP_CBC_CMD(msg=False))

print('求解状态:', pulp.LpStatus[prob.status])
print('最优解:')
for var in prob.variables():
    print(f'{var.name} = {var.varValue}')
print('最优值:', pulp.value(prob.objective))
参数说明示例
cat变量类型:'Integer'整数,'Binary'0-1,'Continuous'连续'Integer'
lowBound, upBound变量上下界lowBound=0
求解器默认使用CBC,也可用GLPK等pulp.PULP_CBC_CMD

竞赛技巧

  • 0-1变量可用于表示是否选择某个项目(如投资选择)。
  • 整数规划求解时间可能较长,变量较多时可考虑松弛为线性规划获得下界。
  • 若模型有对称性,可添加对称破缺约束加速求解。
  • Pulp库也支持混合整数规划(部分变量整数)。

非线性规划 (Nonlinear Programming)

模型描述

目标函数或约束中至少有一个是非线性的优化问题。常用梯度下降、牛顿法等迭代求解。

使用步骤

  1. 定义目标函数和约束(可微)
  2. 提供初始猜测解
  3. 选择求解算法(如SLSQP, trust-constr)
  4. 调用scipy.optimize.minimize求解
Python 代码(使用 scipy.optimize)
import numpy as np
from scipy.optimize import minimize

# 定义目标函数(Rosenbrock函数,经典测试函数)
def objective(x):
    return (1 - x[0])**2 + 100 * (x[1] - x[0]**2)**2

# 约束条件:x0 + x1 ≥ 1,x0² + x1² ≤ 2
def constraint1(x):
    return x[0] + x[1] - 1  # ≥0

def constraint2(x):
    return 2 - x[0]**2 - x[1]**2  # ≥0

cons = [
    {'type': 'ineq', 'fun': constraint1},
    {'type': 'ineq', 'fun': constraint2}
]

# 变量边界
bounds = [(-2, 2), (-2, 2)]

# 初始猜测
x0 = np.array([0.5, 0.5])

# 求解
res = minimize(objective, x0, method='SLSQP', bounds=bounds, constraints=cons)

print('最优解:', res.x)
print('最优值:', res.fun)
print('是否成功:', res.success)
print('迭代次数:', res.nit)
参数说明示例
method求解算法:'SLSQP'(约束),'trust-constr','BFGS'(无约束)'SLSQP'
constraints约束列表,type可为'eq'或'ineq'[{'type':'ineq', 'fun': c}]
bounds变量边界[(-2,2), (-2,2)]
options算法选项,如maxiter, ftol{'maxiter': 1000}

竞赛技巧

  • 初始点对非线性优化影响很大,多尝试几个初始点。
  • 若目标函数不可微,可考虑使用遗传算法、粒子群等元启发式算法。
  • 约束条件尽量用等式或线性约束,非线性约束会增加求解难度。
  • 可设置options={'disp': True}查看迭代过程。

动态规划 (Dynamic Programming)

模型描述

将多阶段决策问题分解为一系列单阶段问题,通过递推关系求解最优策略。适用于背包问题、最短路径等。

使用步骤

  1. 定义阶段、状态、决策
  2. 建立状态转移方程
  3. 确定边界条件
  4. 递推求解(自底向上或自顶向下)
  5. 回溯得到最优策略
Python 代码(0-1背包问题)
def knapsack_01(weights, values, capacity):
    """0-1背包问题动态规划求解"""
    n = len(weights)
    # dp[i][w] 表示前i个物品在容量w下的最大价值
    dp = [[0] * (capacity + 1) for _ in range(n + 1)]
    
    for i in range(1, n + 1):
        for w in range(capacity + 1):
            if weights[i-1] <= w:
                dp[i][w] = max(dp[i-1][w], dp[i-1][w - weights[i-1]] + values[i-1])
            else:
                dp[i][w] = dp[i-1][w]
    
    # 回溯找出选择的物品
    selected = []
    w = capacity
    for i in range(n, 0, -1):
        if dp[i][w] != dp[i-1][w]:
            selected.append(i-1)
            w -= weights[i-1]
    
    return dp[n][capacity], selected

# 示例数据
weights = [2, 3, 4, 5]   # 物品重量
values = [3, 4, 5, 6]    # 物品价值
capacity = 8             # 背包容量

max_value, selected_items = knapsack_01(weights, values, capacity)
print('最大价值:', max_value)
print('选择的物品索引:', selected_items)
print('对应重量:', [weights[i] for i in selected_items])
print('对应价值:', [values[i] for i in selected_items])
参数说明示例
weights物品重量列表[2,3,4,5]
values物品价值列表[3,4,5,6]
capacity背包容量8
dp数组动态规划表,维度(n+1)×(capacity+1)

竞赛技巧

  • 动态规划的关键是找到正确的状态定义和转移方程。
  • 若状态空间太大,可考虑使用记忆化搜索(递归+缓存)。
  • 滚动数组可以节省内存(只保留上一行)。
  • 背包问题变种很多:完全背包、多重背包、分组背包等。

多目标规划 (Multi‑Objective Optimization)

模型描述

同时优化多个相互冲突的目标,通常不存在唯一最优解,而是一组 Pareto 最优解。

使用步骤

  1. 确定多个目标函数
  2. 选择求解方法(加权和、ε约束、进化算法等)
  3. 求解得到 Pareto 前沿
  4. 根据偏好选择最终解
Python 代码(使用权重法)
import numpy as np
from scipy.optimize import minimize

# 两个目标函数
def f1(x):
    return x[0]**2 + x[1]**2

def f2(x):
    return (x[0]-1)**2 + (x[1]-1)**2

# 加权和法:将多目标转化为单目标
def weighted_sum(x, w1=0.5, w2=0.5):
    return w1 * f1(x) + w2 * f2(x)

# 约束:x0 + x1 ≥ 1
cons = ({'type': 'ineq', 'fun': lambda x: x[0] + x[1] - 1})

# 不同权重组合得到不同 Pareto 解
pareto_solutions = []
for w1 in np.linspace(0, 1, 6):
    w2 = 1 - w1
    res = minimize(weighted_sum, x0=[0, 0], args=(w1, w2), constraints=cons)
    if res.success:
        pareto_solutions.append((res.x, f1(res.x), f2(res.x)))

print('Pareto 解集(x, f1, f2):')
for sol in pareto_solutions:
    print(f'x={sol[0]}, f1={sol[1]:.3f}, f2={sol[2]:.3f}')

# 绘制 Pareto 前沿(需 matplotlib)
import matplotlib.pyplot as plt
f1_vals = [sol[1] for sol in pareto_solutions]
f2_vals = [sol[2] for sol in pareto_solutions]
plt.scatter(f1_vals, f2_vals, c='red')
plt.xlabel('f1')
plt.ylabel('f2')
plt.title('Pareto Front (Weighted Sum)')
plt.grid(True)
plt.show()
参数说明示例
w1, w2目标权重,反映偏好w1=0.3, w2=0.7
求解方法加权和法、ε约束法、NSGA‑II等weighted_sum
Pareto前沿无法改进任一目标而不损害其他目标的解集

竞赛技巧

  • 加权和法简单,但无法找到非凸 Pareto 前沿。
  • 进化算法(如 DEAP 库的 NSGA‑II)可直接搜索 Pareto 前沿。
  • 在论文中绘制 Pareto 前沿能直观展示权衡关系。
  • 可结合层次分析法(AHP)确定权重。

预测类模型

灰色预测、时间序列、回归分析、神经网络、Logistic回归等。

灰色预测 GM(1,1)

模型描述

适用于小样本、贫信息的不确定系统预测。通过累加生成减弱随机性,建立微分方程进行预测。

使用步骤

  1. 原始数据序列
  2. 累加生成(AGO)
  3. 建立GM(1,1)微分方程
  4. 求解参数 a, b
  5. 预测并累减还原(IAGO)
  6. 检验精度(后验差比、小误差概率)
Python 代码(实现 GM(1,1))
import numpy as np

def gm11(x0, predict_num=5):
    """
    灰色预测 GM(1,1)
    :param x0: 原始序列,一维数组
    :param predict_num: 预测步数
    :return: 预测序列(包括历史拟合值)
    """
    # 累加生成
    x1 = np.cumsum(x0)
    # 紧邻均值生成
    z1 = (x1[:-1] + x1[1:]) / 2.0
    # 构造矩阵 B, Y
    B = np.vstack([-z1, np.ones(len(z1))]).T
    Y = x0[1:].reshape(-1, 1)
    # 最小二乘估计参数
    u = np.linalg.inv(B.T @ B) @ B.T @ Y
    a, b = u[0, 0], u[1, 0]
    # 时间响应式
    def x1_k(k):
        return (x0[0] - b / a) * np.exp(-a * k) + b / a
    # 拟合值
    fit_x1 = [x1_k(i) for i in range(len(x0))]
    fit_x0 = [fit_x1[0]] + [fit_x1[i] - fit_x1[i-1] for i in range(1, len(fit_x1))]
    # 预测值
    forecast_x1 = [x1_k(i) for i in range(len(x0), len(x0) + predict_num)]
    forecast_x0 = [forecast_x1[0] - fit_x1[-1]] + [forecast_x1[i] - forecast_x1[i-1] for i in range(1, predict_num)]
    # 精度检验
    residual = x0 - fit_x0
    S1 = np.std(x0, ddof=1)
    S2 = np.std(residual, ddof=1)
    C = S2 / S1  # 后验差比
    P = np.sum(np.abs(residual - np.mean(residual)) < 0.6745 * S1) / len(x0)  # 小误差概率
    return fit_x0, forecast_x0, a, b, C, P

# 示例
x0 = np.array([71.1, 72.4, 72.4, 72.1, 71.4, 72.0, 71.6])  # 原始数据
fit, forecast, a, b, C, P = gm11(x0, predict_num=3)
print('拟合值:', fit)
print('预测值(未来3期):', forecast)
print('发展系数 a =', a)
print('灰色作用量 b =', b)
print('后验差比 C =', C)
print('小误差概率 P =', P)
print('精度等级:', '优' if C < 0.35 and P > 0.95 else '合格' if C < 0.5 and P > 0.8 else '勉强' if C < 0.65 and P > 0.7 else '不合格')
参数说明示例
x0原始非负序列[71.1,72.4,72.4,...]
predict_num预测步数3
a发展系数,反映增长趋势-0.01
b灰色作用量72.5
C后验差比,越小越好0.25
P小误差概率,越大越好0.95

竞赛技巧

  • 原始数据需非负,否则可进行平移变换。
  • 当发展系数 |a| > 2 时模型可能不适用。
  • 可通过残差修正、新陈代谢等改进模型。
  • 精度检验是必须的,C<0.35、P>0.95为一级(优)。

时间序列 ARIMA

模型描述

对平稳时间序列进行建模,包含自回归(AR)、差分(I)、移动平均(MA)三部分。

使用步骤

  1. 序列平稳性检验(ADF检验)
  2. 若不平稳,进行差分(d阶)
  3. 确定AR阶数p和MA阶数q(ACF/PACF图)
  4. 拟合ARIMA(p,d,q)模型
  5. 模型诊断(残差白噪声检验)
  6. 预测
Python 代码(使用 statsmodels)
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.tsa.stattools import adfuller
from statsmodels.tsa.arima.model import ARIMA
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf

# 示例数据(模拟)
np.random.seed(42)
n = 100
time = pd.date_range('2020-01-01', periods=n, freq='M')
# 生成含有趋势和季节性的序列
trend = 0.05 * np.arange(n)
seasonal = 10 * np.sin(2 * np.pi * np.arange(n) / 12)
noise = np.random.normal(0, 2, n)
series = trend + seasonal + noise
ts = pd.Series(series, index=time)

# 1. 平稳性检验
def adf_test(series):
    result = adfuller(series)
    print('ADF统计量:', result[0])
    print('p值:', result[1])
    print('临界值:', result[4])
    if result[1] < 0.05:
        print('序列平稳')
    else:
        print('序列非平稳,需要差分')
    return result[1]

p_value = adf_test(ts)
if p_value >= 0.05:
    # 差分
    ts_diff = ts.diff().dropna()
    print('一阶差分后ADF检验:')
    adf_test(ts_diff)
else:
    ts_diff = ts

# 2. 确定 p, q(通过ACF/PACF)
fig, axes = plt.subplots(1, 2, figsize=(12,4))
plot_acf(ts_diff, lags=20, ax=axes[0])
plot_pacf(ts_diff, lags=20, ax=axes[1])
plt.show()

# 3. 拟合 ARIMA 模型(示例 p=1,d=1,q=1)
model = ARIMA(ts, order=(1,1,1))
model_fit = model.fit()
print(model_fit.summary())

# 4. 残差诊断
residuals = model_fit.resid
fig, axes = plt.subplots(1, 2, figsize=(12,4))
axes[0].plot(residuals)
axes[0].set_title('残差序列')
plot_acf(residuals, lags=20, ax=axes[1])
axes[1].set_title('残差ACF')
plt.show()

# 5. 预测未来10期
forecast_steps = 10
forecast = model_fit.forecast(steps=forecast_steps)
print('未来10期预测值:', forecast.values)
print('预测标准差:', np.sqrt(model_fit.forecast_variance))
参数说明示例
order (p,d,q)自回归阶数、差分阶数、移动平均阶数(1,1,1)
seasonal_order季节性参数 (P,D,Q,s)(1,1,1,12)
ADF检验p<0.05表示平稳p=0.01
ACF/PACF确定p,q:ACF截尾定q,PACF截尾定p

竞赛技巧

  • 可使用auto_arima自动选择最优参数(需pmdarima库)。
  • 若存在季节性,使用SARIMA(季节性ARIMA)。
  • 预测结果需给出置信区间,增加可信度。
  • 残差应近似白噪声,否则模型未充分提取信息。

线性回归

模型描述

建立因变量与一个或多个自变量之间的线性关系,用于预测和解释。

使用步骤

  1. 数据预处理(标准化、处理缺失值)
  2. 划分训练集和测试集
  3. 拟合线性回归模型
  4. 评估模型(R², RMSE, MAE)
  5. 预测新数据
Python 代码(使用 sklearn)
import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.metrics import r2_score, mean_squared_error, mean_absolute_error
import matplotlib.pyplot as plt

# 生成示例数据
np.random.seed(42)
n = 100
X = np.random.randn(n, 3)  # 3个特征
true_coef = np.array([2.5, -1.3, 0.8])
y = X @ true_coef + np.random.randn(n) * 0.5  # 线性关系加噪声

# 划分训练集/测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 创建线性回归模型
model = LinearRegression()
model.fit(X_train, y_train)

# 输出系数
print('回归系数:', model.coef_)
print('截距:', model.intercept_)
print('R²(训练集):', model.score(X_train, y_train))

# 预测测试集
y_pred = model.predict(X_test)
print('R²(测试集):', r2_score(y_test, y_pred))
print('RMSE:', np.sqrt(mean_squared_error(y_test, y_pred)))
print('MAE:', mean_absolute_error(y_test, y_pred))

# 可视化预测 vs 真实值
plt.scatter(y_test, y_pred, alpha=0.7)
plt.plot([y_test.min(), y_test.max()], [y_test.min(), y_test.max()], 'r--', lw=2)
plt.xlabel('真实值')
plt.ylabel('预测值')
plt.title('线性回归预测效果')
plt.grid(True)
plt.show()

# 使用模型进行新预测
X_new = np.array([[0.5, -0.2, 1.1]])
y_new = model.predict(X_new)
print('新样本预测值:', y_new)
参数说明示例
特征矩阵 X自变量,形状 (n_samples, n_features)100×3
目标向量 y因变量,形状 (n_samples,)100
test_size测试集比例0.2
评估指标R²(越近1越好)、RMSE、MAER²=0.85

竞赛技巧

  • 若特征量纲差异大,建议标准化(StandardScaler)。
  • 可引入多项式特征(PolynomialFeatures)处理非线性关系。
  • 使用交叉验证评估模型稳定性。
  • 若多重共线性严重,考虑岭回归(Ridge)或LASSO。

BP神经网络

模型描述

多层前馈神经网络,通过误差反向传播调整权重,适合复杂的非线性映射。

使用步骤

  1. 数据预处理(归一化)
  2. 设计网络结构(输入层、隐藏层、输出层)
  3. 选择激活函数、损失函数、优化器
  4. 训练网络(前向传播、反向传播)
  5. 评估与预测
Python 代码(使用 tensorflow/keras)
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
import matplotlib.pyplot as plt

# 生成非线性数据
np.random.seed(42)
n = 500
X = np.random.randn(n, 5)
# 非线性关系:y = x1^2 + sin(x2) + x3*x4 + noise
y = X[:,0]**2 + np.sin(X[:,1]) + X[:,2]*X[:,3] + np.random.randn(n)*0.1

# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

# 标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 构建神经网络模型
model = keras.Sequential([
    layers.Dense(64, activation='relu', input_shape=(X_train.shape[1],)),
    layers.Dropout(0.2),
    layers.Dense(32, activation='relu'),
    layers.Dense(16, activation='relu'),
    layers.Dense(1)  # 输出层,回归问题无需激活函数
])

# 编译模型
model.compile(optimizer='adam', loss='mse', metrics=['mae'])

# 训练模型
history = model.fit(X_train_scaled, y_train, 
                    validation_split=0.2, 
                    epochs=100, 
                    batch_size=32, 
                    verbose=0)

# 绘制训练曲线
plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(history.history['loss'], label='训练损失')
plt.plot(history.history['val_loss'], label='验证损失')
plt.xlabel('Epoch')
plt.ylabel('Loss')
plt.legend()
plt.subplot(1,2,2)
plt.plot(history.history['mae'], label='训练MAE')
plt.plot(history.history['val_mae'], label='验证MAE')
plt.xlabel('Epoch')
plt.ylabel('MAE')
plt.legend()
plt.show()

# 评估测试集
test_loss, test_mae = model.evaluate(X_test_scaled, y_test, verbose=0)
print('测试集 MSE:', test_loss)
print('测试集 MAE:', test_mae)

# 预测
y_pred = model.predict(X_test_scaled).flatten()
print('前5个预测值:', y_pred[:5])
print('对应真实值:', y_test[:5])

# 保存模型(可选)
# model.save('bp_model.h5')
参数说明示例
网络结构各层神经元数量[64,32,16,1]
激活函数隐藏层常用ReLU,输出层线性'relu'
损失函数回归常用MSE,分类用交叉熵'mse'
优化器Adam, SGD, RMSprop'adam'
epochs, batch_size训练轮数、批大小100, 32

竞赛技巧

  • 数据归一化可加速收敛并提高性能。
  • 使用早停(EarlyStopping)防止过拟合。
  • Dropout层可减少过拟合。
  • 若数据量小,可用K折交叉验证。
  • 神经网络是黑箱,解释性差,但预测能力强。

Logistic回归

模型描述

用于二分类或多分类问题,通过Sigmoid函数将线性组合映射到概率。

使用步骤

  1. 数据准备(标签编码)
  2. 特征标准化
  3. 拟合Logistic回归模型
  4. 评估(准确率、精确率、召回率、ROC曲线)
  5. 预测新样本类别
Python 代码(使用 sklearn)
import numpy as np
import pandas as pd
from sklearn.linear_model import LogisticRegression
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score, confusion_matrix, classification_report
import matplotlib.pyplot as plt
import seaborn as sns

# 生成二分类数据
np.random.seed(42)
n = 300
X = np.random.randn(n, 4)
# 生成概率:使用线性组合 + sigmoid
z = 0.8*X[:,0] - 1.2*X[:,1] + 0.5*X[:,2] + 0.3*X[:,3]
prob = 1 / (1 + np.exp(-z))
y = (prob > 0.5).astype(int)  # 根据概率生成标签

# 划分数据集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 创建Logistic回归模型
model = LogisticRegression(penalty='l2', C=1.0, solver='lbfgs', max_iter=1000)
model.fit(X_train_scaled, y_train)

# 预测
y_pred = model.predict(X_test_scaled)
y_pred_proba = model.predict_proba(X_test_scaled)[:, 1]  # 正类概率

# 评估
print('准确率:', accuracy_score(y_test, y_pred))
print('精确率:', precision_score(y_test, y_pred))
print('召回率:', recall_score(y_test, y_pred))
print('F1分数:', f1_score(y_test, y_pred))
print('AUC:', roc_auc_score(y_test, y_pred_proba))
print('\n分类报告:')
print(classification_report(y_test, y_pred))

# 混淆矩阵
cm = confusion_matrix(y_test, y_pred)
plt.figure(figsize=(6,5))
sns.heatmap(cm, annot=True, fmt='d', cmap='Blues')
plt.xlabel('预测标签')
plt.ylabel('真实标签')
plt.title('混淆矩阵')
plt.show()

# 新样本预测
X_new = np.array([[0.5, -0.2, 1.1, 0.3]])
X_new_scaled = scaler.transform(X_new)
pred_class = model.predict(X_new_scaled)
pred_prob = model.predict_proba(X_new_scaled)
print('新样本预测类别:', pred_class[0])
print('新样本预测概率(类0, 类1):', pred_prob[0])
参数说明示例
penalty正则化类型:'l1', 'l2', 'elasticnet', None'l2'
C正则化强度,越小正则化越强1.0
solver优化算法:'lbfgs', 'liblinear', 'saga'等'lbfgs'
max_iter最大迭代次数1000
multi_class多分类策略:'ovr', 'multinomial''ovr'

竞赛技巧

  • 若特征存在多重共线性,可使用L1正则化(penalty='l1')进行特征选择。
  • 对于不平衡数据,可设置class_weight='balanced'。
  • 绘制ROC曲线和计算AUC可全面评估分类性能。
  • 多分类问题可使用one-vs-rest或multinomial。

评价类模型

层次分析法、熵权法、TOPSIS、模糊综合评价、PCA、DEA等。

层次分析法 (AHP)

模型描述

将复杂决策分解为层次结构,通过两两比较构造判断矩阵,计算权重并一致性检验。

使用步骤

  1. 建立层次结构(目标层、准则层、方案层)
  2. 构造判断矩阵(1‑9标度法)
  3. 计算权重向量(特征向量法)
  4. 一致性检验(CR < 0.1)
  5. 计算各方案总排序
Python 代码(实现 AHP)
import numpy as np

def ahp_weights(judgment_matrix):
    """
    计算判断矩阵的权重向量并进行一致性检验
    :param judgment_matrix: n×n 判断矩阵
    :return: 权重向量, CI, RI, CR
    """
    n = judgment_matrix.shape[0]
    # 计算特征值和特征向量
    eigenvalues, eigenvectors = np.linalg.eig(judgment_matrix)
    max_eig = np.max(eigenvalues.real)
    max_idx = np.argmax(eigenvalues.real)
    eig_vec = eigenvectors[:, max_idx].real
    # 权重向量(归一化)
    weights = eig_vec / np.sum(eig_vec)
    # 一致性指标 CI
    CI = (max_eig - n) / (n - 1)
    # 随机一致性指标 RI(n≤15)
    RI_dict = {1:0, 2:0, 3:0.58, 4:0.90, 5:1.12, 6:1.24, 7:1.32, 8:1.41, 9:1.45, 10:1.49, 11:1.51, 12:1.48, 13:1.56, 14:1.57, 15:1.59}
    RI = RI_dict.get(n, 1.6)
    # 一致性比率 CR
    CR = CI / RI
    return weights, CI, RI, CR

# 示例:选择旅游地(目标层:景色、费用、居住、饮食、交通)
# 准则层对目标层的判断矩阵(5个准则)
judgment = np.array([
    [1, 1/2, 4, 3, 3],
    [2, 1, 7, 5, 5],
    [1/4, 1/7, 1, 1/2, 1/3],
    [1/3, 1/5, 2, 1, 1],
    [1/3, 1/5, 3, 1, 1]
])

weights, CI, RI, CR = ahp_weights(judgment)
print('准则层权重:', weights)
print('CI =', CI)
print('RI =', RI)
print('CR =', CR)
if CR < 0.1:
    print('一致性检验通过(CR < 0.1)')
else:
    print('一致性检验未通过,需调整判断矩阵')

# 假设有三个方案(A,B,C)对每个准则的判断矩阵(此处简化,实际需分别构造)
# 通常需要构建5个3×3矩阵,计算每个准则下方案的权重,然后合成总排序
print('\n假设方案层权重矩阵已计算,总排序 = 准则权重 × 方案权重矩阵')
参数说明示例
判断矩阵正互反矩阵,aij表示i相对于j的重要性(1‑9标度)见示例
权重向量各准则的相对重要性[0.3,0.5,0.1,0.05,0.05]
CI一致性指标,CI=(λ_max‑n)/(n‑1)0.03
RI随机一致性指标(查表)1.12 (n=5)
CR一致性比率,CR=CI/RI,应<0.10.027

竞赛技巧

  • 判断矩阵通常由专家打分或文献确定,可结合德尔菲法。
  • 若CR不通过,可微调判断矩阵(如将不一致的元素调整为其相邻标度)。
  • 可使用yaahp等软件辅助计算。
  • 层次分析法主观性较强,常与其他客观赋权法结合。

熵权法

模型描述

根据指标值的变异程度客观赋权,变异越大(熵越小)权重越大。

使用步骤

  1. 数据标准化(消除量纲)
  2. 计算每个指标的比重
  3. 计算熵值
  4. 计算差异系数
  5. 归一化得到权重
Python 代码(熵权法)
import numpy as np

def entropy_weight(data):
    """
    熵权法计算指标权重
    :param data: m×n 矩阵,m个样本,n个指标(要求指标为正向指标)
    :return: 权重向量 (n,)
    """
    # 数据标准化(极差法,转化为[0,1])
    min_val = np.min(data, axis=0)
    max_val = np.max(data, axis=0)
    norm_data = (data - min_val) / (max_val - min_val + 1e-8)  # 防止除零
    # 计算比重
    p = norm_data / np.sum(norm_data, axis=0, keepdims=True)
    # 计算熵值
    k = 1 / np.log(data.shape[0])
    entropy = -k * np.sum(p * np.log(p + 1e-8), axis=0)  # 防止log(0)
    # 差异系数
    d = 1 - entropy
    # 权重
    weights = d / np.sum(d)
    return weights

# 示例:5个样本,4个指标(均为正向指标,越大越好)
data = np.array([
    [100, 80, 90, 70],
    [90, 85, 88, 75],
    [95, 78, 92, 80],
    [85, 90, 85, 85],
    [80, 82, 95, 90]
])

weights = entropy_weight(data)
print('各指标权重:', weights)
print('权重和:', np.sum(weights))

# 若存在负向指标(越小越好),需先正向化
# 方法:取倒数或 max‑x 变换
# 例如,对于负向指标列 j:data[:, j] = max(data[:, j]) - data[:, j]
参数说明示例
data原始数据矩阵,m×n5×4
正向指标越大越好,需统一成绩、收益
负向指标越小越好,需正向化成本、污染
熵值反映指标变异程度,熵越大变异越小0.92
差异系数d=1‑e,越大权重越大0.08

竞赛技巧

  • 熵权法完全依赖数据分布,若某指标值全相同,熵为1,权重为0。
  • 可结合AHP(主观)与熵权法(客观)得到组合权重。
  • 数据标准化方法可根据情况选择极差法、z‑score等。
  • 适用于评价指标间相关性不强的情况。

TOPSIS(逼近理想解排序法)

模型描述

通过计算各方案与正理想解、负理想解的相对接近度进行排序。

使用步骤

  1. 数据预处理(归一化)
  2. 确定权重(可结合AHP、熵权法)
  3. 构造加权规范化矩阵
  4. 确定正、负理想解
  5. 计算各方案到正负理想解的距离
  6. 计算相对接近度并排序
Python 代码(TOPSIS)
import numpy as np

def topsis(data, weights=None, positive_idx=None):
    """
    TOPSIS 综合评价
    :param data: m×n 矩阵,m个方案,n个指标
    :param weights: 权重向量,长度n(若None则等权)
    :param positive_idx: 正向指标列表(默认全为正向),负向指标需在列表中剔除
    :return: 相对接近度,排序索引
    """
    m, n = data.shape
    # 默认等权
    if weights is None:
        weights = np.ones(n) / n
    # 默认全为正向指标
    if positive_idx is None:
        positive_idx = list(range(n))
    # 1. 数据归一化(向量归一化)
    norm_data = data / np.sqrt(np.sum(data**2, axis=0))
    # 2. 加权规范化矩阵
    weighted = norm_data * weights
    # 3. 正负理想解
    positive_ideal = np.zeros(n)
    negative_ideal = np.zeros(n)
    for j in range(n):
        if j in positive_idx:
            positive_ideal[j] = np.max(weighted[:, j])
            negative_ideal[j] = np.min(weighted[:, j])
        else:  # 负向指标
            positive_ideal[j] = np.min(weighted[:, j])
            negative_ideal[j] = np.max(weighted[:, j])
    # 4. 距离计算
    d_positive = np.sqrt(np.sum((weighted - positive_ideal)**2, axis=1))
    d_negative = np.sqrt(np.sum((weighted - negative_ideal)**2, axis=1))
    # 5. 相对接近度
    closeness = d_negative / (d_positive + d_negative + 1e-8)
    # 排序(从大到小)
    rank = np.argsort(-closeness)
    return closeness, rank

# 示例:5个方案,4个指标(前3个正向,第4个负向)
data = np.array([
    [100, 80, 90, 30],   # 方案1
    [90, 85, 88, 25],    # 方案2
    [95, 78, 92, 20],    # 方案3
    [85, 90, 85, 35],    # 方案4
    [80, 82, 95, 40]     # 方案5
])
weights = np.array([0.3, 0.2, 0.3, 0.2])  # 权重
positive_idx = [0, 1, 2]  # 前三个为正向,第四个为负向

closeness, rank = topsis(data, weights, positive_idx)
print('相对接近度:', closeness)
print('排序(方案索引,从优到劣):', rank)
print('最优方案:', rank[0] + 1)
print('最劣方案:', rank[-1] + 1)

# 可视化
import matplotlib.pyplot as plt
plt.bar(range(1, len(closeness)+1), closeness[rank])
plt.xlabel('方案排名')
plt.ylabel('相对接近度')
plt.title('TOPSIS 综合评价结果')
plt.grid(axis='y')
plt.show()
参数说明示例
data原始数据矩阵,m×n5×4
weights指标权重,长度n[0.3,0.2,0.3,0.2]
positive_idx正向指标索引列表[0,1,2]
相对接近度值越大方案越优,范围[0,1]0.75

竞赛技巧

  • TOPSIS对数据分布无要求,但需明确指标方向。
  • 权重可来自AHP、熵权法或专家打分。
  • 若出现两个方案接近度相同,可考虑增加小数位或使用其他方法。
  • 可与灰色关联分析结合,形成灰色TOPSIS。

模糊综合评价

模型描述

运用模糊数学处理定性评价,将模糊因素定量化,适用于评价指标难以精确量化的问题。

使用步骤

  1. 确定因素集(评价指标)和评语集(评价等级)
  2. 构造隶属度矩阵(单因素评价)
  3. 确定权重向量
  4. 合成模糊综合评价结果(加权平均型)
  5. 对结果向量进行分析(最大隶属度或加权评分)
Python 代码(模糊综合评价)
import numpy as np

def fuzzy_evaluation(weight, membership_matrix, score_vector=None):
    """
    模糊综合评价
    :param weight: 权重向量 (n,)
    :param membership_matrix: 隶属度矩阵 n×k,n个因素,k个评语等级
    :param score_vector: 各评语等级的分值向量 (k,),用于加权得分(可选)
    :return: 综合评价向量 (k,),加权得分(若提供score_vector)
    """
    # 模糊合成(加权平均型)
    result = np.dot(weight, membership_matrix)
    # 归一化(使和为1)
    result = result / np.sum(result)
    # 若提供分值向量,计算加权得分
    if score_vector is not None:
        weighted_score = np.dot(result, score_vector)
        return result, weighted_score
    return result

# 示例:评价某产品的“质量”、“价格”、“服务”
# 因素集 U = {质量, 价格, 服务}
# 评语集 V = {很好, 好, 一般, 差}
# 权重向量(质量0.5, 价格0.3, 服务0.2)
weight = np.array([0.5, 0.3, 0.2])

# 隶属度矩阵(每一行表示一个因素对各个评语的隶属度)
membership = np.array([
    [0.6, 0.3, 0.1, 0.0],   # 质量:60%很好,30%好,10%一般,0%差
    [0.1, 0.4, 0.4, 0.1],   # 价格
    [0.2, 0.5, 0.2, 0.1]    # 服务
])

# 评语分值(很好=100,好=80,一般=60,差=40)
score = np.array([100, 80, 60, 40])

result, total_score = fuzzy_evaluation(weight, membership, score)
print('模糊综合评价向量:', result)
print('加权总分:', total_score)
print('最大隶属度评语:', ['很好','好','一般','差'][np.argmax(result)])

# 多方案比较
membership2 = np.array([
    [0.4, 0.4, 0.2, 0.0],
    [0.2, 0.5, 0.2, 0.1],
    [0.3, 0.4, 0.2, 0.1]
])
result2, score2 = fuzzy_evaluation(weight, membership2, score)
print('\n方案二综合评价向量:', result2)
print('方案二加权总分:', score2)
print('方案一 vs 方案二:', '方案一更优' if total_score > score2 else '方案二更优')
参数说明示例
weight因素权重向量[0.5,0.3,0.2]
membership_matrix隶属度矩阵,n×k3×4
score_vector评语分值向量[100,80,60,40]
合成算子常用加权平均型,也可用取大取小型加权平均

竞赛技巧

  • 隶属度可通过调查问卷、专家打分或统计方法确定。
  • 若评价因素多,可建立多级模糊综合评价(先对子因素评价,再对父因素)。
  • 合成算子选择影响结果,加权平均型更细腻,取大取小型突出主要因素。
  • 模糊综合评价适合定性指标多的场景,如满意度、风险评估。

主成分分析 (PCA)

模型描述

通过正交变换将高维相关变量转化为少数不相关的主成分,用于降维、综合评价。

使用步骤

  1. 数据标准化(消除量纲)
  2. 计算协方差矩阵(或相关系数矩阵)
  3. 计算特征值和特征向量
  4. 选择主成分(累计贡献率≥85%)
  5. 计算主成分得分及综合得分
Python 代码(使用 sklearn)
import numpy as np
import pandas as pd
from sklearn.decomposition import PCA
from sklearn.preprocessing import StandardScaler
import matplotlib.pyplot as plt

# 生成示例数据:10个样本,5个指标
np.random.seed(42)
data = np.random.randn(10, 5) * 10 + np.array([50, 30, 20, 40, 60])

# 1. 标准化
scaler = StandardScaler()
data_scaled = scaler.fit_transform(data)

# 2. PCA
pca = PCA()
pca.fit(data_scaled)

# 3. 特征值、贡献率
print('各主成分方差(特征值):', pca.explained_variance_)
print('各主成分贡献率:', pca.explained_variance_ratio_)
print('累计贡献率:', np.cumsum(pca.explained_variance_ratio_))

# 选择主成分数(累计贡献率≥85%)
n_components = np.argmax(np.cumsum(pca.explained_variance_ratio_) >= 0.85) + 1
print('建议主成分数:', n_components)

# 重新PCA,保留n个主成分
pca2 = PCA(n_components=n_components)
principal_components = pca2.fit_transform(data_scaled)
print('主成分得分矩阵形状:', principal_components.shape)

# 4. 综合得分(以第一主成分贡献率为权重加权)
weights = pca2.explained_variance_ratio_ / np.sum(pca2.explained_variance_ratio_)
comprehensive_score = np.dot(principal_components, weights)
print('综合得分:', comprehensive_score)

# 排序
rank = np.argsort(-comprehensive_score)
print('样本排序(从优到劣):', rank)

# 可视化
plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(range(1, len(pca.explained_variance_ratio_)+1), pca.explained_variance_ratio_, 'bo-')
plt.plot(range(1, len(pca.explained_variance_ratio_)+1), np.cumsum(pca.explained_variance_ratio_), 'ro-')
plt.xlabel('主成分')
plt.ylabel('贡献率')
plt.legend(['单个贡献','累计贡献'])
plt.grid(True)
plt.subplot(1,2,2)
plt.scatter(principal_components[:,0], principal_components[:,1])
plt.xlabel('PC1')
plt.ylabel('PC2')
plt.title('主成分散点图')
plt.grid(True)
plt.show()
参数说明示例
n_components保留的主成分数2
explained_variance_ratio_各主成分贡献率[0.6,0.3,...]
累计贡献率通常要求≥85%0.9
主成分得分样本在新坐标系下的坐标矩阵

竞赛技巧

  • PCA前务必标准化,否则量纲大的指标会主导结果。
  • 若指标间相关性弱,PCA降维效果不佳。
  • 主成分的含义需结合特征向量(载荷)解释。
  • 可用于综合评价(综合得分),也可作为其他模型的输入(降维)。

数据包络分析 (DEA)

模型描述

评价具有多输入多输出的决策单元(DMU)的相对效率,无需预设函数形式。

使用步骤

  1. 确定DMU集合、输入指标、输出指标
  2. 选择DEA模型(如CCR、BCC)
  3. 构建线性规划模型
  4. 求解各DMU的效率值(0‑1)
  5. 分析无效DMU的改进方向(松弛变量)
Python 代码(使用 pyDEA)
# 安装:pip install pyDEA
import numpy as np
from pyDEA.dea_models import DEA

# 示例:5个DMU,2个输入(人力、资金),2个输出(产量、利润)
inputs = np.array([
    [10, 200],  # DMU1
    [15, 300],  # DMU2
    [12, 250],  # DMU3
    [8, 180],   # DMU4
    [20, 400]   # DMU5
])
outputs = np.array([
    [100, 50],  # DMU1
    [120, 60],  # DMU2
    [110, 55],  # DMU3
    [90, 45],   # DMU4
    [130, 65]   # DMU5
])

# 创建DEA模型(默认CCR,规模报酬不变)
dea = DEA(inputs, outputs)
# 求解
efficiencies = dea.fit()
print('各DMU的效率值(CCR):', efficiencies)

# 若需BCC模型(规模报酬可变)
from pyDEA.dea_models import BCC
bcc = BCC(inputs, outputs)
bcc_efficiencies = bcc.fit()
print('各DMU的效率值(BCC):', bcc_efficiencies)

# 判断规模报酬(CCR效率 vs BCC效率)
scale_efficiency = efficiencies / bcc_efficiencies
print('规模效率:', scale_efficiency)
print('规模报酬:', ['递增' if se < 1 else '不变' if se == 1 else '递减' for se in scale_efficiency])

# 无效DMU的改进目标(松弛变量)
for i in range(len(inputs)):
    if efficiencies[i] < 0.999:  # 非有效
        print(f'\nDMU{i+1} 无效,效率={efficiencies[i]:.3f}')
        # 此处可调用dea的松弛变量结果,但pyDEA未直接提供,可手动计算
        # 简化为输出改进方向
        print('  应减少输入或增加输出')
参数说明示例
inputs输入矩阵,m×p5×2
outputs输出矩阵,m×q5×2
模型类型CCR(CRS)、BCC(VRS)CCR
效率值0‑1,1表示DEA有效0.95
规模报酬递增、不变、递减递增

竞赛技巧

  • 输入输出指标应满足“ isotonicity ”(输入增加不应导致输出减少)。
  • DMU数量应至少为输入输出指标数之和的2倍。
  • 可结合Malmquist指数分析效率动态变化。
  • DEA结果受极端值影响大,需清洗数据。

微分方程模型

SIR传染病模型、SEIR模型、Logistic增长、Lotka-Volterra捕食模型等。

SIR传染病模型

模型描述

将人群分为易感者(S)、感染者(I)、康复者(R),通过微分方程描述传染病传播过程。

使用步骤

  1. 确定参数:感染率β、恢复率γ
  2. 设定初始条件 S0, I0, R0
  3. 建立微分方程组
  4. 数值求解(如欧拉法、Runge‑Kutta)
  5. 分析基本再生数 R0 = β/γ
  6. 预测疫情趋势和峰值
Python 代码(使用 odeint)
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt

def sir_model(y, t, beta, gamma):
    """SIR模型微分方程组"""
    S, I, R = y
    dSdt = -beta * S * I
    dIdt = beta * S * I - gamma * I
    dRdt = gamma * I
    return [dSdt, dIdt, dRdt]

# 参数设置
beta = 0.3   # 感染率
gamma = 0.1  # 恢复率
R0 = beta / gamma
print('基本再生数 R0 =', R0)

# 初始条件(总人口归一化为1)
S0 = 0.99    # 易感者比例
I0 = 0.01    # 感染者比例
R0_init = 0.0  # 康复者比例
y0 = [S0, I0, R0_init]

# 时间点
t = np.linspace(0, 200, 200)

# 求解微分方程
solution = odeint(sir_model, y0, t, args=(beta, gamma))
S, I, R = solution.T

# 绘图
plt.figure(figsize=(10,6))
plt.plot(t, S, label='易感者 S')
plt.plot(t, I, label='感染者 I')
plt.plot(t, R, label='康复者 R')
plt.xlabel('时间(天)')
plt.ylabel('人口比例')
plt.title('SIR传染病模型模拟(β=0.3, γ=0.1)')
plt.legend()
plt.grid(True)
plt.show()

# 计算峰值
peak_idx = np.argmax(I)
peak_time = t[peak_idx]
peak_value = I[peak_idx]
print('感染峰值时间:', peak_time)
print('感染峰值比例:', peak_value)
print('最终易感者比例:', S[-1])
参数说明示例
β (beta)感染率,每个感染者每天接触并感染易感者的概率0.3
γ (gamma)恢复率,感染者每天恢复的概率0.1
R0基本再生数,R0=β/γ,大于1则疫情扩散3.0
S0, I0, R0_init初始易感、感染、康复者比例0.99,0.01,0

竞赛技巧

  • 可加入出生率、死亡率、隔离措施(时变β)使模型更真实。
  • 若数据充足,可用最小二乘法估计参数β, γ。
  • 绘制相图(S‑I平面)观察轨线。
  • SIR模型是许多扩展模型的基础(如SEIR, SIRS)。

SEIR模型

模型描述

在SIR基础上增加潜伏期人群(E),更贴合COVID‑19等具有潜伏期的传染病。

使用步骤

  1. 确定参数:感染率β、潜伏期转染率σ、恢复率γ
  2. 设定初始条件 S0, E0, I0, R0
  3. 建立微分方程组
  4. 数值求解
  5. 分析疫情趋势
Python 代码(SEIR模型)
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt

def seir_model(y, t, beta, sigma, gamma):
    """SEIR模型微分方程组"""
    S, E, I, R = y
    dSdt = -beta * S * I
    dEdt = beta * S * I - sigma * E
    dIdt = sigma * E - gamma * I
    dRdt = gamma * I
    return [dSdt, dEdt, dIdt, dRdt]

# 参数设置
beta = 0.4    # 感染率
sigma = 1/5   # 潜伏期转染率(潜伏期平均5天)
gamma = 1/10  # 恢复率(感染期平均10天)
R0 = beta / gamma
print('基本再生数 R0 =', R0)

# 初始条件
S0 = 0.99
E0 = 0.0
I0 = 0.01
R0_init = 0.0
y0 = [S0, E0, I0, R0_init]

# 时间点
t = np.linspace(0, 300, 300)

# 求解
solution = odeint(seir_model, y0, t, args=(beta, sigma, gamma))
S, E, I, R = solution.T

# 绘图
plt.figure(figsize=(10,6))
plt.plot(t, S, label='易感者 S')
plt.plot(t, E, label='潜伏者 E')
plt.plot(t, I, label='感染者 I')
plt.plot(t, R, label='康复者 R')
plt.xlabel('时间(天)')
plt.ylabel('人口比例')
plt.title('SEIR模型模拟(β=0.4, σ=0.2, γ=0.1)')
plt.legend()
plt.grid(True)
plt.show()

# 计算峰值
peak_idx = np.argmax(I)
peak_time = t[peak_idx]
peak_value = I[peak_idx]
print('感染峰值时间:', peak_time)
print('感染峰值比例:', peak_value)
print('最终易感者比例:', S[-1])
参数说明示例
β (beta)感染率0.4
σ (sigma)潜伏期转染率,σ=1/潜伏期天数0.2
γ (gamma)恢复率0.1
R0基本再生数4.0

竞赛技巧

  • 潜伏期参数σ可从流行病学资料获得。
  • 可加入无症状感染者、隔离、疫苗接种等扩展。
  • 使用实际疫情数据拟合参数,预测未来趋势。
  • SEIR模型可进一步扩展为SEIRS(康复者失去免疫力)。

Logistic增长模型

模型描述

描述种群在有限资源下的增长过程,增长率先增后减,最终趋于环境容量K。

使用步骤

  1. 确定参数:内禀增长率r、环境容量K
  2. 设定初始种群数量 N0
  3. 求解微分方程(解析解或数值解)
  4. 拟合实际数据估计r, K
  5. 预测种群数量变化
Python 代码(Logistic增长)
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
from scipy.optimize import curve_fit

# 1. 解析解模拟
def logistic_analytic(t, N0, r, K):
    """Logistic方程的解析解"""
    return K / (1 + (K/N0 - 1) * np.exp(-r * t))

# 参数
N0 = 10      # 初始数量
r = 0.2      # 内禀增长率
K = 1000     # 环境容量

# 时间点
t = np.linspace(0, 50, 100)
N = logistic_analytic(t, N0, r, K)

plt.figure(figsize=(10,5))
plt.subplot(1,2,1)
plt.plot(t, N, 'b-', linewidth=2)
plt.xlabel('时间')
plt.ylabel('种群数量')
plt.title('Logistic增长曲线(解析解)')
plt.grid(True)

# 2. 微分方程数值解
def logistic_ode(N, t, r, K):
    """Logistic微分方程"""
    dNdt = r * N * (1 - N/K)
    return dNdt

sol = odeint(logistic_ode, N0, t, args=(r, K))
plt.subplot(1,2,2)
plt.plot(t, sol, 'r-', linewidth=2)
plt.xlabel('时间')
plt.ylabel('种群数量')
plt.title('Logistic增长曲线(数值解)')
plt.grid(True)
plt.tight_layout()
plt.show()

# 3. 参数估计(拟合实际数据)
# 假设有观测数据
t_data = np.array([0, 5, 10, 15, 20, 25, 30, 35, 40])
N_data = np.array([10, 30, 80, 200, 500, 750, 900, 950, 980])

# 定义拟合函数
def logistic_fit(t, r, K):
    N0_fixed = 10  # 已知初始值
    return K / (1 + (K/N0_fixed - 1) * np.exp(-r * t))

# 拟合
popt, pcov = curve_fit(logistic_fit, t_data, N_data, bounds=([0.01, 500], [1, 2000]))
r_fit, K_fit = popt
print('拟合参数:r =', r_fit, ', K =', K_fit)
print('参数标准差:', np.sqrt(np.diag(pcov)))

# 绘制拟合效果
t_fine = np.linspace(0, 50, 100)
N_fit = logistic_fit(t_fine, r_fit, K_fit)
plt.figure(figsize=(8,5))
plt.scatter(t_data, N_data, color='red', label='观测数据')
plt.plot(t_fine, N_fit, 'b-', label='拟合曲线')
plt.xlabel('时间')
plt.ylabel('种群数量')
plt.title('Logistic模型拟合实际数据')
plt.legend()
plt.grid(True)
plt.show()
参数说明示例
r内禀增长率,单位时间内每个个体的增长率0.2
K环境容量,资源限制下的最大种群数量1000
N0初始种群数量10
拐点增长速度最快的点,出现在 N=K/2 时t=17.3

竞赛技巧

  • Logistic模型也可用于产品扩散、技术采纳等社会学问题。
  • 若数据早期增长更快,可考虑Gompertz模型。
  • 拟合时需合理设定参数边界,避免过拟合。
  • 可引入时变环境容量K(t)以反映环境变化。

Lotka‑Volterra捕食模型

模型描述

描述捕食者与猎物相互作用的动态系统,呈现周期性振荡。

使用步骤

  1. 确定参数:猎物增长率α、捕食者死亡率γ、相互作用系数β, δ
  2. 设定初始种群数量 x0, y0
  3. 建立微分方程组
  4. 数值求解
  5. 分析平衡点及稳定性
  6. 绘制相图和时间序列
Python 代码(Lotka‑Volterra)
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt

def lotka_volterra(z, t, alpha, beta, delta, gamma):
    """Lotka‑Volterra模型"""
    x, y = z  # x:猎物, y:捕食者
    dxdt = alpha * x - beta * x * y
    dydt = delta * x * y - gamma * y
    return [dxdt, dydt]

# 参数(典型值)
alpha = 0.1   # 猎物自然增长率
beta = 0.02   # 捕食者对猎物的捕食率
delta = 0.01  # 捕食者增长率(取决于捕食效率)
gamma = 0.1   # 捕食者自然死亡率

# 初始数量
x0 = 40  # 猎物
y0 = 9   # 捕食者
z0 = [x0, y0]

# 时间点
t = np.linspace(0, 200, 1000)

# 求解
sol = odeint(lotka_volterra, z0, t, args=(alpha, beta, delta, gamma))
x, y = sol.T

# 绘制时间序列
plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(t, x, 'g-', label='猎物')
plt.plot(t, y, 'r-', label='捕食者')
plt.xlabel('时间')
plt.ylabel('种群数量')
plt.title('Lotka‑Volterra模型时间序列')
plt.legend()
plt.grid(True)

# 绘制相图
plt.subplot(1,2,2)
plt.plot(x, y, 'b-', alpha=0.7)
plt.scatter(x[0], y[0], color='red', label='起点')
plt.xlabel('猎物数量')
plt.ylabel('捕食者数量')
plt.title('相图(极限环)')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()

# 平衡点分析
# 平衡点1:x=0, y=0(灭绝)
# 平衡点2:x*=gamma/delta, y*=alpha/beta
x_star = gamma / delta
y_star = alpha / beta
print('非零平衡点:猎物 =', x_star, '捕食者 =', y_star)

# 计算周期振荡的振幅和周期(近似)
peak_indices, _ = find_peaks(x)
if len(peak_indices) > 1:
    period = np.mean(np.diff(t[peak_indices]))
    print('猎物振荡周期(近似):', period, '时间单位')
参数说明示例
α (alpha)猎物自然增长率(无捕食者时)0.1
β (beta)捕食率,反映捕食者捕食效率0.02
δ (delta)捕食者增长率系数0.01
γ (gamma)捕食者自然死亡率0.1
平衡点(γ/δ, α/β),系统围绕该点振荡(10,5)

竞赛技巧

  • 模型可扩展为三种群(两个捕食者一个猎物)或加入竞争。
  • 可加入环境承载力(Logistic增长)使模型更现实。
  • 相图可直观展示系统动态,平衡点稳定性可通过雅可比矩阵分析。
  • 可用于经济周期、生态管理等领域。

统计分析模型

聚类分析、主成分分析、蒙特卡洛模拟、支持向量机等。

K‑means聚类

模型描述

将数据划分为K个簇,使同一簇内样本距离最小,不同簇间样本距离最大。

使用步骤

  1. 数据标准化
  2. 选择簇数K(肘部法、轮廓系数)
  3. 初始化聚类中心(随机或K‑means++)
  4. 迭代:分配样本到最近中心 → 重新计算中心
  5. 评估聚类效果
Python 代码(使用 sklearn)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score
from sklearn.datasets import make_blobs

# 生成示例数据
X, y_true = make_blobs(n_samples=300, centers=4, cluster_std=0.8, random_state=42)

# 标准化
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 1. 肘部法选择K
inertia = []
K_range = range(1, 11)
for k in K_range:
    kmeans = KMeans(n_clusters=k, random_state=42, n_init='auto')
    kmeans.fit(X_scaled)
    inertia.append(kmeans.inertia_)

plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(K_range, inertia, 'bo-')
plt.xlabel('簇数 K')
plt.ylabel('误差平方和 (Inertia)')
plt.title('肘部法')
plt.grid(True)

# 2. 轮廓系数
silhouette = []
for k in K_range[1:]:  # 轮廓系数要求至少2个簇
    kmeans = KMeans(n_clusters=k, random_state=42, n_init='auto')
    labels = kmeans.fit_predict(X_scaled)
    silhouette.append(silhouette_score(X_scaled, labels))

plt.subplot(1,2,2)
plt.plot(K_range[1:], silhouette, 'ro-')
plt.xlabel('簇数 K')
plt.ylabel('轮廓系数')
plt.title('轮廓系数法')
plt.grid(True)
plt.tight_layout()
plt.show()

# 根据轮廓系数选择最佳K(越大越好)
best_k = K_range[1:][np.argmax(silhouette)]
print('建议簇数:', best_k)

# 3. 使用最佳K进行聚类
kmeans = KMeans(n_clusters=best_k, random_state=42, n_init='auto')
labels = kmeans.fit_predict(X_scaled)
centers = scaler.inverse_transform(kmeans.cluster_centers_)  # 还原到原始尺度

# 可视化
plt.figure(figsize=(8,6))
scatter = plt.scatter(X[:,0], X[:,1], c=labels, cmap='viridis', alpha=0.7)
plt.scatter(centers[:,0], centers[:,1], c='red', marker='X', s=200, label='聚类中心')
plt.xlabel('特征1')
plt.ylabel('特征2')
plt.title('K‑means聚类结果')
plt.legend()
plt.colorbar(scatter, label='簇标签')
plt.grid(True)
plt.show()

print('聚类中心(原始尺度):')
print(centers)
print('每个簇的样本数:', np.bincount(labels))
参数说明示例
n_clusters簇数K4
init初始化方法:'random', 'k‑means++''k‑means++'
max_iter最大迭代次数300
inertia误差平方和,越小聚类越紧150.2
轮廓系数范围[-1,1],越大聚类越好0.65

竞赛技巧

  • K‑means对异常值敏感,可先进行异常检测。
  • 数据需标准化,否则量纲大的特征主导距离计算。
  • 肘部法可能不清晰,可结合轮廓系数、Gap statistic等。
  • 可尝试多次随机初始化,选择最佳结果。

层次聚类

模型描述

通过计算样本间距离,逐步合并(凝聚)或分裂(分裂)形成树状结构。

使用步骤

  1. 计算距离矩阵
  2. 选择 linkage 方法(ward, complete, average, single)
  3. 构建树状图(dendrogram)
  4. 确定切割高度得到聚类结果
  5. 分析聚类层次
Python 代码(层次聚类)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.cluster.hierarchy import dendrogram, linkage, fcluster
from sklearn.preprocessing import StandardScaler
from sklearn.datasets import make_blobs

# 生成数据
X, _ = make_blobs(n_samples=50, centers=3, cluster_std=0.8, random_state=42)
scaler = StandardScaler()
X_scaled = scaler.fit_transform(X)

# 1. 计算 linkage 矩阵
Z = linkage(X_scaled, method='ward')  # ward方法最小化簇内方差
print('Linkage矩阵形状:', Z.shape)
print('前5行:')
print(Z[:5])

# 2. 绘制树状图
plt.figure(figsize=(12,5))
dendrogram(Z, truncate_mode='lastp', p=12, show_leaf_counts=True, leaf_rotation=90)
plt.xlabel('样本索引')
plt.ylabel('距离')
plt.title('层次聚类树状图(Ward linkage)')
plt.grid(axis='y')
plt.show()

# 3. 根据距离阈值切割
threshold = 5  # 通过树状图观察选择合适的阈值
labels = fcluster(Z, t=threshold, criterion='distance')
print('聚类结果(阈值={}):'.format(threshold))
print('簇标签:', labels)
print('簇数:', len(np.unique(labels)))

# 4. 可视化聚类结果
plt.figure(figsize=(8,6))
scatter = plt.scatter(X[:,0], X[:,1], c=labels, cmap='tab10', s=80)
plt.xlabel('特征1')
plt.ylabel('特征2')
plt.title('层次聚类结果(阈值切割)')
plt.colorbar(scatter, label='簇标签')
plt.grid(True)
plt.show()

# 5. 尝试不同 linkage 方法
methods = ['ward', 'complete', 'average', 'single']
fig, axes = plt.subplots(2, 2, figsize=(12,10))
axes = axes.flatten()
for idx, method in enumerate(methods):
    Z_m = linkage(X_scaled, method=method)
    dendrogram(Z_m, truncate_mode='lastp', p=12, ax=axes[idx])
    axes[idx].set_title(f'Linkage: {method}')
    axes[idx].set_xlabel('样本')
    axes[idx].set_ylabel('距离')
    axes[idx].grid(axis='y')
plt.tight_layout()
plt.show()
参数说明示例
linkage簇间距离计算方式:ward, complete, average, single'ward'
metric样本间距离度量:euclidean, cityblock, cosine等'euclidean'
criterion切割准则:'distance', 'maxclust''distance'
threshold切割阈值(距离)或簇数5.0

竞赛技巧

  • Ward linkage适合欧氏距离,且倾向于产生大小相近的簇。
  • 树状图可直观展示聚类层次,帮助确定簇数。
  • 层次聚类可处理任意形状的簇,但计算复杂度高(O(n³))。
  • 可用于样本聚类,也可用于变量聚类(对特征降维)。

蒙特卡洛模拟

模型描述

通过大量随机抽样估计复杂系统的概率或数值解,适用于风险分析、积分计算等。

使用步骤

  1. 定义随机变量及其分布
  2. 生成大量随机样本
  3. 将样本输入模型计算输出
  4. 统计输出结果(均值、方差、分位数等)
  5. 分析精度(标准误差)
Python 代码(蒙特卡洛模拟)
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats as stats

# 示例1:估计圆周率π(投点法)
np.random.seed(42)
n_samples = 100000
# 在[0,1]×[0,1]正方形内均匀投点
x = np.random.rand(n_samples)
y = np.random.rand(n_samples)
# 计算落在单位圆内的点数
inside = (x**2 + y**2) <= 1
pi_estimate = 4 * np.sum(inside) / n_samples
print('蒙特卡洛估计 π =', pi_estimate)
print('相对误差:', abs(pi_estimate - np.pi) / np.pi * 100, '%')

# 可视化
plt.figure(figsize=(6,6))
plt.scatter(x[inside], y[inside], color='blue', s=1, alpha=0.5, label='圆内')
plt.scatter(x[~inside], y[~inside], color='red', s=1, alpha=0.5, label='圆外')
circle = plt.Circle((0,0), 1, color='black', fill=False, linewidth=2)
plt.gca().add_artist(circle)
plt.xlim(0,1)
plt.ylim(0,1)
plt.xlabel('x')
plt.ylabel('y')
plt.title('蒙特卡洛法估计π({}个样本)'.format(n_samples))
plt.legend(loc='upper right', markerscale=10)
plt.grid(True)
plt.show()

# 示例2:风险分析(项目利润模拟)
np.random.seed(42)
n_trials = 50000
# 假设利润 = 收入 - 成本,收入服从正态分布,成本服从均匀分布
mean_revenue = 100
std_revenue = 20
cost_low = 40
cost_high = 70

revenue = np.random.normal(mean_revenue, std_revenue, n_trials)
cost = np.random.uniform(cost_low, cost_high, n_trials)
profit = revenue - cost

# 统计结果
print('\n项目利润模拟({}次试验)'.format(n_trials))
print('平均利润:', np.mean(profit))
print('利润标准差:', np.std(profit))
print('利润小于0的概率(亏损概率):', np.sum(profit < 0) / n_trials)
print('5%分位数(VaR):', np.percentile(profit, 5))
print('95%分位数:', np.percentile(profit, 95))

# 绘制利润分布直方图
plt.figure(figsize=(10,4))
plt.subplot(1,2,1)
plt.hist(profit, bins=50, density=True, alpha=0.7, color='skyblue')
plt.axvline(np.mean(profit), color='red', linestyle='--', label='均值')
plt.xlabel('利润')
plt.ylabel('密度')
plt.title('利润分布直方图')
plt.legend()
plt.grid(True)

# 累积分布函数
plt.subplot(1,2,2)
sorted_profit = np.sort(profit)
cdf = np.arange(1, n_trials+1) / n_trials
plt.plot(sorted_profit, cdf, 'b-', linewidth=2)
plt.axhline(0.05, color='red', linestyle='--', label='5%分位')
plt.axvline(np.percentile(profit, 5), color='red', linestyle='--')
plt.xlabel('利润')
plt.ylabel('累积概率')
plt.title('利润累积分布函数(CDF)')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
参数说明示例
n_samples随机样本数,越多精度越高100000
随机分布根据问题选择:均匀、正态、指数等normal(100,20)
输出统计量均值、标准差、分位数、概率均值=60
标准误差估计精度,≈标准差/√n0.1

竞赛技巧

  • 若模型计算昂贵,可先进行少量试验估计方差,再确定所需样本数。
  • 可使用方差缩减技术(如对偶变量、控制变量)提高效率。
  • 蒙特卡洛模拟结果需给出置信区间(如95%置信区间)。
  • 适用于高维积分、期权定价、排队系统等。

支持向量机 (SVM)

模型描述

寻找最大间隔超平面分离两类样本,可通过核技巧处理非线性问题。

使用步骤

  1. 数据标准化
  2. 选择核函数(线性、多项式、RBF)
  3. 调整正则化参数C和核参数
  4. 训练SVM模型
  5. 评估分类性能
  6. 可视化决策边界(二维)
Python 代码(使用 sklearn)
import numpy as np
import matplotlib.pyplot as plt
from sklearn.svm import SVC
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score
from sklearn.datasets import make_moons, make_circles

# 生成非线性可分数据(月亮形)
X, y = make_moons(n_samples=300, noise=0.2, random_state=42)

# 划分训练集/测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

# 标准化
scaler = StandardScaler()
X_train_scaled = scaler.fit_transform(X_train)
X_test_scaled = scaler.transform(X_test)

# 1. 线性SVM(对比)
svm_linear = SVC(kernel='linear', C=1.0)
svm_linear.fit(X_train_scaled, y_train)
y_pred_linear = svm_linear.predict(X_test_scaled)
print('线性SVM准确率:', accuracy_score(y_test, y_pred_linear))

# 2. RBF核SVM(默认)
svm_rbf = SVC(kernel='rbf', C=1.0, gamma='scale')
svm_rbf.fit(X_train_scaled, y_train)
y_pred_rbf = svm_rbf.predict(X_test_scaled)
print('RBF核SVM准确率:', accuracy_score(y_test, y_pred_rbf))

# 3. 网格搜索调参
param_grid = {
    'C': [0.1, 1, 10, 100],
    'gamma': ['scale', 'auto', 0.1, 1, 10],
    'kernel': ['rbf', 'poly']
}
grid = GridSearchCV(SVC(), param_grid, cv=5, scoring='accuracy', n_jobs=-1)
grid.fit(X_train_scaled, y_train)
print('最佳参数:', grid.best_params_)
print('最佳交叉验证准确率:', grid.best_score_)

# 用最佳模型预测
best_svm = grid.best_estimator_
y_pred_best = best_svm.predict(X_test_scaled)
print('测试集准确率:', accuracy_score(y_test, y_pred_best))
print('\n分类报告:')
print(classification_report(y_test, y_pred_best))

# 可视化决策边界
def plot_decision_boundary(clf, X, y, title):
    h = 0.02  # 网格步长
    x_min, x_max = X[:,0].min()-0.5, X[:,0].max()+0.5
    y_min, y_max = X[:,1].min()-0.5, X[:,1].max()+0.5
    xx, yy = np.meshgrid(np.arange(x_min, x_max, h),
                         np.arange(y_min, y_max, h))
    Z = clf.predict(np.c_[xx.ravel(), yy.ravel()])
    Z = Z.reshape(xx.shape)
    
    plt.figure(figsize=(8,6))
    plt.contourf(xx, yy, Z, alpha=0.3, cmap='coolwarm')
    plt.scatter(X[:,0], X[:,1], c=y, edgecolors='k', cmap='coolwarm', s=50)
    plt.xlabel('特征1')
    plt.ylabel('特征2')
    plt.title(title)
    plt.grid(True)
    plt.show()

plot_decision_boundary(svm_linear, X_train_scaled, y_train, '线性SVM决策边界')
plot_decision_boundary(svm_rbf, X_train_scaled, y_train, 'RBF核SVM决策边界')
plot_decision_boundary(best_svm, X_train_scaled, y_train, '调参后SVM决策边界')
参数说明示例
C正则化参数,越小容忍错误越多,泛化越强1.0
kernel核函数:'linear', 'poly', 'rbf', 'sigmoid''rbf'
gammaRBF核参数,越大模型越复杂'scale'
degree多项式核的次数3
支持向量决定超平面的关键样本数量

竞赛技巧

  • SVM适合小样本、高维数据,但对缺失值和噪声敏感。
  • RBF核需调整C和gamma,可用网格搜索或随机搜索。
  • 对于多分类问题,SVM采用one‑vs‑one或one‑vs‑rest策略。
  • 可扩展为支持向量回归(SVR)用于回归问题。

图论模型

最短路径、最小生成树、最大流、旅行商问题、排队论等。

最短路径(Dijkstra算法)

模型描述

在带权有向图中,找到从源点到其他所有顶点的最短路径(边权非负)。

使用步骤

  1. 初始化距离数组,源点距离为0,其余为无穷大
  2. 选择未访问顶点中距离最小的顶点
  3. 更新其邻居顶点的距离(若通过当前顶点更短)
  4. 重复直到所有顶点访问完毕
  5. 回溯路径
Python 代码(Dijkstra算法)
import heapq

def dijkstra(graph, start):
    """
    使用优先队列实现Dijkstra算法
    :param graph: 邻接表,graph[u] = [(v, weight), ...]
    :param start: 源点
    :return: 最短距离字典,前驱节点字典
    """
    n = len(graph)
    dist = {i: float('inf') for i in range(n)}
    prev = {i: None for i in range(n)}
    dist[start] = 0
    # 优先队列(距离,顶点)
    pq = [(0, start)]
    
    while pq:
        current_dist, u = heapq.heappop(pq)
        if current_dist > dist[u]:
            continue
        for v, w in graph[u]:
            new_dist = dist[u] + w
            if new_dist < dist[v]:
                dist[v] = new_dist
                prev[v] = u
                heapq.heappush(pq, (new_dist, v))
    return dist, prev

def reconstruct_path(prev, target):
    """从prev字典重建路径"""
    path = []
    while target is not None:
        path.append(target)
        target = prev[target]
    return path[::-1]

# 示例图(有向图,顶点0‑5)
graph = {
    0: [(1, 4), (2, 2)],
    1: [(2, 1), (3, 5)],
    2: [(1, 1), (3, 8), (4, 10)],
    3: [(4, 2), (5, 6)],
    4: [(5, 3)],
    5: []
}

start = 0
dist, prev = dijkstra(graph, start)
print('从顶点 {} 出发的最短距离:'.format(start))
for v in range(len(graph)):
    print('到顶点 {}: 距离 = {},路径 = {}'.format(v, dist[v], reconstruct_path(prev, v)))

# 可视化(简单文本)
print('\n邻接表:')
for u in graph:
    print('{} -> {}'.format(u, graph[u]))
参数说明示例
graph邻接表,字典或列表{0:[(1,4),(2,2)]}
start源点0
dist最短距离字典{0:0,1:3,...}
prev前驱节点字典,用于重建路径{1:0,2:0,...}

竞赛技巧

  • 若边权为负,需使用Bellman‑Ford算法。
  • 若只需求两点间最短路径,可使用A*算法(有启发函数)。
  • 稠密图可用邻接矩阵,稀疏图用邻接表。
  • Floyd‑Warshall算法可求所有顶点对的最短路径(O(n³))。

最小生成树(Prim算法)

模型描述

在连通带权无向图中,找到连接所有顶点的树,使得总边权最小。

使用步骤

  1. 任选起始顶点,加入树集合
  2. 重复:选择连接树集合与非树集合的最小权边
  3. 将该边加入生成树,对应顶点加入树集合
  4. 直到所有顶点加入树集合
Python 代码(Prim算法)
import heapq

def prim(graph, start=0):
    """
    Prim算法求最小生成树(邻接表,无向图)
    :param graph: 邻接表,graph[u] = [(v, weight), ...]
    :param start: 起始顶点
    :return: 生成树边列表 [(u,v,weight), ...],总权重
    """
    n = len(graph)
    visited = [False] * n
    mst_edges = []
    total_weight = 0
    # 优先队列存储(权重, u, v),v尚未访问
    pq = []
    visited[start] = True
    for v, w in graph[start]:
        heapq.heappush(pq, (w, start, v))
    
    while pq and len(mst_edges) < n-1:
        weight, u, v = heapq.heappop(pq)
        if visited[v]:
            continue
        visited[v] = True
        mst_edges.append((u, v, weight))
        total_weight += weight
        # 将v的未访问邻居加入队列
        for neighbor, w in graph[v]:
            if not visited[neighbor]:
                heapq.heappush(pq, (w, v, neighbor))
    
    return mst_edges, total_weight

# 示例无向图(邻接表)
graph = {
    0: [(1, 2), (3, 6)],
    1: [(0, 2), (2, 3), (3, 8), (4, 5)],
    2: [(1, 3), (4, 7)],
    3: [(0, 6), (1, 8), (4, 9)],
    4: [(1, 5), (2, 7), (3, 9)]
}

edges, total = prim(graph, start=0)
print('最小生成树边:')
for u, v, w in edges:
    print('{} -- {} (权重 {})'.format(u, v, w))
print('总权重:', total)

# 也可使用Kruskal算法(并查集)
print('\nKruskal算法实现(略)')
参数说明示例
graph无向图邻接表{0:[(1,2),(3,6)]}
start起始顶点0
mst_edges生成树的边列表[(0,1,2),...]
total_weight最小生成树总权重16

竞赛技巧

  • Prim算法适合稠密图,Kruskal适合稀疏图。
  • 若图不连通,则生成森林(每个连通分量一棵树)。
  • 最小生成树可用于网络设计、聚类分析等。
  • 可扩展为次小生成树。

最大流(Ford‑Fulkerson)

模型描述

在网络流中,从源点到汇点的最大流量,满足容量约束和流量守恒。

使用步骤

  1. 构建残量图(初始为原图)
  2. 在残量图中寻找增广路径(如BFS‑Edmonds‑Karp)
  3. 计算路径上的最小剩余容量
  4. 更新残量图(正向边减流量,反向边加流量)
  5. 重复直到无增广路径
Python 代码(Edmonds‑Karp算法)
from collections import deque

def bfs(capacity, source, sink, parent):
    """BFS寻找增广路径"""
    n = len(capacity)
    visited = [False] * n
    queue = deque([source])
    visited[source] = True
    while queue:
        u = queue.popleft()
        for v in range(n):
            if not visited[v] and capacity[u][v] > 0:
                visited[v] = True
                parent[v] = u
                if v == sink:
                    return True
                queue.append(v)
    return False

def edmonds_karp(capacity, source, sink):
    """
    Edmonds‑Karp算法求最大流
    :param capacity: 容量矩阵,capacity[u][v] = 边(u→v)的容量
    :param source: 源点
    :param sink: 汇点
    :return: 最大流量,流矩阵
    """
    n = len(capacity)
    # 流矩阵,初始为0
    flow = [[0] * n for _ in range(n)]
    max_flow = 0
    parent = [-1] * n
    
    while bfs(capacity, source, sink, parent):
        # 计算增广路径上的最小剩余容量
        path_flow = float('inf')
        v = sink
        while v != source:
            u = parent[v]
            path_flow = min(path_flow, capacity[u][v])
            v = u
        # 更新残量图和流矩阵
        v = sink
        while v != source:
            u = parent[v]
            capacity[u][v] -= path_flow
            capacity[v][u] += path_flow  # 反向边
            flow[u][v] += path_flow
            v = u
        max_flow += path_flow
        parent = [-1] * n  # 重置parent
    return max_flow, flow

# 示例网络(容量矩阵)
# 顶点:0(source),1,2,3(sink)
capacity = [
    [0, 10, 5, 0],
    [0, 0, 15, 10],
    [0, 0, 0, 10],
    [0, 0, 0, 0]
]
source, sink = 0, 3
max_flow, flow_matrix = edmonds_karp([row[:] for row in capacity], source, sink)
print('最大流量:', max_flow)
print('流矩阵:')
for row in flow_matrix:
    print(row)

# 最小割:在残量图中从源点可达的顶点集合
print('\n最小割(源点侧):', [i for i in range(len(capacity)) if capacity[i][i]==0])  # 简化表示
参数说明示例
capacity容量矩阵,n×n见代码
source, sink源点、汇点0,3
flow matrix流矩阵,记录每条边的流量[[0,8,2,0],...]
最大流源点到汇点的最大流量15

竞赛技巧

  • Edmonds‑Karp算法复杂度O(VE²),Dinic算法更高效。
  • 最大流等于最小割容量(最大流最小割定理)。
  • 可扩展为多源多汇、带上下界的流。
  • 可用于匹配、分配、运输等问题。

旅行商问题(TSP)

模型描述

给定一系列城市和距离,求访问每个城市一次并返回起点的最短回路。

使用步骤

  1. 构建距离矩阵
  2. 选择算法(精确算法、启发式算法)
  3. 求解近似最优解
  4. 验证解的有效性
Python 代码(模拟退火求解TSP)
import numpy as np
import random
import math

def distance_matrix(coords):
    """根据坐标计算欧氏距离矩阵"""
    n = len(coords)
    dist = np.zeros((n, n))
    for i in range(n):
        for j in range(n):
            if i != j:
                dist[i][j] = np.linalg.norm(coords[i] - coords[j])
    return dist

def total_distance(path, dist):
    """计算路径总距离"""
    total = 0
    for i in range(len(path)-1):
        total += dist[path[i]][path[i+1]]
    total += dist[path[-1]][path[0]]  # 回到起点
    return total

def simulated_annealing_tsp(dist, start_temp=1000, end_temp=1, alpha=0.99, max_iter=5000):
    """
    模拟退火求解TSP
    :param dist: 距离矩阵
    :param start_temp: 初始温度
    :param end_temp: 终止温度
    :param alpha: 降温系数
    :param max_iter: 每个温度下的迭代次数
    :return: 最优路径,最优距离
    """
    n = len(dist)
    # 初始解(随机排列)
    current_path = list(range(n))
    random.shuffle(current_path)
    current_dist = total_distance(current_path, dist)
    best_path = current_path[:]
    best_dist = current_dist
    
    temp = start_temp
    while temp > end_temp:
        for _ in range(max_iter):
            # 生成邻域解:随机交换两个城市
            i, j = random.sample(range(n), 2)
            new_path = current_path[:]
            new_path[i], new_path[j] = new_path[j], new_path[i]
            new_dist = total_distance(new_path, dist)
            delta = new_dist - current_dist
            # 接受准则
            if delta < 0 or random.random() < math.exp(-delta / temp):
                current_path = new_path
                current_dist = new_dist
                if current_dist < best_dist:
                    best_path = current_path[:]
                    best_dist = current_dist
        temp *= alpha  # 降温
    return best_path, best_dist

# 生成随机城市坐标
np.random.seed(42)
n_cities = 15
coords = np.random.rand(n_cities, 2) * 100
dist = distance_matrix(coords)

# 求解
best_path, best_dist = simulated_annealing_tsp(dist, start_temp=1000, end_temp=1, alpha=0.995, max_iter=1000)
print('最优路径(城市索引):', best_path)
print('最优距离:', best_dist)

# 可视化
import matplotlib.pyplot as plt
plt.figure(figsize=(8,6))
plt.scatter(coords[:,0], coords[:,1], c='red', s=100)
for i, (x, y) in enumerate(coords):
    plt.text(x+1, y+1, str(i), fontsize=10)
# 绘制路径
tour = coords[best_path + [best_path[0]]]
plt.plot(tour[:,0], tour[:,1], 'b-', linewidth=1, alpha=0.7)
plt.xlabel('X')
plt.ylabel('Y')
plt.title('旅行商问题近似解(模拟退火)')
plt.grid(True)
plt.show()
参数说明示例
dist距离矩阵,n×n欧氏距离
start_temp初始温度,影响接受劣解的概率1000
alpha降温系数,每次温度乘以alpha0.995
max_iter每个温度的迭代次数1000
邻域操作交换、逆转、插入等交换两城市

竞赛技巧

  • TSP是NP‑hard问题,城市多时需用启发式算法(遗传算法、蚁群算法等)。
  • 可引入约束(如时间窗口)变为VRPTW(车辆路径问题)。
  • 对称TSP距离矩阵对称,非对称TSP需用有向图。
  • 可使用LKH或Concorde等专用求解器求精确解(城市数≤50)。

排队论(M/M/1模型)

模型描述

描述单服务台、到达间隔和服务时间均服从指数分布的排队系统。

使用步骤

  1. 确定到达率λ和服务率μ
  2. 计算系统利用率ρ=λ/μ(需ρ<1)
  3. 计算各项性能指标(平均队长、平均等待时间等)
  4. 分析系统稳定性
Python 代码(M/M/1排队模型)
import numpy as np
import matplotlib.pyplot as plt

def mm1_queue(lambd, mu, n=1000):
    """
    M/M/1排队模型理论计算
    :param lambd: 到达率(单位时间平均到达数)
    :param mu: 服务率(单位时间平均服务数)
    :param n: 模拟顾客数(用于仿真)
    :return: 理论指标字典,仿真结果字典
    """
    # 理论计算
    rho = lambd / mu
    if rho >= 1:
        print('警告:系统不稳定(ρ ≥ 1)')
        return None, None
    L = rho / (1 - rho)  # 平均队长(系统中顾客数)
    Lq = rho**2 / (1 - rho)  # 平均排队长(等待服务顾客数)
    W = 1 / (mu - lambd)  # 平均逗留时间
    Wq = rho / (mu - lambd)  # 平均等待时间
    P0 = 1 - rho  # 系统空闲概率
    
    theory = {
        'ρ': rho, 'L': L, 'Lq': Lq, 'W': W, 'Wq': Wq, 'P0': P0,
        'Pn公式': f'P_n = (1-ρ)ρ^n'
    }
    
    # 离散事件仿真(简单版本)
    np.random.seed(42)
    inter_arrival = np.random.exponential(1/lambd, n)  # 到达间隔
    service_time = np.random.exponential(1/mu, n)      # 服务时间
    
    arrival_time = np.cumsum(inter_arrival)
    departure_time = np.zeros(n)
    departure_time[0] = arrival_time[0] + service_time[0]
    for i in range(1, n):
        departure_time[i] = max(arrival_time[i], departure_time[i-1]) + service_time[i]
    
    wait_time = departure_time - arrival_time - service_time
    system_time = departure_time - arrival_time
    queue_length = np.zeros(n)
    for i in range(n):
        # 计算时刻t的队长(粗略)
        t = arrival_time[i]
        queue_length[i] = np.sum((arrival_time <= t) & (departure_time > t)) - 1  # 减去正在服务的
    
    simulation = {
        '平均队长(仿真)': np.mean(queue_length),
        '平均等待时间(仿真)': np.mean(wait_time),
        '平均逗留时间(仿真)': np.mean(system_time)
    }
    return theory, simulation

# 示例:λ=4人/小时,μ=5人/小时
lambd = 4
mu = 5
theory, sim = mm1_queue(lambd, mu, n=5000)
print('M/M/1排队模型理论指标:')
for key, val in theory.items():
    print(f'{key}: {val}')
print('\n仿真结果({}个顾客):'.format(5000))
for key, val in sim.items():
    print(f'{key}: {val:.3f}')

# 绘制队长概率分布
rho = lambd / mu
max_n = 10
probs = [(1-rho) * rho**n for n in range(max_n+1)]
plt.figure(figsize=(8,5))
plt.bar(range(max_n+1), probs, alpha=0.7)
plt.xlabel('系统中顾客数 n')
plt.ylabel('概率 P_n')
plt.title('M/M/1系统队长概率分布(ρ={:.2f})'.format(rho))
plt.grid(axis='y')
plt.show()
参数说明示例
λ (lambd)到达率,单位时间平均到达顾客数4
μ (mu)服务率,单位时间平均服务顾客数5
ρ (rho)系统利用率,ρ=λ/μ,必须<10.8
L, Lq平均队长、平均排队长4,3.2
W, Wq平均逗留时间、平均等待时间1,0.8

竞赛技巧

  • M/M/c模型(多服务台)公式类似,但更复杂。
  • 若到达或服务非指数分布,可用G/G/1近似或仿真。
  • 排队论可用于服务设施设计、呼叫中心、生产线优化。
  • 仿真时需忽略初始瞬态(warm‑up period)以保证稳态。

智能算法模型

遗传算法、模拟退火、粒子群算法、元胞自动机等。

遗传算法 (Genetic Algorithm)

模型描述

模拟自然选择过程,通过选择、交叉、变异等操作迭代优化种群。

使用步骤

  1. 编码(二进制、实数、排列)
  2. 初始化种群
  3. 计算适应度
  4. 选择(轮盘赌、锦标赛)
  5. 交叉(单点、多点、均匀)
  6. 变异(位翻转、高斯扰动)
  7. 重复直到满足终止条件
Python 代码(遗传算法求函数极值)
import numpy as np
import matplotlib.pyplot as plt

def fitness(x):
    """目标函数(求最大值)"""
    return np.sin(x) + np.sin(2*x) + np.cos(3*x)  # 在[0, 2π]内

def genetic_algorithm(pop_size=50, n_generations=100, crossover_rate=0.8, mutation_rate=0.1):
    # 参数
    x_min, x_max = 0, 2*np.pi
    chromosome_length = 20  # 二进制编码长度
    
    # 初始化种群(二进制串)
    population = np.random.randint(2, size=(pop_size, chromosome_length))
    
    best_fitness_history = []
    avg_fitness_history = []
    
    for gen in range(n_generations):
        # 解码
        decoded = population.dot(2**np.arange(chromosome_length)[::-1]) / (2**chromosome_length - 1) * (x_max - x_min) + x_min
        # 计算适应度
        fitness_values = fitness(decoded)
        best_fitness = np.max(fitness_values)
        avg_fitness = np.mean(fitness_values)
        best_fitness_history.append(best_fitness)
        avg_fitness_history.append(avg_fitness)
        
        # 选择(轮盘赌)
        probs = fitness_values / np.sum(fitness_values)
        selected_indices = np.random.choice(pop_size, size=pop_size, p=probs)
        selected_population = population[selected_indices]
        
        # 交叉(单点交叉)
        crossover_mask = np.random.rand(pop_size) < crossover_rate
        crossover_points = np.random.randint(1, chromosome_length, size=np.sum(crossover_mask))
        new_population = selected_population.copy()
        idx = 0
        for i in range(pop_size):
            if crossover_mask[i]:
                partner = np.random.randint(pop_size)
                point = crossover_points[idx]
                new_population[i, point:] = selected_population[partner, point:]
                idx += 1
        
        # 变异(位翻转)
        mutation_mask = np.random.rand(pop_size, chromosome_length) < mutation_rate
        new_population = np.where(mutation_mask, 1 - new_population, new_population)
        
        population = new_population
    
    # 最后一代的最佳个体
    decoded = population.dot(2**np.arange(chromosome_length)[::-1]) / (2**chromosome_length - 1) * (x_max - x_min) + x_min
    fitness_values = fitness(decoded)
    best_idx = np.argmax(fitness_values)
    best_x = decoded[best_idx]
    best_f = fitness_values[best_idx]
    
    return best_x, best_f, best_fitness_history, avg_fitness_history

best_x, best_f, best_hist, avg_hist = genetic_algorithm(pop_size=50, n_generations=100)
print('最优解 x =', best_x)
print('最优值 f =', best_f)

# 绘制适应度进化曲线
plt.figure(figsize=(10,4))
plt.subplot(1,2,1)
x_plot = np.linspace(0, 2*np.pi, 400)
y_plot = fitness(x_plot)
plt.plot(x_plot, y_plot, 'b-', label='目标函数')
plt.axvline(best_x, color='red', linestyle='--', label='GA最优解')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('目标函数与GA解')
plt.legend()
plt.grid(True)

plt.subplot(1,2,2)
plt.plot(best_hist, 'r-', label='最佳适应度')
plt.plot(avg_hist, 'g-', label='平均适应度')
plt.xlabel('迭代次数')
plt.ylabel('适应度')
plt.title('遗传算法进化过程')
plt.legend()
plt.grid(True)
plt.tight_layout()
plt.show()
参数说明示例
pop_size种群大小50
n_generations迭代代数100
crossover_rate交叉概率0.8
mutation_rate变异概率0.1
编码方式二进制、实数、排列等二进制

竞赛技巧

  • 编码方式影响搜索效率,实数编码适合连续优化。
  • 适应度函数设计需保证非负,且与目标函数单调相关。
  • 可引入精英保留策略(elitism)防止最优解丢失。
  • 遗传算法适合复杂非线性、多峰函数优化。

模拟退火 (Simulated Annealing)

模型描述

模拟固体退火过程,以一定概率接受劣解,从而跳出局部最优。

使用步骤

  1. 初始化当前解和温度
  2. 生成邻域解
  3. 计算目标函数差ΔE
  4. 若ΔE<0则接受,否则以概率exp(-ΔE/T)接受
  5. 降低温度
  6. 重复直到温度低于阈值
Python 代码(模拟退火求函数极值)
import numpy as np
import math
import matplotlib.pyplot as plt

def target_function(x):
    """多峰函数,求最小值"""
    return np.sin(x) + 0.5 * np.sin(3*x) + 0.2 * np.cos(5*x)

def simulated_annealing(initial_temp=100, final_temp=0.1, alpha=0.95, max_iter=1000):
    x_min, x_max = -10, 10
    # 初始解
    current_x = np.random.uniform(x_min, x_max)
    current_value = target_function(current_x)
    best_x = current_x
    best_value = current_value
    
    temp = initial_temp
    history = []
    
    while temp > final_temp:
        for _ in range(max_iter):
            # 生成邻域解(随机扰动)
            neighbor_x = current_x + np.random.normal(0, 1)  # 高斯扰动
            # 边界处理
            neighbor_x = np.clip(neighbor_x, x_min, x_max)
            neighbor_value = target_function(neighbor_x)
            
            delta = neighbor_value - current_value
            # 接受准则
            if delta < 0 or np.random.rand() < math.exp(-delta / temp):
                current_x = neighbor_x
                current_value = neighbor_value
                if current_value < best_value:
                    best_x = current_x
                    best_value = current_value
            history.append((current_x, current_value, temp))
        temp *= alpha  # 降温
    
    return best_x, best_value, history

best_x, best_value, history = simulated_annealing(initial_temp=100, final_temp=0.1, alpha=0.95, max_iter=100)
print('最优解 x =', best_x)
print('最优值 f =', best_value)

# 可视化
x_plot = np.linspace(-10, 10, 1000)
y_plot = target_function(x_plot)
plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(x_plot, y_plot, 'b-', label='目标函数')
plt.axvline(best_x, color='red', linestyle='--', label='SA最优解')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('模拟退火求解多峰函数')
plt.legend()
plt.grid(True)

plt.subplot(1,2,2)
history_x = [h[0] for h in history]
history_f = [h[1] for h in history]
history_t = [h[2] for h in history]
plt.scatter(history_x, history_f, c=history_t, cmap='coolwarm', s=5, alpha=0.5)
plt.colorbar(label='温度')
plt.xlabel('x')
plt.ylabel('f(x)')
plt.title('搜索轨迹(颜色表示温度)')
plt.grid(True)
plt.tight_layout()
plt.show()
参数说明示例
initial_temp初始温度,影响接受劣解的概率100
final_temp终止温度0.1
alpha降温系数0.95
max_iter每个温度的迭代次数100
邻域生成随机扰动方式高斯扰动

竞赛技巧

  • 降温速度不宜过快(alpha接近1),否则易陷入局部最优。
  • 可采用自适应降温策略(根据接受率调整)。
  • 模拟退火适合组合优化(如TSP、调度)。
  • 可与局部搜索结合(如两阶段:SA粗搜,梯度下降细搜)。

粒子群优化 (Particle Swarm Optimization)

模型描述

模拟鸟群觅食行为,粒子通过跟踪个体最优和群体最优更新位置。

使用步骤

  1. 初始化粒子群(位置、速度)
  2. 计算每个粒子的适应度
  3. 更新个体最优和全局最优
  4. 更新粒子速度和位置
  5. 重复直到满足终止条件
Python 代码(PSO求函数极值)
import numpy as np
import matplotlib.pyplot as plt

def sphere_function(x):
    """Sphere函数,最小值在原点"""
    return np.sum(x**2)

def pso(n_particles=30, n_dimensions=2, max_iter=100, w=0.7, c1=1.5, c2=1.5):
    # 搜索范围
    x_min, x_max = -5, 5
    v_max = 0.2 * (x_max - x_min)
    
    # 初始化粒子位置和速度
    positions = np.random.uniform(x_min, x_max, (n_particles, n_dimensions))
    velocities = np.random.uniform(-v_max, v_max, (n_particles, n_dimensions))
    
    # 个体最优位置和适应度
    pbest_positions = positions.copy()
    pbest_values = np.array([sphere_function(p) for p in positions])
    
    # 全局最优
    gbest_idx = np.argmin(pbest_values)
    gbest_position = pbest_positions[gbest_idx].copy()
    gbest_value = pbest_values[gbest_idx]
    
    # 记录历史
    gbest_history = [gbest_value]
    positions_history = [positions.copy()]
    
    for iter in range(max_iter):
        for i in range(n_particles):
            # 更新速度
            r1, r2 = np.random.rand(n_dimensions), np.random.rand(n_dimensions)
            velocities[i] = w * velocities[i] + \
                           c1 * r1 * (pbest_positions[i] - positions[i]) + \
                           c2 * r2 * (gbest_position - positions[i])
            # 速度边界
            velocities[i] = np.clip(velocities[i], -v_max, v_max)
            # 更新位置
            positions[i] += velocities[i]
            positions[i] = np.clip(positions[i], x_min, x_max)
            
            # 计算适应度
            fitness = sphere_function(positions[i])
            # 更新个体最优
            if fitness < pbest_values[i]:
                pbest_positions[i] = positions[i].copy()
                pbest_values[i] = fitness
                # 更新全局最优
                if fitness < gbest_value:
                    gbest_position = positions[i].copy()
                    gbest_value = fitness
        
        gbest_history.append(gbest_value)
        positions_history.append(positions.copy())
    
    return gbest_position, gbest_value, gbest_history, positions_history

gbest_pos, gbest_val, gbest_hist, pos_hist = pso(n_particles=30, n_dimensions=2, max_iter=100)
print('全局最优位置:', gbest_pos)
print('全局最优值:', gbest_val)

# 可视化
plt.figure(figsize=(12,4))
plt.subplot(1,2,1)
plt.plot(gbest_hist, 'r-', linewidth=2)
plt.xlabel('迭代次数')
plt.ylabel('全局最优适应度')
plt.title('PSO收敛曲线')
plt.grid(True)

# 绘制粒子运动轨迹(二维时)
if len(gbest_pos) == 2:
    plt.subplot(1,2,2)
    # 绘制等高线
    x = np.linspace(-5, 5, 100)
    y = np.linspace(-5, 5, 100)
    X, Y = np.meshgrid(x, y)
    Z = X**2 + Y**2
    plt.contour(X, Y, Z, levels=20, cmap='gray')
    # 绘制粒子轨迹(取前5个粒子)
    for i in range(min(5, len(pos_hist[0]))):
        traj_x = [pos[i][0] for pos in pos_hist]
        traj_y = [pos[i][1] for pos in pos_hist]
        plt.plot(traj_x, traj_y, '.-', alpha=0.5, markersize=3)
    plt.scatter(gbest_pos[0], gbest_pos[1], c='red', s=100, marker='*', label='全局最优')
    plt.xlabel('x1')
    plt.ylabel('x2')
    plt.title('粒子运动轨迹(二维)')
    plt.legend()
    plt.grid(True)
plt.tight_layout()
plt.show()
参数说明示例
n_particles粒子数30
n_dimensions问题维度2
w惯性权重,平衡全局与局部搜索0.7
c1, c2加速常数,分别控制个体和群体认知1.5,1.5
v_max最大速度,防止粒子飞出搜索空间2.0

竞赛技巧

  • 惯性权重w可随时间线性减小(如从0.9到0.4),增强后期局部搜索。
  • 粒子群算法适合连续优化,对初值不敏感。
  • 可引入收缩因子(constriction factor)保证收敛。
  • 离散问题需设计位置和速度的离散表示。

元胞自动机 (Cellular Automaton)

模型描述

由规则网格上的元胞组成,每个元胞根据邻居状态和规则更新,模拟复杂系统演化。

使用步骤

  1. 定义网格大小和元胞状态(如0/1)
  2. 定义邻居类型(如Von Neumann, Moore)
  3. 定义状态转移规则
  4. 初始化网格
  5. 迭代更新并可视化
Python 代码(生命游戏 Game of Life)
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation

def game_of_life(grid):
    """应用生命游戏规则更新网格"""
    new_grid = grid.copy()
    rows, cols = grid.shape
    for i in range(rows):
        for j in range(cols):
            # 计算 Moore 邻居数(8邻域)
            total = np.sum(grid[i-1:i+2, j-1:j+2]) - grid[i, j]
            # 规则
            if grid[i, j] == 1:
                if total < 2 or total > 3:
                    new_grid[i, j] = 0  # 死亡
            else:
                if total == 3:
                    new_grid[i, j] = 1  # 繁殖
    return new_grid

# 初始化网格(随机)
size = 50
grid = np.random.choice([0, 1], size=(size, size), p=[0.7, 0.3])

# 模拟多步并记录
steps = 100
history = [grid.copy()]
for _ in range(steps):
    grid = game_of_life(grid)
    history.append(grid.copy())

# 绘制演化过程
fig, axes = plt.subplots(2, 3, figsize=(12,8))
axes = axes.flatten()
for idx, step in enumerate([0, 1, 10, 30, 60, 99]):
    ax = axes[idx]
    ax.imshow(history[step], cmap='binary', interpolation='nearest')
    ax.set_title('Step {}'.format(step))
    ax.set_xticks([])
    ax.set_yticks([])
plt.suptitle('生命游戏演化(随机初始)', fontsize=16)
plt.tight_layout()
plt.show()

# 动画(可选)
def update(frame):
    plt.clf()
    plt.imshow(history[frame], cmap='binary', interpolation='nearest')
    plt.title('Step {}'.format(frame))
    plt.axis('off')

# 如需保存动画,取消注释以下代码
# ani = FuncAnimation(plt.figure(), update, frames=len(history), interval=200, repeat=False)
# plt.show()

# 计算统计量
live_cells = [np.sum(h) for h in history]
plt.figure(figsize=(8,4))
plt.plot(live_cells, 'b-')
plt.xlabel('时间步')
plt.ylabel('活细胞数')
plt.title('活细胞数量变化')
plt.grid(True)
plt.show()

print('初始活细胞数:', live_cells[0])
print('最终活细胞数:', live_cells[-1])
print('最大活细胞数:', np.max(live_cells))
参数说明示例
网格大小rows × cols50×50
邻居类型Moore(8邻域)、Von Neumann(4邻域)Moore
状态通常为0(死)和1(活)0/1
规则生命游戏:B3/S23(出生3/存活2或3)自定义

竞赛技巧

  • 元胞自动机可用于模拟交通流、森林火灾、传染病传播等。
  • 边界条件常用周期边界(环形)或固定边界。
  • 规则设计是核心,可基于经验或数据拟合。
  • 可扩展为多状态、非均匀规则、随机元胞自动机。

竞赛技巧汇总

时间分配、论文写作、代码调试、常见坑点、加分项等。