三次方程 ax³ + bx² + cx + d = 0 求根是經典數值計算問題。雖然存在卡丹公式解析解,但二分法以其簡單、穩健、保證收斂的特性,成為工程實踐中尋找實根的首選。本文深度解析一個完整的 C++ 實現,展示如何結合導數極值定位根區間與二分法精確求根。
目標函數:f(x) = x³ - 3x² - 6x + 8
導數:f'(x) = 3x² - 6x - 6 = 0
極值點:x = (3 ± √21)/2 ≈ -0.791, 3.791
由羅爾定理,實根分佈於極值點分隔的區間:
(-∞, -0.791):單調遞增,一根(-0.791, 3.791):單調遞減,一根(3.791, +∞):單調遞增,一根double bisection(double x, double y, double z) {
double a = x, b = y, p;
while ((b - a) / 2 >= z) { // 區間寬度判斷
p = (a + b) / 2;
if (f(p) * f(b) <= 0) // 根在 [p, b]
a = p;
else
b = p;
}
return p;
}
收斂條件: (b-a)/2 < z 保證誤差 < z。
int main() {
double a[5], tol;
char filename[256];
cout << "誤差容限: "; cin >> tol;
cout << "輸出文件: "; cin >> filename;
ofstream outfile(filename);
// 極值點
a[2] = (3 - sqrt(21)) / 2; // ≈ -0.791
a[3] = (3 + sqrt(21)) / 2; // ≈ 3.791
// 向外擴展確保異號
a[1] = a[2]; a[4] = a[3];
for (int i=1; ; i++) {
if (f(a[1]) >= 0) a[1]--; // 左端點向左直到 f<0
else if (f(a[4]) <= 0) a[4]++; // 右端點向右直到 f>0
else break;
}
// 三個區間求根
for (i=1; i<4; i++)
outfile << "根在 (" << a[i] << "," << a[i+1] << ") = "
<< bisection(a[i], a[i+1], tol) << endl;
}
// 輸入
tol = 1e-7
file = output.txt
// 輸出
函數f(x)在區間(-2.79129,-0.791288)上的根為-2
函數f(x)在區間(-0.791288,3.79129)上的根為1
函數f(x)在區間(3.79129,4.79129)上的根為4
精確根為 -2, 1, 4,驗證:(x+2)(x-1)(x-4) = x³ - 3x² - 6x + 8 ✓
原代碼 if(f(a[1])>=0) a[1]--; else if(f(a[4])<=0) a[4]++; 存在邏輯缺陷:else if 導致單邊擴展。應獨立檢查:
while (f(a[1]) >= 0) a[1] -= step;
while (f(a[4]) <= 0) a[4] += step;
// step 可自適應:初始 1.0,失敗時倍增
#define f(x) ((1*(x) - 3)*(x) - 6)*(x) + 8 // 3 次乘法 + 3 次加法
// 避免 pow() 開銷,提升數值穩定性
while (b - a > 2*tol) { // 等價但更清晰
p = (a + b) * 0.5;
if (f(p) == 0) break; // 精確命中
if (f(a) * f(p) < 0) b = p; else a = p;
}
class PolynomialRootFinder {
std::function f_;
double tol_;
public:
std::vector findRealRoots(double left, double right);
// 結合斯圖姆序列隔離根區間,再二分求根
};
| 方法 | 收斂階 | 需導數 | 保證收斂 | 適用性 |
|---|---|---|---|---|
| 二分法 | 線性 (1/2) | 否 | 是 (異號區間) | 最廣 |
| 牛頓法 | 二次 | 是 | 否 (需良好初值) | 光滑函數 |
| 割線法 | 超線性 (1.618) | 否 | 否 | 無導數場景 |
| 卡丹公式 | 解析 | 否 | 是 | 僅三次 |
三次方程二分法求根展示了數學洞察(極值點隔離根)與工程實踐(二分法保證收斂)的完美結合。雖然卡丹公式給出解析解,但二分法的無需導數、保證收斂、易於並行化、擴展到任意連續函數的特性,使其成為工程軟件中求解非線性方程的基石。掌握根區間隔離、收斂判斷、數值穩定求值,是每個計算科學工程師的必備內功。