提示:黄色背景表示重点。橙色背景表示步骤。
目录
嗯,在学过整数二分之后,这里其实想了怎样通过二分的方法实现,emm,但是因为还不太熟练,不知道怎么表示这道题里所需要的性质,以及在想三次方怎么通过二分优化......额,没转过来弯,所以又用了遍历......(小声,不敢抬头)然后写了一堆bug(微笑)
暴力解法
这道题采用暴力的方法 相比 我们之前思考暴力做法 要注意很多地方,首先暴力的话应该是通过for循环吧,但是for循环第三个条件应该是类似i++这种的把(我们常用的,作为小白我能想到的),但是我们定义的可是double类型的浮点数啊,直接+1会漏掉多少情况啊;还有在写if判断条件,x*x*x==n是很容易范的错误吧,
由于浮点数的精度有限,因此在比较两个浮点数是否相等时应该判断两个浮点数之差的绝对值是否小于一个很小的常数(比如1e-8)。
由于以上原因,这道题暴力解法很容易出错,当然也不是不行,下面展示一下搜索结果(微笑)(问就是我没写出来)。看过之后也不难理解。
#include <iostream>
#include <cmath>
using namespace std;
double n;
int main()
{
cin >> n;
double ans = -1;//倘若输出-1 表示没有找到答案(也就是当输入的n不满足题目要求的时候)
for (double i = -10000; i <= 10000; i += 1e-6)
{
if (fabs(i * i * i - n) < 1e-6)
{
ans = i;
break;
}
}
printf("%.6lf\n", ans);
return 0;
}
嗯,除了二分其实还有另一种方法求一个数开n次方:牛顿迭代法。额,由于比较陌生,需要用到一个公式,我就先不研究这种方法了。感兴趣的可以搜一搜。(我兴趣不是很大[笑])
浮点数二分
思路就是:我们不用想那么多,什么二次方根,三次方根的,只要明确一件事情:二分就是一种通过不断缩小范围(方式是每次缩小一半)来找到最终答案的方法。不用考虑太多别的
所以在这道题里,用二分,首先就要定义出左右边界吧。我们的查找范围起始应该在 题目所要求的数据范围内,即【-10000,10000】,第一次写的时候写错了。
【0,n】:如果n是一个负数,那么它的三次方根也是一个负数,倘若这样无法求出正确答案。
【-n,n】:假设n=0.001,那么它的三次方根约等于0.1。那么区间内就不包含答案0.1,因此,通过二分法求解时就无法得到正确答案。
所以我们的初始范围应该是题目中所规定的数据范围。
然后进行while循环。这里就要说一下循环条件了。当区间范围只剩一个元素的时候,由于是浮点型数据,所以通过这样的方式:r-l<1e-8 来表示区间只剩一个数(可以看作整数浮点里的left<right),可以理解哈。
循环体内部,首先定义mid值。这里要注意的是,不能使用>>位运算符表示除以二的过程。
位运算符只能用于整型数据(如long,int等),不能用于浮点型数据(如double,float 等)。这是因为位运算符是直接对整数的二进制位进行操作的,而浮点数的二进制表示与整数不同,不能直接进行位运算。
接着要考虑if的判断条件。我们要想清楚,if里面的条件是用来根据性质划分区域的。那这道题里,性质应该是所得出的mid值的三次方与n的大小关系,即mid*mid*mid>=n时应该更改区间的右边界,反之更改左边界。
区间边界的更改。在浮点数二分中,我们不需要使用mid+1或mid-1来更新区间。这是因为浮点数具有无限精度,因此每次迭代时区间的大小都会减半。当区间大小小于指定的精度阈值时,循环结束。由于浮点数的精度非常高,因此不会出现死循环的情况。
到此浮点二分的思路就写完了。
上代码
#include<iostream>
using namespace std;
double n;
int main()
{
cin>>n;
double l=-10000,r=10000;
while(r-l>1e-8)
{
double mid=(l+r)/2;
if(mid*mid*mid>=n)r=mid;
else l=mid;
}
cout<<l<<endl;
return 0;
}
ok啦。
至此二分就全部讲解完了,接下来会做一些习题。
有问题欢迎指出,非常感谢!!
也欢迎交流建议奥。