二分算法,790. 数的三次方根
给定一个浮点数 n,求它的三次方根。
输入格式
共一行,包含一个浮点数 n。
输出格式
共一行,包含一个浮点数,表示问题的解。
注意,结果保留 6 位小数。
数据范围
−10000≤n≤10000
输入样例:
1000.00
输出样例:
10.000000
难度:简单
时/空限制:1s / 64MB
思路: 使用二分进行数据逼近
#include<iostream>
#include <cmath>
using namespace std;
double x;
int main(){
cin >> x;
int op = 1;
if(x!=abs(x)){
op = -1;
x = abs(x);
}
double mid = 0;
// 问题1:为什么要设置为 r = x+1
double l = 0, r = x+1;
while(abs(mid*mid*mid-x)>10e-10){
mid = (l+r)/2;
if(mid*mid*mid>=x) r = mid;
//问题2:为什么此时l能=mid,后面不用跟上一个常数?
else l = mid;
}
printf("%lf",op*r);
}
问题1:为什么要将r
设置为 x+1
,为什么不是x
?
因为假设给出数据为 0.01, 答案应该为0.1. 假设r
= 0.001,这个算法就将在 0~0.001
区间去找, 毫无疑问,这个区间是不包含正确答案的,所以为了防止输入的数据小于1,将r
设置为x+1
问题2:为什么此时l能=mid,后面不用跟上一个常数?
跟常数的目的是为了防止传统二分int除法产生的 i/2 = (i+1)/2问题,这种问题会导致二分的l
和r
卡住不动,从而无法满足 r<=l
的出循环条件。这里并非用r<=l
作为出循环条件,而是使用误差小于10e-8作为出循环条件,因而不需加一个常数。
一个猜想:
其实这道题也能用r<=l
作为出循环条件,在这种情况下 l=mid+10e-8。但是这种情况下,有的数据会时间超时。因为双精度浮点型小数点后面有效数字为15~16位, 要两个一直逼近的话,后面会非常困难。我们只需要误差小于10e-6,但是double照这种条件会一直二分到10e-16.
我认为,到了10e-15精度,double才会出现int的问题,产生下取"整"。
超时代码:
#include<iostream>
#include <cmath>
using namespace std;
double x;
int main(){
cin >> x;
int op = 1;
if(x!=abs(x)){
op = -1;
x = abs(x);
}
double mid = 0;
double l = 0, r = x+1;
while(r>l){
mid = (l+r)/2;
if(mid*mid*mid>=x) r = mid;
else l = mid+10e-8;
}
printf("%lf",op*r);
}