蓝桥杯 算法提高 一元三次方程求解

一、题目

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

样例输出
2.00 2.00 5.00

数据规模和约定
|a|,|b|,|c|,|d|<=10

二、思路

要找出三个解,暴力的话是肯定可以解的,但肯定超时。那么就要缩小范围。
回顾一下高中数学,我们也求过近似解

首先对函数求导,f '(x) = 3ax^2 +2bx+c。
由于本题约定该方程必有三个不同的实数根,那么一定存在两个极值点v1,v2,假设v1 < v2。 根据求根公式可以解出方程 3ax^2 + 2bx + c = 0的两根v1,v2。

由此可以得出函数的三个根的限制条件: -100 < x1
由高中数学可知,若 m < n,且f(m)*f(n)<0,则在(m,n)之间一定有一个根,那么我们可以根据这个来二分区间逼近根。

三、代码

#include 
using namespace std;
#include 
#include 

float a, b, c, d;		

float find(float left, float right);
float f(float x);

int main()
{
	scanf("%f%f%f%f",&a,&b,&c,&d);

	//用求根公式求出两个极值点
	float v1, v2;
	v1 = (0 - 2 * b - sqrt(4 * b * b - 12 * a * c)) / 6 * a;
	v2 = (0 - 2 * b + sqrt(4 * b * b - 12 * a * c)) / 6 * a;

	find(-100.000, v1);
	find(v1, v2);
	find(v2, 100.000);

	return 0;
}

float find(float left, float right)
{
	if (left <= right)
	{
		float mid = (left + right) / 2;			//直接除以2没问题吧

		float temp = f(mid);	//将f(mid)保存在变量中,不然总是会调用函数

		if ((temp > 0 && temp < 0.001)||(temp < 0 &&temp > -0.001))		//如何确保精度呢
		{
			printf("%.2f ",mid);
			return mid;
		}

		if (f(left) * temp < 0)				//存在根
			find(left, mid);
		else
			find(mid, right);
	}

}

float f(float x)
{
	return a * x * x * x + b * x * x + c * x + d;
}

在这里插入图片描述在这里插入图片描述

四、总结

1、当采用f(mid) == 0条件return,会造成超时。而通过 printf 详细数据,如图,我们可以发现不需要 函数值等于0 这么苛刻的条件。我们观察数据发现,函数值小于0.001时,就可以确保精度达到0.01
蓝桥杯 算法提高 一元三次方程求解_第1张图片
以下代码,验证了%f输出格式可以实现四舍五入

#include 
using namespace std;

int main()
{
	float x1 = 1.9999;
	float x2 = -1.9999;
	float x3 = 1.3299;
	float x4 = -1.3201;
	printf("%.2f,%.2f,%.2f,%.2f",x1,x2,x3,x4);
	 
	return 0;
}

在这里插入图片描述
2、在用vs 2019中运行,可以直接abs(temp) < 0.001,但是dev C++5.11中,实数使用abs函数会丧失精度,同样的代码,应该是编译器的差异【注:temp即f(mid),是函数值】。改成如下条件就可以了。

(temp > 0 && temp < 0.001)||(temp < 0 && temp > -0.001)

五、参考

1、 一元三次方程求解
2、思路

你可能感兴趣的:(C/C++,#,蓝桥杯,算法,蓝桥杯,c++,一元三次函数求解)