<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>微分方程求解中的迭代思想</title>
<script>
MathJax = {
tex: {
packages: {'[+]': ['ams', 'bm', 'unicode', 'mhchem']},
inlineMath: [['$', '$'], ['\\(', '\\)']],
displayMath: [['$$', '$$'], ['\\[', '\\]']],
processEscapes: true
},
chtml: {
fontURL: 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/output/chtml/fonts/woff-v2'
}
};
</script>
<script src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml.js" id="MathJax-script" async></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@5.5.0/dist/echarts.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/mathjs/12.4.1/math.min.js"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Noto+Serif+SC:wght@400;700&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons+Outlined" rel="stylesheet">
<style>
body {
font-family: "Inter", "Noto Serif SC", serif, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
line-height: 1.8;
background-color: #f8f9fa; /* Lighter gray */
color: #343a40; /* Darker text */
padding: 15px;
box-sizing: border-box;
}
.container {
max-width: 1000px;
margin: 20px auto;
padding: 30px 40px;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 8px 25px rgba(0,0,0,0.1);
}
.material-icons-outlined {
vertical-align: middle;
font-size: 1.2em;
margin-right: 0.35em;
line-height: 1;
}
h1 {
font-size: 2.3rem;
color: #007bff; /* Primary blue */
border-bottom: 2px solid #007bff;
padding-bottom: 0.5em;
margin-bottom: 1em;
display: flex;
align-items: center;
font-family: "Noto Serif SC", serif; font-weight: 700;
}
h1 > .material-icons-outlined { font-size: 1.25em; margin-right: 0.4em; }
h2 {
font-size: 1.75rem;
color: #17a2b8; /* Info blue/teal */
border-bottom: 1px solid #dee2e6; /* Lighter border */
padding-bottom: 0.4em;
margin-top: 2.5em;
margin-bottom: 1.2em;
display: flex;
align-items: center;
font-family: "Noto Serif SC", serif; font-weight: 700;
}
h2 > .material-icons-outlined { font-size: 1.15em; margin-right: 0.4em; }
h3 {
font-size: 1.4rem;
color: #28a745; /* Success green */
margin-top: 2em;
margin-bottom: 1em;
font-family: "Inter", sans-serif; font-weight: 600;
}
h3 > .material-icons-outlined { font-size: 1.1em; margin-right: 0.4em; }
h4 {
font-size: 1.15rem;
color: #6c757d; /* Muted/secondary text */
margin-top: 1.8em;
margin-bottom: 0.8em;
font-weight: 600;
}
p { margin-bottom: 1.2em; color: #495057; font-size: 1.05rem; }
ul { padding-left: 25px; margin-bottom: 1.2em; }
li { margin-bottom: 0.6em; color: #495057; font-size: 1.05rem; }
strong, .strong-emphasis { font-weight: 600; color: #0056b3; } /* Darker blue for emphasis */
.text-highlight { background-color: #e9ecef; padding: 0.1em 0.3em; border-radius: 0.2rem; font-family: "Fira Code", monospace; color: #c82333;} /* For code/math terms */
.math-formula {
font-size: 1.1em;
padding: 15px 20px;
background-color: #e9f5ff; /* Very light blue */
border-radius: 6px;
text-align: left;
margin: 1em 0 1.5em 0;
overflow-x: auto;
border: 1px solid #bce0ff; /* Light blue border */
}
.interactive-demo {
margin-top: 2em;
padding: 20px;
background-color: #fdfdfe;
border: 1px solid #e0e5ec;
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
}
.interactive-demo h4 { margin-top: 0; color: #fd7e14; /* Orange for demo titles */ } /* Orange for demo titles */
.input-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 18px; margin-bottom: 1.2em; }
.input-group { display: flex; flex-direction: column; }
.input-group label { margin-bottom: .4em; font-weight: 500; color: #343a40; font-size:0.9rem; }
.input-group input[type="text"], .input-group input[type="number"], .input-group select {
padding: .55em .75em; border-radius: .25rem; border: 1px solid #ced4da; font-size: 0.95rem;
font-family:"Inter", sans-serif; transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.input-group input:focus, .input-group select:focus { border-color:#007bff; box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25); outline:none;}
button.action-button {
background-color: #28a745; color: white; padding: .6em 1.3em; border: none; border-radius: .25rem;
font-size: 1rem; cursor: pointer; transition: background-color 0.2s ease, transform 0.1s ease;
display: inline-block; margin-top: 0.5em; box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
button.action-button:hover { background-color: #218838; transform: translateY(-1px); }
.plot-container { width: 100%; height: 400px; margin-top: 1.5em; border:1px solid #dee2e6; border-radius:.25rem; padding:10px; box-sizing:border-box;}
.results-text { margin-top: 1em; font-family: "Fira Code", monospace; font-size: 0.9rem; white-space: pre-wrap; background-color: #f8f9fa; padding: 10px; border-radius: 4px; border: 1px solid #e9ecef; max-height: 200px; overflow-y: auto;}
.error-message { color: #dc3545; font-weight: bold; margin-top: 10px; }
.status-message { color: #007bff; font-weight: 500; margin: 0.5em 0; min-height: 1.3em; }
hr.section-divider { border: 0; height: 1px; background-image: linear-gradient(to right, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15), rgba(0, 0, 0, 0)); margin: 3em 0; }
</style>
</head>
<body>
<div class="container">
<h1><span class="material-icons-outlined">loop</span>微分方程求解中的迭代思想与方法</h1>
<p>在求解微分方程时,“迭代法”这一术语的含义根据我们是寻求连续解还是离散解而有所不同。下面将分别探讨这两种情况,并提供相应的演示。</p>
<hr class="section-divider">
<h2><span class="material-icons-outlined">waves</span>1. 针对连续解的迭代思想</h2>
<p>严格来说,直接求解微分方程(得到一个函数作为解)的方法通常不直接称为“迭代法”。但某些方法在概念上确实采用了迭代或逐步逼近的思想,直接作用于函数空间。</p>
<h3><span class="material-icons-outlined">functions</span>Picard 迭代法 (Picard Iteration)</h3>
<p>Picard 迭代法是常微分方程初值问题解的<strong class="strong-emphasis">存在唯一性证明</strong>(Picard-Lindelöf定理)中的核心。它从一个初始函数(通常是基于初值的常数函数)开始,通过反复积分微分方程的右端项来构造一个函数序列 $u_0(t), u_1(t), u_2(t), \dots$。</p>
<p>对于初值问题 $y'(t) = f(t, y(t))$, $y(t_0) = y_0$,迭代格式为:</p>
<div class="math-formula">
$u_0(t) = y_0$ <br>
$u_{k+1}(t) = y_0 + \int_{t_0}^{t} f(s, u_k(s)) ds$
</div>
<p>在特定条件下(例如 $f$ 满足Lipschitz条件),这个函数序列会<strong class="strong-emphasis">收敛到微分方程的精确解</strong>。这可以被视为在函数空间中的一种迭代过程。</p>
<div class="interactive-demo">
<h4><span class="material-icons-outlined">play_circle_outline</span>Picard 迭代法演示</h4>
<p>求解 $y' = y$, $y(0) = 1$。真实解为 $y(t) = e^t$。</p>
<div class="input-grid">
<div class="input-group">
<label for="picardIterations">迭代次数:</label>
<input type="number" id="picardIterations" value="3" min="0" max="10">
</div>
<div class="input-group">
<label for="picardTMax">绘制范围 $t_{max}$:</label>
<input type="number" id="picardTMax" value="2" min="0.1" max="5" step="0.1">
</div>
</div>
<button id="runPicard" class="action-button">运行 Picard 迭代</button>
<div class="status-message" id="picardStatus"></div>
<div class="plot-container" id="picardPlot"></div>
<div class="results-text" id="picardResults">迭代结果函数将显示在此...</div>
</div>
<h3><span class="material-icons-outlined">calculate</span>函数空间中的 Newton-Raphson 方法</h3>
<p>对于非线性偏微分方程或边值问题,有时可以将问题抽象为在某个函数空间中寻找算子方程 $F(u)=0$ 的根。Newton-Raphson 方法可以推广到函数空间(如 Newton-Kantorovich 方法),通过<strong class="strong-emphasis">线性化算子</strong>并迭代求解线性问题来逼近非线性问题的解。每一步迭代都会得到一个新的近似函数。</p>
<p>这些方法直接作用于函数,试图逐步逼近真实的函数解。</p>
<hr class="section-divider">
<h2><span class="material-icons-outlined">grid_on</span>2. 针对离散解的迭代法</h2>
<p>这才是《微分方程数值解法》课程中通常重点讨论“迭代法”的语境。当使用有限差分法、有限元法等将微分方程离散化后,会得到一个大型的代数方程组(通常是线性的,有时非线性)。迭代法是求解这类代数方程组的有效手段,尤其当方程组<strong class="strong-emphasis">规模很大且系数矩阵稀疏</strong>时。</p>
<h3><span class="material-icons-outlined">linear_scale</span>基本迭代法 (用于线性方程组 $Ax=b$)</h3>
<p>这些方法都基于对矩阵 $A$ 进行某种分裂 $A = M - N$,然后构造迭代格式 $Mx^{(k+1)} = Nx^{(k)} + b$。</p>
<ul>
<li><strong class="text-highlight">雅可比迭代法 (Jacobi Iteration)</strong></li>
<li><strong class="text-highlight">高斯-赛德尔迭代法 (Gauss-Seidel Iteration)</strong></li>
<li><strong class="text-highlight">逐次超松弛迭代法 (Successive Over-Relaxation, SOR)</strong></li>
</ul>
<div class="interactive-demo">
<h4><span class="material-icons-outlined">play_circle_outline</span>Jacobi 迭代法演示</h4>
<p>求解线性方程组 $Ax=b$。示例:</p>
<div class="math-formula">
$\begin{pmatrix} 4 & -1 & 0 \\ -1 & 4 & -1 \\ 0 & -1 & 3 \end{pmatrix} \begin{pmatrix} x_1 \\ x_2 \\ x_3 \end{pmatrix} = \begin{pmatrix} 2 \\ 6 \\ 5 \end{pmatrix}$
</div>
<p>(精确解: $x_1=1, x_2=2, x_3=7/3 \approx 2.333$)</p>
<div class="input-grid">
<div class="input-group">
<label for="jacobiMatrixA">矩阵 A (JSON格式, e.g., [[4,-1,0],[-1,4,-1],[0,-1,3]]):</label>
<input type="text" id="jacobiMatrixA" value="[[4,-1,0],[-1,4,-1],[0,-1,3]]">
</div>
<div class="input-group">
<label for="jacobiVectorB">向量 b (JSON格式, e.g., [2,6,5]):</label>
<input type="text" id="jacobiVectorB" value="[2,6,5]">
</div>
<div class="input-group">
<label for="jacobiMaxIter">最大迭代次数:</label>
<input type="number" id="jacobiMaxIter" value="10" min="1" max="100">
</div>
<div class="input-group">
<label for="jacobiTolerance">容差 (用于收敛判断):</label>
<input type="number" id="jacobiTolerance" value="0.0001" min="1e-10" max="1e-1" step="1e-5">
</div>
</div>
<button id="runJacobi" class="action-button">运行 Jacobi 迭代</button>
<div class="status-message" id="jacobiStatus"></div>
<div class="results-text" id="jacobiResults">迭代过程及结果将显示在此...</div>
<div class="error-message" id="jacobiError"></div>
</div>
<h3><span class="material-icons-outlined">model_training</span>更高级的迭代法</h3>
<p>这些方法通常用于大型稀疏线性系统,特别是对称正定系统。</p>
<ul>
<li><strong class="text-highlight">共轭梯度法 (Conjugate Gradient Method, CG)</strong></li>
<li><strong class="text-highlight">预处理共轭梯度法 (Preconditioned Conjugate Gradient Method, PCG)</strong></li>
</ul>
<h3><span class="material-icons-outlined">splitscreen</span>特定类型问题的迭代思想方法</h3>
<ul>
<li><strong class="strong-emphasis">交替方向隐式迭代法 (Alternating Direction Implicit, ADI)</strong>:ADI 本身是针对多维抛物或椭圆问题的差分格式构造方法。但它的每一步也涉及到求解一系列可以迭代求解的三对角系统,并且整个过程可以看作是一种迭代逼近多维问题解的方式。</li>
</ul>
<hr class="section-divider">
<h2><span class="material-icons-outlined">summarize</span>总结</h2>
<p>直接求解<strong class="strong-emphasis">连续问题</strong>时,像 Picard 迭代法和函数空间中的 Newton 法体现了迭代思想,它们作用于函数本身。</p>
<p>当我们谈论求解<strong class="strong-emphasis">离散解</strong>(即求解离散化后产生的代数方程组)时,迭代法指的是像雅可比、高斯-赛德尔、SOR、共轭梯度等用于求解线性(或非线性)代数系统的方法。这些方法在教材中主要是在讨论椭圆型方程的有限差分法(以及抛物型或双曲型方程的隐式格式)离散化后得到的代数方程组的求解时出现。</p>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
// --- Picard Iteration Demo ---
const picardIterationsInput = document.getElementById('picardIterations');
const picardTMaxInput = document.getElementById('picardTMax');
const runPicardButton = document.getElementById('runPicard');
const picardPlotDiv = document.getElementById('picardPlot');
const picardResultsDiv = document.getElementById('picardResults');
const picardStatusDiv = document.getElementById('picardStatus');
let picardChart = echarts.init(picardPlotDiv);
// Factorial function for Picard
function factorial(n) {
if (n === 0 || n === 1) return 1;
let result = 1;
for (let i = 2; i <= n; i++) result *= i;
return result;
}
// Function to generate Picard iterates for y' = y, y(0) = 1
// u_k(t) = 1 + t + t^2/2! + ... + t^k/k!
function getPicardIterate(k, t) {
let sum = 0;
for (let i = 0; i <= k; i++) {
sum += Math.pow(t, i) / factorial(i);
}
return sum;
}
function getPicardIterateString(k) {
if (k < 0) return "N/A";
let terms = [];
for (let i = 0; i <= k; i++) {
if (i === 0) terms.push("1");
else if (i === 1) terms.push("t");
else terms.push(`t^${i}/${i}!`);
}
return `u_${k}(t) = ${terms.join(" + ")}`;
}
runPicardButton.addEventListener('click', () => {
picardStatusDiv.textContent = "正在生成Picard迭代...";
const numIterations = parseInt(picardIterationsInput.value);
const tMax = parseFloat(picardTMaxInput.value);
const points = 100;
const step = tMax / points;
let seriesData = [];
let legendData = [];
let resultsText = "";
// True solution: e^t
let trueSolutionData = [];
for (let i = 0; i <= points; i++) {
const t = i * step;
trueSolutionData.push([t, Math.exp(t)]);
}
seriesData.push({
name: 'e^t (真实解)',
type: 'line',
smooth: true,
data: trueSolutionData,
lineStyle: { width: 2, color: '#28a745' }
});
legendData.push('e^t (真实解)');
// Picard iterates
for (let k = 0; k <= numIterations; k++) {
let iterateData = [];
for (let i = 0; i <= points; i++) {
const t = i * step;
iterateData.push([t, getPicardIterate(k, t)]);
}
const seriesName = `u_${k}(t)`;
seriesData.push({
name: seriesName,
type: 'line',
smooth: true,
data: iterateData,
lineStyle: { opacity: 0.7 }
});
legendData.push(seriesName);
resultsText += getPicardIterateString(k) + "\n";
}
picardResultsDiv.textContent = resultsText;
const option = {
tooltip: { trigger: 'axis' },
legend: { data: legendData, type: 'scroll', top: 10 },
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
xAxis: { type: 'value', name: 't', min: 0, max: tMax },
yAxis: { type: 'value', name: 'u(t)' },
series: seriesData
};
picardChart.setOption(option, true);
picardStatusDiv.textContent = "Picard迭代已生成。";
MathJax.typesetPromise(); // Retypeset if new MathJax content is added (though not in this specific demo's text output)
});
// Initial Picard plot
runPicardButton.click();
// --- Jacobi Iteration Demo ---
const jacobiMatrixAInput = document.getElementById('jacobiMatrixA');
const jacobiVectorBInput = document.getElementById('jacobiVectorB');
const jacobiMaxIterInput = document.getElementById('jacobiMaxIter');
const jacobiToleranceInput = document.getElementById('jacobiTolerance');
const runJacobiButton = document.getElementById('runJacobi');
const jacobiResultsDiv = document.getElementById('jacobiResults');
const jacobiErrorDiv = document.getElementById('jacobiError');
const jacobiStatusDiv = document.getElementById('jacobiStatus');
runJacobiButton.addEventListener('click', () => {
jacobiErrorDiv.textContent = "";
jacobiStatusDiv.textContent = "正在运行Jacobi迭代...";
jacobiResultsDiv.textContent = "";
try {
const A_str = jacobiMatrixAInput.value;
const b_str = jacobiVectorBInput.value;
const maxIter = parseInt(jacobiMaxIterInput.value);
const tol = parseFloat(jacobiToleranceInput.value);
const A = JSON.parse(A_str);
const b = JSON.parse(b_str);
if (!Array.isArray(A) || !A.every(row => Array.isArray(row)) || !Array.isArray(b)) {
throw new Error("矩阵A或向量b的格式不正确。请输入有效的JSON数组。");
}
const n = A.length;
if (n === 0 || A.some(row => row.length !== n) || b.length !== n) {
throw new Error("矩阵A必须是方阵,且A和b的维度必须匹配。");
}
// Check for zero diagonal elements
for(let i = 0; i < n; i++) {
if (Math.abs(A[i][i]) < 1e-9) { // Using a small tolerance for zero
throw new Error(`Jacobi迭代失败:矩阵A的对角元素 A[${i}][${i}] 过小或为零。方法可能不收敛或导致除零错误。`);
}
}
let x = Array(n).fill(0); // Initial guess
let x_new = Array(n).fill(0);
let resultsText = "初始猜测 x^(0): [" + x.map(val => val.toFixed(5)).join(", ") + "]\n\n";
let converged = false;
for (let k = 0; k < maxIter; k++) {
for (let i = 0; i < n; i++) {
let sigma = 0;
for (let j = 0; j < n; j++) {
if (i !== j) {
sigma += A[i][j] * x[j];
}
}
x_new[i] = (b[i] - sigma) / A[i][i];
}
resultsText += `迭代 ${k + 1}:\nx_new: [${x_new.map(val => val.toFixed(5)).join(", ")}]\n`;
let norm_diff = 0;
for (let i = 0; i < n; i++) {
norm_diff += Math.pow(x_new[i] - x[i], 2);
}
norm_diff = Math.sqrt(norm_diff);
resultsText += `||x_new - x||_2 = ${norm_diff.toExponential(5)}\n\n`;
x = [...x_new]; // Update x for next iteration
if (norm_diff < tol) {
resultsText += `在 ${k + 1} 次迭代后收敛。\n`;
converged = true;
break;
}
}
if (!converged) {
resultsText += `达到最大迭代次数 ${maxIter},可能未完全收敛。\n`;
}
resultsText += "\n最终解 x: [" + x.map(val => val.toFixed(5)).join(", ") + "]";
jacobiResultsDiv.textContent = resultsText;
jacobiStatusDiv.textContent = "Jacobi迭代完成。";
} catch (e) {
jacobiErrorDiv.textContent = "错误: " + e.message;
jacobiStatusDiv.textContent = "迭代失败。";
console.error(e);
}
});
// Initial typeset for any static MathJax on page
MathJax.typesetPromise();
});
</script>
</body>
</html>
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.