方程求解
多项式求根
roots 函数
使用 roots 函数求解多项式方程的根。多项式由系数向量表示,从最高次到常数项:
# 多项式:p(x) = a_n*x^n + ... + a_1*x + a_0
# 系数向量:[a_n, ..., a_1, a_0]
r = roots(coeffs)
一次方程
# 求解:2x + 3 = 0
coeffs = [2, 3]
r = roots(coeffs)
disp("解:x = " + num2str(r)) # -1.5
二次方程
# 求解:x^2 - 5x + 6 = 0
# (x-2)(x-3) = 0,根为 2 和 3
coeffs = [1, -5, 6]
r = roots(coeffs)
disp("根:")
disp(r) # [3; 2]
# 求解:x^2 + 2x + 5 = 0(有复数根)
coeffs = [1, 2, 5]
r = roots(coeffs)
disp("复数根:")
disp(r) # [-1+2i; -1-2i]
高次方程
# 求解:x^3 - 6x^2 + 11x - 6 = 0
# (x-1)(x-2)(x-3) = 0
coeffs = [1, -6, 11, -6]
r = roots(coeffs)
disp("三次方程的根:")
disp(r) # [3; 2; 1]
# 求解:x^4 - 1 = 0
coeffs = [1, 0, 0, 0, -1]
r = roots(coeffs)
disp("四次方程的根:")
disp(r) # [1; -1; i; -i]
从根构造多项式
# poly 函数:从根生成多项式系数
roots_vec = [1, 2, 3]
coeffs = poly(roots_vec)
disp("多项式系数:")
disp(coeffs) # [1 -6 11 -6]
# 验证
r = roots(coeffs)
disp("验证根:")
disp(r)
线性方程组
使用左除运算符 \
求解线性方程组 Ax = b,使用矩阵左除 \:
# Ax = b
x = A \ b
基本示例
# 求解方程组:
# 2x + y = 5
# x - y = 1
# 即:x = 2, y = 1
A = [2 1; 1 -1]
b = [5; 1]
x = A \ b
disp("解:")
disp(x) # [2; 1]
# 验证
result = A * x
disp("验证 Ax = b:")
disp(result) # [5; 1]
三元方程组
# 求解:
# x + 2y + z = 6
# 2x + y - z = 1
# x - y + 2z = 5
A = [1 2 1; 2 1 -1; 1 -1 2]
b = [6; 1; 5]
x = A \ b
disp("解向量:")
disp(x)
# 提取各个变量
x1 = x(1)
x2 = x(2)
x3 = x(3)
disp("x = " + num2str(x1))
disp("y = " + num2str(x2))
disp("z = " + num2str(x3))
欠定和超定系统
# 超定系统(方程多于未知数)- 最小二乘解
A = [1 1; 1 2; 1 3] # 3个方程,2个未知数
b = [2; 3; 4]
x = A \ b # 自动计算最小二乘解
disp("最小二乘解:")
disp(x)
# 计算残差
residual = b - A * x
disp("残差:")
disp(residual)
齐次方程组
# 求解 Ax = 0 的非零解
A = [1 2 3; 4 5 6; 7 8 9] # 秩不满
# 检查秩
r = rank(A)
disp("矩阵的秩:" + num2str(r))
# 对于齐次方程,可以通过求零空间获得解
# 使用 SVD 分解等方法(如果支持)
非线性方程求解
使用 solve 函数
TianYuan 提供 solve 函数求解非线性方程:
# solve(f, x0, tolerance, maxIter)
# f: 函数
# x0: 初始猜测
# tolerance: 容差
# maxIter: 最大迭代次数
root = solve(@myFunc, x0)
示例:求平方根
# 定义方程 f(x) = x^2 - 2 = 0
function y = f(x)
y = x^2 - 2
end
# 求解(从初始猜测 1.0 开始)
root = solve(@f, 1.0)
disp("sqrt(2) ≈ " + num2str(root)) # 1.414...
示例:超越方程
# 求解:x = cos(x)
function y = f(x)
y = x - cos(x)
end
root = solve(@f, 0.5)
disp("x = cos(x) 的解:" + num2str(root)) # 0.739...
手动实现牛顿法
# 牛顿法求根:x_{n+1} = x_n - f(x_n)/f'(x_n)
function root = newtonMethod(f, df, x0, tol, maxIter)
x = x0
for iter = 1:maxIter
fx = f(x)
if abs(fx) < tol
root = x
return
end
dfx = df(x)
x = x - fx / dfx
end
root = x
disp("警告:达到最大迭代次数")
end
# 示例:求 sqrt(2)
function y = f(x)
y = x^2 - 2
end
function y = df(x)
y = 2*x
end
root = newtonMethod(@f, @df, 1.0, 1e-10, 100)
disp("sqrt(2) = " + num2str(root))
常微分方程(ODE)
ode45 函数
使用 Dormand-Prince RK45 自适应步长方法求解常微分方程初值问题:
# ode45(f, tspan, y0)
# f: 函数句柄 @f,其中 f(t, y) 返回 dydt
# tspan: 时间区间 [t0, tf]
# y0: 初始条件(标量或列向量)
# 返回: t 为列向量,y 为矩阵(每行是一个时刻的状态)
[t, y] = ode45(@odefun, tspan, y0)
说明:默认相对容差 1e-3,绝对容差 1e-6。对于刚性方程组可能出现步长过小的报错, 此类方程建议手动实现隐式方法(如 Backward Euler)。
关键概念:返回值结构
ode45 返回两个输出:t 是列向量(各时刻),y 是矩阵(每行对应一个时刻,每列对应一个状态分量):
# 一阶 ODE — 标量状态
function dydt = decay(t, y)
dydt = -2 * y # dy/dt = -2y
end
[t, y] = ode45(@decay, [0, 3], 1)
# t: 列向量,形如 [0; 0.05; 0.12; ...]
# y: 同长度的列向量,y(k,1) 是 t(k) 时刻的状态值
disp("步数 = " + num2str(length(t))) # 自适应步数(约数十步)
disp("y(0) = " + num2str(y(1, 1))) # 1.0(初始值)
disp("y(3) ≈ " + num2str(y(length(t), 1))) # ≈ 0.00248(e^{-6})
关键概念:向量状态(方程组)
对于 n 维方程组,y0 是 n×1 列向量,ODE 函数返回同形状的列向量,y 结果矩阵有 n 列:
# 二阶 ODE 转为一阶方程组:d²x/dt² + x = 0
# 状态 y = [x; x'],方程组 y' = [y(2); -y(1)]
function dydt = harmonic(t, y)
dydt = [y(2); -y(1)] # 列向量返回
end
y0 = [1; 0] # 初始位移 1,初始速度 0
[t, y] = ode45(@harmonic, [0, 6.2832], y0)
# y 是 N×2 矩阵:
# 第 1 列 = 位移 x(t)
# 第 2 列 = 速度 x'(t)
x = y(:, 1) # 提取所有时刻的位移
v = y(:, 2) # 提取所有时刻的速度
plot(t, x)
xlabel('t'); ylabel('x(t)')
title('简谐振动(ω=1)')
示例:简单的一阶ODE
# 求解:dy/dt = -2y, y(0) = 1
# 解析解:y(t) = e^(-2t)
function dydt = odefun(t, y)
dydt = -2 * y
end
# 在 [0, 2] 区间求解
tspan = [0, 2]
y0 = 1
[t, y] = ode45(@odefun, tspan, y0)
# 绘制结果
plot(t, y)
xlabel('t')
ylabel('y')
title('dy/dt = -2y 的解')
grid on
# 与解析解比较
y_exact = exp(-2 * t)
hold on
plot(t, y_exact, '--')
legend('数值解', '解析解')
hold off
示例:指数增长模型
# 人口增长模型:dP/dt = rP
# P(0) = P0, r = 0.1
function dPdt = population(t, P)
r = 0.1
dPdt = r * P
end
tspan = [0, 50]
P0 = 100
[t, P] = ode45(@population, tspan, P0)
plot(t, P)
xlabel('时间')
ylabel('人口')
title('指数增长模型')
grid on
示例:简谐振动
# 二阶ODE:d²x/dt² + ω²x = 0
# 转换为一阶系统:
# dx/dt = v
# dv/dt = -ω²x
function dydt = harmonic(t, y)
omega = 2 * pi # 角频率
x = y(1)
v = y(2)
dxdt = v
dvdt = -omega^2 * x
dydt = [dxdt; dvdt]
end
# 初始条件:x(0) = 1, v(0) = 0
tspan = [0, 2]
y0 = [1; 0]
[t, y] = ode45(@harmonic, tspan, y0)
# 提取位置和速度
x = y(:, 1)
v = y(:, 2)
# 绘制位置
plot(t, x)
xlabel('时间')
ylabel('位移')
title('简谐振动')
grid on
示例:洛伦兹吸引子
# 洛伦兹方程(混沌系统):
# dx/dt = σ(y - x)
# dy/dt = x(ρ - z) - y
# dz/dt = xy - βz
function dydt = lorenz(t, y)
sigma = 10
rho = 28
beta = 8/3
x = y(1)
y_val = y(2)
z = y(3)
dxdt = sigma * (y_val - x)
dydt_val = x * (rho - z) - y_val
dzdt = x * y_val - beta * z
dydt = [dxdt; dydt_val; dzdt]
end
# 初始条件
tspan = [0, 50]
y0 = [1; 1; 1]
[t, y] = ode45(@lorenz, tspan, y0)
# 绘制三维轨迹
plot3(y(:,1), y(:,2), y(:,3))
xlabel('X')
ylabel('Y')
zlabel('Z')
title('洛伦兹吸引子')
grid on
优化问题
黄金分割搜索(一维优化)
function xmin = goldenSearch(f, a, b, tol)
# 黄金分割法找函数最小值
phi = (1 + sqrt(5)) / 2 # 黄金比例
while abs(b - a) > tol
x1 = b - (b - a) / phi
x2 = a + (b - a) / phi
if f(x1) < f(x2)
b = x2
else
a = x1
end
end
xmin = (a + b) / 2
end
# 示例:找 f(x) = (x-2)^2 的最小值
function y = parabola(x)
y = (x - 2)^2
end
xmin = goldenSearch(@parabola, 0, 5, 1e-6)
disp("最小值点:x = " + num2str(xmin)) # 2
disp("最小值:f(x) = " + num2str(parabola(xmin))) # 0
梯度下降(多维优化)
function xmin = gradientDescent(f, grad, x0, alpha, tol, maxIter)
# 梯度下降法
# f: 目标函数
# grad: 梯度函数
# x0: 初始点
# alpha: 学习率
# tol: 容差
# maxIter: 最大迭代次数
x = x0
for iter = 1:maxIter
g = grad(x)
if norm(g) < tol
xmin = x
return
end
x = x - alpha * g
end
xmin = x
end
# 示例:最小化 f(x,y) = x^2 + y^2
function z = f(x)
z = x(1)^2 + x(2)^2
end
function g = grad(x)
g = [2*x(1); 2*x(2)]
end
x0 = [5; 5]
xmin = gradientDescent(@f, @grad, x0, 0.1, 1e-6, 1000)
disp("最小值点:")
disp(xmin) # [0; 0]
综合示例
示例1:求方程组的交点
# 求两个圆的交点
# 圆1: x^2 + y^2 = 1
# 圆2: (x-1)^2 + y^2 = 1
# 从第二个方程减去第一个方程:
# -2x + 1 = 0 => x = 0.5
# 代入第一个方程:0.25 + y^2 = 1 => y = ±sqrt(0.75)
x = 0.5
y = sqrt(0.75)
disp("交点1:(" + num2str(x) + ", " + num2str(y) + ")")
disp("交点2:(" + num2str(x) + ", " + num2str(-y) + ")")
示例2:弹道轨迹
# 在重力作用下的抛体运动
# d²x/dt² = 0
# d²y/dt² = -g
function dydt = projectile(t, y)
g = 9.8 # 重力加速度
# y = [x, vx, y_pos, vy]
x = y(1)
vx = y(2)
y_pos = y(3)
vy = y(4)
dxdt = vx
dvxdt = 0
dydt_pos = vy
dvydt = -g
dydt = [dxdt; dvxdt; dydt_pos; dvydt]
end
# 初始条件:v0 = 20 m/s, θ = 45°
v0 = 20
theta = 45 * pi / 180
vx0 = v0 * cos(theta)
vy0 = v0 * sin(theta)
tspan = [0, 3]
y0 = [0; vx0; 0; vy0]
[t, y] = ode45(@projectile, tspan, y0)
# 提取轨迹
x = y(:, 1)
y_pos = y(:, 3)
# 只绘制在地面以上的部分
idx = y_pos >= 0
plot(x(idx), y_pos(idx))
xlabel('水平距离 (m)')
ylabel('高度 (m)')
title('抛体运动轨迹')
grid on
示例3:化学反应动力学
# 一级反应:A -> B
# d[A]/dt = -k[A]
# d[B]/dt = k[A]
function dcdt = reaction(t, c)
k = 0.5 # 反应速率常数
A = c(1)
B = c(2)
dAdt = -k * A
dBdt = k * A
dcdt = [dAdt; dBdt]
end
# 初始浓度:[A]0 = 1, [B]0 = 0
tspan = [0, 10]
c0 = [1; 0]
[t, c] = ode45(@reaction, tspan, c0)
# 绘制浓度随时间的变化
plot(t, c(:,1), 'b-', t, c(:,2), 'r-')
xlabel('时间')
ylabel('浓度')
legend('[A]', '[B]')
title('化学反应动力学')
grid on