目录
前言
思路讲解
代码展示
java版本
注意事项
C/C++版本
注意事项
对于一元二次方程,相信大家都不陌生,毕竟初中的时候就熟悉了。但是,解一元二次方程确实一个苦差事,毕竟真正在高中和大学中出现的那些方程都不是考官有意设的,也就是说它们很有可能是一个无限不循环小数,这样一来解它们就需要浪费很多不必要的时间。于是,程序员们就开始寻思着让计算机来代替人们计算。现在,解一元二次方程这个功能几乎每个计算器里头都有,我们今天就了解一下这个功能的原理,以及如何实现使用该功能一秒解出一元二次方程。我们这次演示的是java语言。
首先该程序的核心思想就是利用公式法解一元二次方程。公式法是:x=[-b±sqrt(b^2-4ac)]/(2a),其中sqrt()表示的是根号下。根据这个公式我们可以得到一元二次方程的解。该公式事实上揭示的是方程系数与根的关系。那我们根据这个我们就有了一个基本构思:向用户要一个一元二次方程的各项系数,然后返回解给用户。
这就完了么?理论上来说是的。但是,在用户实际使用的时候,还会有诸多不便。比如。有些一元二次方程没有解。因为sqrt()内的结果可能为负,而我们都知道,二次根号下的数是不能为负的(我们默认数都在实数范围),所以,我们还要给程序添加一个判断方程是否有解的一些语句。
除此以外,在实际使用上,我们会发现,有一些讨厌的一元二次方程的两个解都是无理数,而大多用户又不需要小数点后那么多位。你说,我们可以让用户自己约!可惜的是,没有用户有时间使用这种麻烦的代码,尤其是他今晚有100道题要写的时候。于是,我们要新添加一个功能,即,在用户输入完系数后,询问用户需要约到小数点后几位,并编写相应的方程。可是,有时候用户懒得写了,怎么办呢?我们再添加一个新功能:如果他不想写保留位,就可以选择按下换行键跳过这一步,直接显示结果。你瞧,这不就就舒服多了。
不过事实上,这个程序还有很多地方需要修改,比如静态方法quadraticSolution()中好像有些地方重复了。大家也可以试着扩充该程序,比如支持拍100道一元二次方程题的照,然后一键返回所有题目的答案。
package ELEM;//一个语法,说明该程序所在的包叫ELEM
//以下四行导入库
import java.io.BufferedReader;//我们会用该库读取缓冲器中数据
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;//实现输入功能的必备库
public class Quadratic {
public static double[] quadraticSolution(double a, double b, double c) {
double[] arr = new double[2];//初始化double类型的数组
if((b*b-4*a*c)>=0) {//判断方程有解的条件:delta大于0。我们会说明原因的
arr[0]=(-b+Math.sqrt(b*b-4*a*c))/(2*a);//第一个解
arr[1]=(-b-Math.sqrt(b*b-4*a*c))/(2*a);//第二个解
return arr;
}
else {
return arr;//方程无解的返回值
}
}
public static double correctTo(double data,int dec) {
int num=(int)Math.pow(10.0,dec);//实现的效果是10.0^dec
return Math.ceil(data*num)/num;//四舍五入
}
public static void main(String[] args) throws IOException {
Scanner input=new Scanner(System.in);
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
System.out.println("enter all the coefficients of this formula: a, b, c:");
//以下几行为一元二次方程各个项的系数:a, b, c
double a=input.nextDouble();
double b=input.nextDouble();
double c=input.nextDouble();
double[] result=Quadratic.quadraticSolution(a, b, c);
if(result[0]==result[1]==0.0 &&(b!=0 || c!=0)) {//方程无解的情况
System.out.println("it has no solution");
}
else {
System.out.println("correct to which decimal(press enter to skip this part):");
String str = "";//初始化并给读取器赋值
str = bf.readLine();
if (str.length() != 0) {
int dec=Integer.parseInt(str);//类型转换String->int
for(int j=0;j
这里有需要注意的点。1.在main函数中if判断方程无解的情况。当一个方程两个根都是0的时候,好像方程就无解了?错!方程x^2=0有解。虽然我们很确信,没有一个人会这样输入,但是为了确保程序结果的严谨性,我们还是要加两个条件来判断方程真的无解,即:系数b为0或c不为0。我们可以看到,这其实是一种比较鸡肋的判断方法,使用了纯数学方法判断。
第二点,大家可以看到BufferReader登场了。我们从他的名字就很容易判断出,他是专门用来读取缓存区中的数据的。但是在我们按下回车键“\r"解除阻塞状态后,他就无法获取到信息,在程序中体现为str.length()==0。当str长度不为0时,我们可以确信,用户一定输入了想要保留的小数位,而这信息储存在str中,为String类型。因此我们在把该数据赋值给dec之前,还需要通过Integer类中的parseInt()静态方法给该信息做一个类型转换——从String类型转为int类型。
第三点,大家注意到correctTo函数了么?他是用来保留小数位的。回忆一下,我们一般遇到这种情况是怎么做的?比如用户要保留2位小数。我们假设该方程只有一个根,是2.62782102。此时我们需要对第三位小数7进行四舍五入判断,结果得到是2.63。java中只有Math.ceil()支持四舍五入,可问题是他只支持一位小数的!那怎么办呢?很简单,我们可以给原数乘上10^2得到262.782102。现在就转换成约一位小数的问题了,交给Math.ceil()静态方法解决,得到263.0。接下来就很简单了,我们把263.0除以10^2即可的到结果,大功告成!
#include
#include
#include
//this program output the solution to any quadraticEquation
double* solution(double a, double b, double c) {//formula
static double arr[2];
double delta = b * b - 4.0 * a * c;
if (delta >= 0.0) {
arr[0] = (-b + sqrt(delta)) / (2.0 * a);//one root of the equation
arr[1] = (-b - sqrt(delta)) / (2.0 * a);//another root of the equation
return arr;//get address
}
else {
return arr;//the condition in which equation has no solution
}
}
double roundToDec(double data, int dec) {//correct root to dec decimal places
int num = (int)pow(10, dec + 1);
int integer = (int)(data * num);
if (integer % 10 >= 5) {//rounding
return (double)(integer + 10) / num;
}
else {
return (double)integer / num;
}
}
int main() {
double a, b, c;
int dec;
printf("enter coefficient a,b,c of this formula:");
//get user input of coefficient
scanf("%lf", &a);
getchar();//cancel out blocking status
scanf("%lf", &b);
getchar();
scanf("%lf", &c);
double* roots = solution(a, b, c);//receive solution array from function
if (roots[0] ==0.0 && roots[1] == 0.0) {//has no solution
printf("it has no solution");
}
else {
printf("correct to which decimal:");
getchar();
scanf("%d", &dec);
for (int i = 0; i < 2; i++) {
roots[i] = roundToDec(roots[i], dec);
}
if (roots[0] == roots[1]) {// has only one root
printf("x=%lf", roots[0]);
}
else {
for (int j = 0; j < 2; j++) {//has two roots
printf("x%d=%lf\n", j + 1, roots[j]);
}
}
}
//system("pause");
return 0;
}
这里有几点需要注意的。首先,大家一定一定要注意一下这里的类型的变换,尤其是新学C语言不久的读者,类型出了问题通常是比较讨厌的,因为C语言编译器不会提醒你是类型的问题,他会伪装成一个逻辑错误,要你自己debug发现。所以类型一定是代码检查的重中之重。像这里的roundToDec()方程我就不说了,在Java中已经把思路捋的七七八八了。
这里大家看到连续scanf()那里,中间插了几个getchar()。这里涉及到了部分scanf()的工作原理,因此我简单的解释一下。打个比方,我们一般洗衣服,都会集满一桶脏衣服再去放进洗衣机,这样方便且更有效率。scanf()也是这样。在你输入时且没有按下回车键之前,你的输入会储存到缓存区,此时程序进入阻塞状态,就是直到你按下回车程序才继续运行。不过这一切有一个前提,就是输入前缓存区为空,否则下一个scanf()直接清空缓存区。而在你输入回车后,回车符"\r"会留在缓存区中,这样下次scanf()拿到的就是"\r"。所以我们要用getchar()把"\r"拿走先。