想起了自己之前刚学习C语言的时候总喜欢做一些有意思的事,最让自己满意的就是解一元三次方程。这其中也有别的原因的,高中时候也研究过一元三次方程,但是当时绞尽脑汁也没有想出求解的办法,只是找出了三个根之间的关系,于是很不甘心,之后虽然也百度到了解答,但是由于步骤太过复杂冗长导致完全看不进去,直到接触到了计算机语言才知道这些问题也可以交给电脑解决,于是花了大约9个小时(一边写一边找bug)终于完成,首先我们来看一下中间我都遇到了什么问题,以及最终是怎么解决的
。。。。
突然又懒得讲了怪麻烦的而且这也没啥了不起的写了也没有人看挺自卑的还是算了吧我直接贴代码加点注释然后就去补番
由于一开始养成了不好的代码习惯,无论是缩进还是命名法我现在看了都想打自己,凑合凑合吧
#include<stdio.h>
#include<math.h>
#include<stdlib.h>
main()
{
int z;
for(z=0;;++z)
{
float a,b,c,d;
int ab,g,bc,cd,de,ef;
printf("\n 一元三次方程求实数解近似值\n\n <玩不坏版>\n\n");
printf(" aX^3+bX^2+cX+d=0\n\n 请输入系数:\n\n a=");
bc=scanf("%f",&a);
for(g=0;;++g)
{
if(bc==1)
{
break;
}
if(bc!=1)
{
fflush(stdin);
printf("\n 格式错误,请重新输入\n\n a=");
bc=scanf("%f",&a);
}
}
for(g=0;;++g)
{
if(a==0)
{
printf("\n a不能为0哦,不然就不是三次方程了\n\n请重新输入一次吧!\n\n a=");
scanf("%f",&a);
}
if(a!=0)
{
break;
}
}
printf("\n b=");
cd=scanf("%f",&b);
for(g=0;;++g)
{
if(cd==1)
{
break;
}
if(cd!=1)
{
fflush(stdin);
printf("\n 格式错误,请重新输入\n\n b=");
cd=scanf("%f",&b);
}
}
printf("\n c=");
de=scanf("%f",&c);
for(g=0;;++g)
{
if(de==1)
{
break;
}
if(de!=1)
{
fflush(stdin);
printf("\n 格式错误,请重新输入\n\n c=");
de=scanf("%f",&c);
}
}
printf("\n d=");
ef=scanf("%f",&d);
for(g=0;;++g)
{
if(ef==1)
{
break;
}
if(ef!=1)
{
fflush(stdin);
printf("\n 格式错误,请重新输入\n\n d=");
ef=scanf("%f",&d);
}
} //这之前的代码全部是用来纠错的
if(a<0)
{
a=-a;
b=-b;
c=-c;
d=-d;
} //系数正负转化,方便后面的讨论
double i,k,p,q;
int o,j,h;
if(b*b<=3*a*c)
{
if(b*b==3*a*c) //讨论各种情况
{
k=-b/(3*a);
p=a*k*k*k+b*k*k+c*k+d;
o=1;
if(p>0)
{
for(j=0;;++j)
{
i=-b/(3*a)-o;
q=a*i*i*i+b*i*i+c*i+d;
o=o*2;
if(q<=0)
{
break;
}
}
}
if(p<0)
{
for(j=0;;++j)
{
i=-b/(3*a)+o;
q=a*i*i*i+b*i*i+c*i+d;
o=o*2;
if(q>=0)
{
break;
}
}
}
for(h=0;h<50;++h)
{
i=i-(a*i*i*i+b*i*i+c*i+d)/(3*a*i*i+2*b*i+c);
}
if(p==0)
{
i=-b/(3*a);
}
}
if(b*b<3*a*c)
{
i=-b/(3*a);
for(h=0;h<100;++h)
{
i=i-(a*i*i*i+b*i*i+c*i+d)/(3*a*i*i+2*b*i+c);
}
}
printf("\n这个方程对应的函数是单调的\n\n所以只有一个实数解\n\n 即\n\n X=%.16f",i);
}
if(b*b>3*a*c)
{
double r,s,t,u;
int w,y;
r=(-b-sqrt(b*b-3*a*c))/(3*a);
s=(-b+sqrt(b*b-3*a*c))/(3*a);
t=a*r*r*r+b*r*r+c*r+d;
u=a*s*s*s+b*s*s+c*s+d;
if(t*u>=0)
{
if(d>0)
{
o=1;
for(w=0;;++w)
{
i=r-o;
o=o*2;
if(a*i*i*i+b*i*i+c*i+d<=0)
{
break;
}
}
}
if(d<0)
{
o=1;
for(w=0;;++w)
{
i=s+o;
o=o*2;
if(a*i*i*i+b*i*i+c*i+d>=0)
{
break;
}
}
}
for(h=0;h<100;++h)
{
i=i-(a*i*i*i+b*i*i+c*i+d)/(3*a*i*i+2*b*i+c);
}
}
if(t*u>0)
{
printf("\n这个方程只有一个实数解\n\n 即\n\n X=%.16f",i); //函数极小值大于零,只有一个解
}
if(t*u==0)
{
if(t==0)
{
printf("\n这个方程刚好有两个解\n\n 分别是\n\n X1=%.16f\n\n X2=%.16f",r,i); //极小值等于零,两个解
}
if(u==0)
{
printf("\n这个方程刚好有两个解\n\n 分别是\n\n X1=%.16f\n\n X2=%.16f",i,s); //极大值等于零,两个解
}
}
if(t*u<0)
{
double m,n,v;
o=1;
for(w=0;;++w)
{
m=r-o;
o=o*2;
if(a*m*m*m+b*m*m+c*m+d<=0)
{
break;
}
}
for(h=0;h<100;++h)
{
m=m-(a*m*m*m+b*m*m+c*m+d)/(3*a*m*m+2*b*m+c);
}
o=1;
for(w=0;;++w)
{
n=s+o;
o=o*2;
if(a*n*n*n+b*n*n+c*n+d>=0)
{
break;
}
}
for(h=0;h<100;++h)
{
n=n-(a*n*n*n+b*n*n+c*n+d)/(3*a*n*n+2*b*n+c);
}
v=(r+s)/2;
for(h=0;h<100;++h)
{
if(a*v*v*v+b*v*v+c*v+d>0)
{
r=v;
v=(r+s)/2;
}
if(a*v*v*v+b*v*v+c*v+d<0)
{
s=v;
v=(r+s)/2;
}
if(a*v*v*v+b*v*v+c*v+d==0)
{
break;
}
}
printf("\n这个方程有三个解\n\n 分别是\n\n X1=%.16f\n\n X2=%.16f\n\n X3=%.16f",m,v,n);
}
//最麻烦的一种情况,我是采用的两边的解做切线,中间的二分,做切线就是先找一个比较远的点作为切点,
//然后把在这里的切线与X轴的交点的横坐标作为新的切点的横坐标,以此类推,不断趋向于正确答案
//二分就是先找一个函数曲线上的点,判断函数值的正负,从而判断下一步朝哪个方向移动,
//虽然这个问题看起来思路简单,实现起来的时候还是会遇到很多问题,
//比如最开始的点如何选取才能让它收敛,还要二次求导观察它的斜率走向,
//并且要对三次函数的图像的各种性质有很好的了解才行
//所以它还是一个很有挑战性的问题的,虽然会经常被大家小看,
//在解决问题的时候也能学到很多东西,提升自己的逻辑思考能力
//如果你想去ACM的话,用来练手定是极好的
}
printf("\n\n输入1保留历史纪录\n输入2清空屏幕\n输入其他数字退出:");
scanf("%d",&ab);
if(ab!=1)
{
if(ab==2)
{
system("cls"); //清空屏幕
}
else
{
break;
}
}
}
}
下面是运行效果图,输入系数输出答案,我还加入了防止输入错误信息导致运行出错的代码
\(=^_^=)/