zoukankan      html  css  js  c++  java
  • P1024 一元三次方程求解 牛顿迭代+盛金公式+二分+勘根定理

    P1024 一元三次方程求解

    传送门

    题目描述

    有形如:ax^3+bx^2+cx^1+dx^0=0 这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d均为实数),并约定该方程存在三个不同实根(根的范围在-100至100之间),且根与根之差的绝对值1。要求由小到大依次在同一行输出这三个实根(根与根之间留有空格),并精确到小数点后2位。

    提示:记方程f(x)=0,若存在2个数x1x2,且x1<x2f(x1)×f(x2)<0,则在(x1,x2)之间一定有一个根。

    输入格式

    一行,4个实数A,B,C,D。

    输出格式

    一行,3个实根,并精确到小数点后2位。

    输入输出样例

    输入 #1

    1 -5 -4 20

    输出 #1

    -2.00 2.00 5.00

    解题思路1:可以通过盛金公式分开讨论求解:盛金公式博客:传送门

    AC代码:

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<set>
    #include<queue>
    #define EPS 0.001
    using namespace std;
    double a,b,c,d;int main(void)
    {
        ios::sync_with_stdio(false);
        cin>>a>>b>>c>>d;
        priority_queue<double,vector<double>,greater<double> > q;
        double A,B,C;
        A=b*b-3*a*c;
        B=b*c-9*a*d;
        C=c*c-3*b*d;
        double derta=B*B-4*A*C; 
        double K=B/A;
        double x1,x2,x3;
        if(A==B&&B==0)
        {
            x1=x2=x3=(-b)/(3*a);
        }
        else if(derta==0)
        {
            x1=-b/a+K;
            x2=x3=-K/2;
        }
        else if(derta<0)
        {
            double T=(2*A*b-3*a*B)/(2*A*sqrt(A));
            double theta=acos(T);
            double xt=theta/3.0;
            x1=(-1*b-2*sqrt(A)*cos(xt))/(3.0*a);
            x2=(-1*b+sqrt(A)*(cos(xt)-sqrt(3)*sin(xt)))/(3*a);
            x3=(-1*b+sqrt(A)*(cos(xt)+sqrt(3)*sin(xt)))/(3*a);
        }
        q.push(x1);
        q.push(x2);
        q.push(x3);
        while(q.size()!=1)
        {
            printf("%.2lf ",q.top());
            q.pop();
        }
        printf("%.2lf
    ",q.top());
        q.pop();
        return 0;
    }

    解法思路2:可以通过牛顿迭代,我们知道这个三次函数有三个解,那么就会有两个波峰,通过对导函数的学习我们知道

    波峰的时候当f'(x)=0,我们就能找到两个波峰的横坐标,我们通过对左边波峰的横坐标的左边进行牛顿迭代,我们就能求出最左边的零点

    我们通过右边波峰的横坐标的右边进行牛顿迭代,就能求出最右边的零点,然后对两个波峰之间的横坐标进行牛顿迭代,我们就能求出最中间的零点

    附上大佬的牛顿迭代讲解:传送门

    AC代码:

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    double a,b,c,d;
    using namespace std;
    #define EPS 0.001
    double f(double x)//这里是原函数(^U^)ノ~YO
    {
        return a*x*x*x+b*x*x+c*x+d;
    }
    double ff(double x)//这是导函数( ̄▽ ̄)"
    {
        return 3*a*x*x+2*b*x+c;
    }
    double ND(double x)//这就是牛顿迭代((●'◡'●))
    {
        while(fabs(f(x))>EPS)
            x-=f(x)/ff(x);
        return x;
    }
    int main(void)
    {
        cin>>a>>b>>c>>d;
        double derta=sqrt(4*b*b-12*a*c);
        double x1=(-2*b-derta)/(6*a);//左边波峰的横坐标
        double x2=(-2*b+derta)/(6*a);//右边波峰的横坐标
        printf("%.2lf %.2lf %.2lf
    ",ND(x1-EPS),ND((x1+x2)/2),ND(x2+EPS));
    }

    解法思路3:二分法!,这也是此题的考点!(其实我开始二分也不会),在借鉴了大佬的博客之后,

    我还是搞懂了此处二分的技巧(但是开始看到的时候就是想不出来%%%),因为零点的范围是[-100.0,100.0]

    并且两根之间的距离大于等于1,并且题目给出了提示f(x1)*f(x2)<0,则在(x1,x2)之间必有零点(这个就是勘根定理),所以我们可以枚举每一个范围为1的区间

    然后查看是否存在零点,然后找到三个输出就行。(因为必定有三个不同的实根)。

    AC代码:

    #include<iostream>
    #include<cmath>
    #include<cstdio>
    #include<queue>
    #define EPS 0.001
    using namespace std;
    double a,b,c,d;
    double f(double x)
    {
        return a*x*x*x+b*x*x+c*x+d;
    }
    int main(void)
    {
        ios::sync_with_stdio(false);
        cin>>a>>b>>c>>d;
        int ans=0;
        for(int i=-100;i<100;++i)
        {
            double l=i;//这个范围就为1
            double r=i+1.0;
            if(!f(l))//如果找到左零点
            {
                ans++;
                printf("%.2lf ",l);//直接输出
            }
            if(f(l)*f(r)<0)//勘根定理,发现在l和r范围有零点
            {
                while(fabs(l-r)>EPS)//二分
                {
                    double mid=(l+r)/2;
                    if(f(mid)*f(r)<0)
                    l=mid;
                    else
                    r=mid;
                }
                if(ans<2)
                printf("%.2lf ",r);
                else
                {
                    printf("%.2lf
    ",r);
                    break;//跳出能省点时间
                }
            }
        }
        return 0;
    }

    其实本题由于数据比较少,好像暴力枚举也能过(因为精度实在是太小了!0.01),但是我就不写了,比较简单。

  • 相关阅读:
    分享Silverlight/WPF/Windows Phone一周学习导读(12月13日12月19日)
    分享Silverlight/WPF/Windows Phone一周学习导读(12月20日12月26日)
    关注Silverlight的未来 推荐注册微软Silverlight FireStarter大会
    Silverlight for Windows Phone Toolkit升级 新增四个控件
    分享Silverlight新鲜事(11月15日21日) PDC 10 Downloader
    分享Silverlight/WPF/Windows Phone一周学习导读(11月22日28日)
    微软副总裁Bob Muglia对Silverlight的公开道歉信
    分享Silverlight/WPF/Windows Phone一周学习导读(11月29日12月5日)
    [转]diff和patch
    Linux查看进程的所有子进程和线程
  • 原文地址:https://www.cnblogs.com/Mangata/p/13520980.html
Copyright © 2011-2022 走看看