一 序
本篇属于算法整理系列。
不能否认,有的大公司重视基础,算法可能就是重要考察点。
题目:实现函数:
double
sqrt
(
int
v,
double
t)
假设函数的返回结果为
, 其中 开根号V是真实的值 , t 为给定的一个误差, 例如0.1 。
r
, 要求 r 要满足一定的误差条件, 用公式表达就是: 二 循环法
如果面试时候,面试官问你这个问题,加上一个限制:不能使用math.sqrt.
其实脑子第一反应就是脑子有病,为啥不用,实际工作中也没这样的需求啊,我用计算器也能算。但是还是得冷静下来。
想想面试官是为了考察啥。
要是卡住或者说不会就太尴尬了。
简单的思路就是让程序模拟大脑计算。比如我们习惯性的记住整数的,比如4的是开根号是2,9的开根号是3,很少有人去专门记忆2的是1.414.。。
所以
先用一个循环找到 r, 使得 r^2 是离给定 v 最近的平方数。用精度小的去作为step.
没循环一次,就累加一下步长。
其实是能算出来的,也能满足精度需求,就是不是最优解。因为毕竟暴力循环耗时长一些。
有个矛盾:要想精度高,步长就要小,步长小了,循环次数就多,所以有个均衡考虑。
推广思考下:如果有个业务场景,不断尝试推送消息给用户,用户不在线就收不到,消息的数量有很多。
所以你要每秒去推送,可能推送不过来。所以对于重试推送的,可以适当的调大时间间隔step.
30秒再试。30分钟再试,3小时再试。等等,几次都失败就废弃,认为不可达。
三 二分查找法:
二分搜索或者折半查找很多人在大学是学过的,数据结构就涉及这块。
当然注意一下边界条件,就不展开了,可以看下demo
当然能做到这一步,基本上面试就算过了。
四 牛顿迭代法
我是不懂的,数学学得一塌糊涂。从百科上给自己科普一下:https://baike.baidu.com/item/%E7%89%9B%E9%A1%BF%E8%BF%AD%E4%BB%A3%E6%B3%95
设r是
的根,选取
作为r的初始近似值,过点
做曲线
的切线L,L的方程为
,求出L与x轴交点的横坐标
,称x
1
为r的一次近似值。过点
做曲线
的切线,并求该切线与x轴交点的横坐标
,称
为r的二次近似值。重复以上过程,得r的近似值序列,其中,
称为r的
次近似值,上式称为
牛顿迭代公式
。
![](https://gss3.bdstatic.com/-Po3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D59/sign=5082ab3058afa40f38c6ced4aa6459d9/eaf81a4c510fd9f9f458496b272dd42a2834a460.jpg)
![](https://gss2.bdstatic.com/-fo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D15/sign=802826810d2442a7aa0ef9a0d04342de/6a63f6246b600c33993dc2ca184c510fd9f9a16f.jpg)
![](https://gss1.bdstatic.com/9vo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D71/sign=f2cbaa33a38b87d65442a91e0608b5bb/ac345982b2b7d0a24d231c9fc9ef76094a369acd.jpg)
![](https://gss3.bdstatic.com/7Po3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D59/sign=a638fa186259252da7171d0d359bf9a9/810a19d8bc3eb1354c3bafb9a41ea8d3fd1f4437.jpg)
![](https://gss0.bdstatic.com/94o3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D180/sign=bb2a367e530fd9f9a4175161152cd42b/00e93901213fb80e2b3e78d534d12f2eb83894c0.jpg)
![](https://gss3.bdstatic.com/-Po3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D113/sign=4759ab76e9f81a4c2232e8c8e42b6029/adaf2edda3cc7cd9c67a2ddb3b01213fb80e9158.jpg)
![](https://gss2.bdstatic.com/-fo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D71/sign=43308263f1deb48fff69a3dff11f38a8/c995d143ad4bd113cd3aaf3058afa40f4bfb0538.jpg)
![](https://gss3.bdstatic.com/7Po3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D59/sign=a638fa186259252da7171d0d359bf9a9/810a19d8bc3eb1354c3bafb9a41ea8d3fd1f4437.jpg)
![](https://gss2.bdstatic.com/9fo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D113/sign=265ea26696eef01f49141cc4d3ff99e0/71cf3bc79f3df8dc43d19493cf11728b461028c7.jpg)
![](https://gss2.bdstatic.com/-fo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D15/sign=4e7e68110bf79052eb1f433b0df3edbc/5d6034a85edf8db1a5c967660b23dd54574e74c8.jpg)
![](https://gss0.bdstatic.com/94o3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D129/sign=763132a7087b020808c93be35bd9f25f/6a63f6246b600c339f73c4ca184c510fd9f9a1ad.jpg)
![](https://gss2.bdstatic.com/-fo3dSag_xI4khGkpoWK1HF6hhy/baike/s%3D33/sign=670fbe609a22720e7fcee4f97acbeb58/5d6034a85edf8db1a5eb67660b23dd54574e74ee.jpg)
直接对应的开根号公式:
demo:
package com.daojia.math;
public class SquereTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
long t1 =System.currentTimeMillis();
double srt = Math.sqrt(100000000.0);
System.out.println("jdkuse:"+(System.currentTimeMillis()-t1)+":"+srt);
double jingdu = 0.001;
long t2 =System.currentTimeMillis();
double srt1 = sq1(100000000.0,0.001);
System.out.println("循环use:"+(System.currentTimeMillis()-t2)+":"+srt1);
System.out.println(Math.abs(srt1-srt)<jingdu);
long t3 =System.currentTimeMillis();
double srt2 = sq2(100000000.0,0.001);
System.out.println("二分法use:"+(System.currentTimeMillis()-t3)+":"+srt2);
System.out.println(Math.abs(srt2-srt)<jingdu);
long t4 =System.currentTimeMillis();
double srt3 = sq3(100000000.0,0.001);
System.out.println("牛顿法use:"+(System.currentTimeMillis()-t4)+":"+srt3);
System.out.println(Math.abs(srt3-srt)<jingdu);
}
/**
* 暴力循环法
* @param num
* @param jingdu
* @return
*/
public static double sq1(double num,double jingdu){
if(num<0)
{
return -1;
}
double step =jingdu/5;
System.out.println(step);
double res =0.000;
while(res*res<(num-jingdu))
{
res =res+step;
}
return res;
}
/*
* 二分法
*
*/
public static double sq2(double num,double jingdu){
if(num<0)
{
return -1;
}
double last =0.0000;
double low=0;
double mid;
double up=(num>=1?num:1);
mid=(low+up)/2;
do{
if(mid*mid>num)
{
up = mid;
}else{
low = mid;
}
last=mid;
mid=(up+low)/2;
}while(Math.abs(mid-last)>jingdu);
return mid;
}
/*
* 牛顿迭代法
*
*/
public static double sq3(double num,double jingdu){
if(num<0)
{
return -1;
}
double x=num,y=0.0;
while(Math.abs(x-y)>0.00001){
y=x;
x=0.5*(x+num/x);
}
return x;
}
}
jdkuse:0:10000.0
2.0E-4
循环use:147:10000.000011057207
true
二分法use:0:9999.999747378752
true
牛顿法use:0:10000.0
true
我们可以看出,现在计算机还是很快的,对于1亿,精度要求0.001的情况下。用了147毫秒。
二分法时间个jdk的math一样。但是结果不如牛顿迭代法的高。
因为jdk的math是直接调用了public static native double sqrt(double a);
这个native的方法时比Java更快的C语言编写的。我们不可见源码。但是从效果对比来看,牛顿迭代法显然是最优的选择。
************************************************************
还是懂数学的好,
底层还是博大精深。有太多的东西需要学习。