一元三次方程求实数根

题目

蓝桥杯算法训练VIP-一元三次方程求解

image-20231118201701332

数学分析

image-20231118202252546

对于求解一元三次方程,我们可以使用卡尔达诺公式,可以应对方程有一个实根和两个复数根、一个实根和两个重复复数根、以及三个不同实根这三种情况。

image-20231118202636832

示例代码

暴力解法

暴力解法很容易想到,缺点就是只能应对实根范围比较小的题目

  • 根据题目实数根的范围,设它为i,遍历i从-100到100,每次增加0.01(因为精确两位小数)
  • 判断一元三次方程的值是否在-0.01到0.01之间(因为是浮点数,由于浮点数的二进制表示无法精确表示某些十进制小数,例如0.1的二进制表示就是一个无限循环小数,而计算机储存浮点数使用的二进制位数有限,所以浮点数是一个近似值,只能确定范围,不能精确到某一个值)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class 一元三次方程 {
public static void main(String[] args) {
double a, b, c, d; // ax^3 + bx^2 + cx + d = 0
Scanner scanner = new Scanner(System.in);
// 输入系数
// System.out.println("请输入方程的系数(空格分隔):");
a = scanner.nextDouble();
b = scanner.nextDouble();
c = scanner.nextDouble();
d = scanner.nextDouble();

double i, temp;
double[] num = new double[3]; // 存放实根
int k = 0; // 记录实根数目

for (i = -100; i <= 100; i += 0.01) {
temp = a * Math.pow(i, 3) + b * Math.pow(i, 2) + c * i + d;
if (temp > -0.01 && temp < 0.01)
num[k++] = i;
if (k == 3)
break;
}
// 输出实根
System.out.printf("%.2f %.2f %.2f", num[0], num[1], num[2]);
}
}

数学解法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class Main {

public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
// System.out.println("请输入方程的系数(空格分隔):");
double a = scanner.nextDouble();
double b = scanner.nextDouble();
double c = scanner.nextDouble();
double d = scanner.nextDouble();

// 解方程
double[] roots = solveCubicEquation(a, b, c, d);
Arrays.sort(roots);

// System.out.println("方程的实根为:");
for (double root : roots) {
System.out.printf("%.2f ", root);
}

scanner.close();
}

private static double[] solveCubicEquation(double a, double b, double c, double d) {
double[] roots = new double[3];

// 将方程转化为标准形式
double p = c / a - (b * b) / (3 * a * a);
double q = d / a + (2 * b * b * b) / (27 * a * a * a) - (b * c) / (3 * a * a);

// 计算判别式
double delta = q * q / 4 + Math.pow((p / 3), 3);

if (delta > 0) {
// 一实根,两虚根
double u = Math.cbrt(-q / 2 + Math.sqrt(delta));
double v = Math.cbrt(-q / 2 - Math.sqrt(delta));
roots[0] = u + v - b / (3 * a);
} else if (delta == 0) {
// 一个实根和两个重复的虚根
double u = Math.cbrt(-q / 2);
roots[0] = 2 * u - b / (3 * a);
roots[1] = roots[2] = -u - b / (3 * a);
} else {
// 三个不同的实根
double theta = Math.acos(-q / 2 * Math.sqrt(-27 / (p * p * p)));
roots[0] = 2 * Math.sqrt(-p / 3) * Math.cos(theta / 3) - b / (3 * a);
roots[1] = 2 * Math.sqrt(-p / 3) * Math.cos((theta + 2 * Math.PI) / 3) - b / (3 * a);
roots[2] = 2 * Math.sqrt(-p / 3) * Math.cos((theta + 4 * Math.PI) / 3) - b / (3 * a);
}
return roots;
}
}