卡丹公式和盛金公式推导——求解一元三次方程
看 AcWing 上好像还没有关于盛金公式解三次方程的分享,今天来写一篇~
在做洛谷P1024一元三次方程求解这道题的时候,发现题解里有关于盛金公式的介绍,于是自己也去按卡丹公式(Cardano formula)推导了一下~
题目介绍
有形如:ax3+bx2+cx+d=0 这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在 −100 至 100 之间),且根与根之差的绝对值 ≥1 。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后 2 位。
卡丹公式介绍
有三元一次方程ax3+bx2+cx+d=0
设函数f(x)=ax3+bx2+cx+d
可以求导得到f′(x)=3ax2+2bx+c
该二次函数顶点横坐标为x=−b3a
记为 λ ,令 x−λ=y ,有f(x)=a(y−a3b)3+b(y−a3b)2+c(y−a3b)+d
令 f(x)=0 ,化简后二次项可消去,得到y3−py+q=0其中
p=b2−3ac3a2q=2b3−9abc+27a2d27a3
引入完全立方公式(u+v)3=u3+3u2v+3uv2+v3变形得到(u+v)3−3uv(u+v)−(u3+v3)=0
记 y=u+v ,有y3−3uvy−(u3+v3)=0发现该方程与 y3−py+q=0 相似,于是设p=3uv,q=−(u3+v3)
得到u=3√−q2+√(q2)2−(p3)3v=3√−q2−√(q2)2−(p3)3
对于方程x3=λ其三个解分别为x1=3√λ,x2=ω3√λ,x3=ω23√λ其中 ω=−1+√3i2,ω2=−1−√3i2
由此可知,三次方程的判别式为Δ=(q2)2−(p3)3
于是,卡丹判别法为:
当 Δ>0 时,方程有一个实根和一对共轭虚根;
当 Δ=0 时,方程有三个实根,其中有一个两重根;
当 Δ<0 时,方程有三个不相等的实根。
三次方程求根公式过于冗长,可通过上述推导得出,故不再赘述。
盛金公式介绍
在推导卡丹判别式的时候,我们有
p=b2−3ac3a2q=2b3−9abc+27a2d27a3
观察 p ,不妨令 A=b2−3ac ,则可得到
p=A3a2q=2Ab−3a(bc−9ad)27a3
此时观察 q ,不妨令 B=bc−9ad ,则可二次简化 q q=2Ab−3aB27a3
于是,当 A=B=0 时, p=q=0 ,有三重根,得到盛金公式1 x1=x2=x3=−b3a=−cb=−3dc
当 A≠0 时,从判别式 Δ=(q2)2−(p3)3 入手进行推导。
将 A 和 B 代入并化简得 Δ=B2−4A(c2−3bd)324a4
联系二次方程的求根公式,不妨设 C=c2−3bd ,则得到盛金总判别式 Δ=(q2)2−(p3)3=B2−4AC(18a2)2
为与总判别式区分,记 δ=B2−4ac为 盛金判别式
对于卡丹公式推导过程中的u=3√−q2+√(q2)2−(p3)3v=3√−q2−√(q2)2−(p3)3
我们可以用上面的简单代换简化u=−13a3√Ab+3a(−B−√δ)2v=−13a3√Ab+3a(−B+√δ)2
将根式下的内容提出,记作Y1=Ab+3a(−B−√δ)2Y2=Ab+3a(−B+√δ)2
回代自然也有u=−3√Y13av=−3√Y23a
综合得到 y 的通解y1=−3√Y1+3√Y23ay2=(3√Y1+3√Y2)+√3(3√Y1−3√Y2)i−6ay3=(3√Y1+3√Y2)−√3(3√Y1−3√Y2)i−6a
同样可以得到 x的通解x1=−b−(3√Y1+3√Y2)3ax2=−2b+(3√Y1+3√Y2)+√3(3√Y1−3√Y2)i6ax3=−2b+(3√Y1+3√Y2)−√3(3√Y1−3√Y2)i6a
以上六式即为 盛金公式2 ,前提是 δ=B2−4AC>0 。显然,当 δ>0 时,方程有一个实根和一对共轭虚根。
当 δ=0 时,以盛金公式2求解,有
x1=−b−23√Y3a,x2=x3=−b+3√Y3a其中Y=2Ab−3aB2联系 δ=0 ,有(2Ab−3aB)2=4A3显然,此时 A>0 ,同乘以 Y 并变形得到 Y=(2Ab−3aB)38A3代入 x 此时的特解得到x1=−ba+BA,x2=x3=−B2A此即为盛金公式3。
当 δ<0 时,有Y1=Ab+3a(−B−√−δ)2iY2=Ab+3a(−B+√−δ)2i
不妨设 a>0 ,有Y1=Ab+3a(−B−√4A3−(2Ab−3aB)2)2iY2=Ab+3a(−B+√4A3−(2Ab−3aB)2)2i
利用复数开方法则,有\sqrt[3]{Y_1}=
\left\\{\begin{aligned}
& \sqrt{A}\left(\cos\frac{\theta}{3}+i\sin\frac{\theta}{3}\right) \\\
& \sqrt{A}\left[\cos\left(\frac{\theta}{3}+\frac{2\pi}{3}\right)+i\sin\left(\frac{\theta}{3}+\frac{2\pi}{3}\right)\right] \\\
& \sqrt{A}\left[\cos\left(\frac{\theta}{3}+\frac{2\pi}{3}\right)+i\sin\left(\frac{\theta}{3}-\frac{2\pi}{3}\right)\right] \\\
\end{aligned}
\right.
\sqrt[3]{Y_2}=
\left\\{\begin{aligned}
& \sqrt{A}\left(\cos\frac{\theta}{3}-i\sin\frac{\theta}{3}\right) \\\
& \sqrt{A}\left[\cos\left(\frac{\theta}{3}+\frac{2\pi}{3}\right)-i\sin\left(\frac{\theta}{3}+\frac{2\pi}{3}\right)\right] \\\
& \sqrt{A}\left[\cos\left(\frac{\theta}{3}+\frac{2\pi}{3}\right)-i\sin\left(\frac{\theta}{3}-\frac{2\pi}{3}\right)\right] \\\
\end{aligned}
\right.
其中 θ=arccos(2Ab−3aB2√A3)
有约束条件3√Y1⋅2√Y2=A
选取合适的 u 和 v 代入盛金公式2,得到
\left\\{\begin{aligned}
x_1&=\frac{-b-2\sqrt{A}\cos\frac{\theta}{3}}{3a} \\\
x_2&=\frac{-b+\sqrt{A}\left(\cos\frac{\theta}{3}+\sqrt{3}\sin\frac{\theta}{3}\right)}{3a} \\\
x_2&=\frac{-b+\sqrt{A}\left(\cos\frac{\theta}{3}-\sqrt{3}\sin\frac{\theta}{3}\right)}{3a} \\\
\end{aligned}
\right.
此即为 盛金公式4。当然为了形式上更加规范,也可用差角公式进行改写
\left\\{\begin{aligned}
x_1&=\frac{-b-2\sqrt{A}\cos\frac{\theta}{3}}{3a} \\\
x_2&=\frac{-b-2\sqrt{A}\left(\frac{\theta}{3}+\frac{2\pi}{3}\right)}{3a} \\\
x_2&=\frac{-b-2\sqrt{A}\left(\frac{\theta}{3}-\frac{2\pi}{3}\right)}{3a} \\\
\end{aligned}
\right.
以上推导盛金公式4的过程是基于 a>0 的假设的,当 a<0 时推导过程完全一致,只需互换 Y1 和 Y2 的位置即可。
至此,盛金公式推导完毕。
题目解答
当然可以用二分来做出三次方程的解,提供题解如下:
#include<bits/stdc++.h>
using namespace std;
double a, b, c, d;
double fc(double x)
{
return a * x * x * x + b * x * x + c * x + d;
}
int main()
{
double l, r, m, x1, x2;
int s = 0, i;
scanf("%lf%lf%lf%lf", &a, &b, &c, &d);
// 枚举互相不重叠的每一个长度为1的区间,在区间内进行二分查找
for (i = -100; i < 100;i++)
{
l = i;
r = i + 1;
x1 = fc(l);
x2 = fc(r);
if(!x1)
{
printf("%.2lf ", l);
s ++;
} //判断左端点,是零点直接输出。
//不能判断右端点,会重复。
if(x1 * x2 < 0) //区间内有根。
{
while(r - l >= 0.001) //二分控制精度。
{
m = (l + r) / 2; //middle
if(fc(m) * fc(r) <= 0)
l = m;
else
r = m; //计算中点处函数值缩小区间。
}
printf("%.2lf ", r);
//输出右端点。
s++;
}
if (s == 3)
break;
//找到三个就退出大概会省一点时间
}
return 0;
}
用盛金公式做就更简单啦
#include <bits/stdc++.h>
using namespace std;
int main()
{
double a, b, c, d;
double as, bs, t, si;
double x1, x2, x3;
cin >> a >> b >> c >> d;
as = b * b - 3 * a * c;
bs = b * c - 9 * a * d;
t = (2 * as * b - 3 * a * bs) / (2 * sqrt(as * as * as));
si = acos(t);
x1 = (-b - 2 * sqrt(as) * cos(si / 3)) / (3 * a);
x2 = (-b + sqrt(as) * (cos(si / 3) + sqrt(3) * sin(si / 3))) / (3 * a);
x3 = (-b + sqrt(as) * (cos(si / 3) - sqrt(3) * sin(si / 3))) / (3 * a);
cout << fixed << setprecision(2) << x1 << " ";
cout << fixed << setprecision(2) << x3 << " ";
cout << fixed << setprecision(2) << x2 << " ";
return 0;
}
The End
感觉所以盛金公式和卡丹公式是一个东西,式子变形一下就能互相得出结果了
大佬太强了 我直接%%%%%
神犇明明是你🥺