求整数N的开方,精度在0.001

二分法

若N大于1,则从[1, N]开始,low = 1, high = N, mid = low + (high - low) >> 1开始进行数值逼近
若N小于1,则从[N, 1]开始,low = 0, high = N, mid = low + (high - low) >> 1开始进行数值逼近
#include <stdio.h>
#include <stdlib.h>
#include <math.h>  #define ACCURACY 0.001  double newSqrt(double n)
{  double low, high, mid, tmp;  // 获取上下界  if (n > 1)   {  low = 1;  high = n;  } else {  low = n;  high = 1;  }  // 二分法求开方  while (low <= high) {  mid = (low + high) / 2.000;  tmp = mid * mid;  if (tmp - n <= ACCURACY && tmp -n >= ACCURACY * -1) {  return mid;  } else if (tmp > n) {  high = mid;  } else {  low = mid;  }  }  return -1.000;
}  int main(void)
{  double n, res;  while (scanf("%lf", &n) != EOF) {  res = newSqrt(n);  printf("%lf\n", res);  }  return 0;
}  

牛顿迭代法思路求解:

#include<iostream>
using namespace std;
int main()
{int N;cout<<"输入N的值:";cin>>Ndouble x1 = 1;//初值double x2 = x1/2.0+N/2.0/x1;while( fabs(x2-x1)>0.001){x1 = x2;x2 = x1/2.0+N/2.0/x1;}cout<<x1<<endl;return 0;
}

整数开方定义

所谓整数平方根即

算法

算法1.猜试法

利用等差级数公式:

  
  这样的话, 从1开始一直算到数列的前项和第一次大于x的时候,即是所求。下面给出source code(C):


unsigned linear_search(unsigned long x)
{
    unsigned long sum_n = 1;
    unsigned n = 1;

if(x <= 1)
    {
        return x;
    }

while(sum_n <= x)
    {
        n++;
        sum_n += (n<<1) - 1;
    }

return (n-1);

}

这种方法无异于穷举法,其唯一的优点是:每次的迭代用到了前面迭代的结果,所以会有一些效率的增益。对于该算法的改进就是不穷举,改用我们熟悉的二分查找法来做。(二分逼近法)


unsigned bi_search(unsigned long x)
{
    unsigned long sum_n = 0;
    unsigned n = (x >> 1);
    unsigned top = x;
    unsigned bottom = 0;

if (x <= 1)
    {
        return x;
    }
    for (;;)
    {
        sum_n = n * n;
        if (sum_n < x)
        {
          bottom = n;
          n += ((top - bottom) >> 1);
          if (n == bottom)
              return n;
        }
        else 
        if (sum_n > x)
        {
          top = n;
          n -= ((top - bottom) >>1);
          if (n == top)
              return n-1;
        }
        else
        {
            return n;
        }
    }
}

算法2 Newton 法

把这个问题转换为方程求根问题,即:,求x。

而方程求根的问题可以用Newton 法来解决。现在的问题有一点不同,即所求的根必须是整数。通过证明,我们可以发现,Newton迭代公式是适用于整数情况的,于是有:


至于是怎么证明的,可以参考hacker’s delight

另外,初值的选择也是很重要的一环,这里我们选择大于等于的最小的2的幂次数。

OK,下面给出程序:


unsigned newton_method(unsigned long x)
{
    unsigned long x1 = x - 1;
    unsigned s = 1;
    unsigned g0,g1;

/* 初值设定  通常将初始值设为1,但是只有1的开方才会是1,通过预处理找到更精确地初始值a[n]*/  
    if (x1 > 65535) {s += 8; x1 >>= 16;}
    if (x1 > 255)   {s += 4; x1 >>= 8;}
    if (x1 > 15)    {s += 2; x1 >>= 4;}
    if (x1 > 3)     {s += 1; x1 >>= 2;}

/*迭代*/
    g0 = 1 << s;
    g1 = (g0 + (x >> s)) >> 1;
    while(g1 < g0)
    {
       g0 = g1;
       g1 = (g0 + x/g0) >> 1;
    }
    return g0;
}

算法3 逐比特确认法

逐比特确认法认为一个32位整数求根,结果应该是一个16位整数。求这个16位整数,其实质是确认每位的比特是0还是1.我们把这个根分为两个相加的部分,一部分是已确认的值,另一部分是未确认的值。从高位到低位,每次迭代确认一位。初始时,已确认部分为0。则问题的初始形式为:

   
  算法发明者为:James Ulery  论文:Computing Integer Square Roots

下面给出源代码:


unsigned bitwise_verification(unsigned long x)
{

unsigned long temp = 0;
   unsigned v_bit = 15;
   unsigned n = 0;
   unsigned b = 0x8000;

if (x <= 1)
       return x;

do{
       temp = ((n << 1) + b) << (v_bit--);
       if (x >= temp)
       {
           n += b;
           x -= temp;
       }
   }while (b >>= 1);
   
   return n; 
}

性能比较

在0~1000000范围内对四种算法进行了遍历性的测试,得到测试结果:

  
  显见四种算法的遍历性能以逐比特确认法为最好,逐比特确认法从本质上来说是一种二分查找法,而且其查找范围为整个16位整数域;而我们实现的二分查找法的查找范围到已知变量为止,从范围上来说比逐比特确认法来得小,但是最后平均性能却不及逐比特确认法。其原因在于:逐比特确认法把问题分解为相同的子问题的集合,采用递推的方法,很好地利用了前面步骤的结果,不用什么都从头算起,从而避免了重复劳动,用加减法和移位操作代替了乘除法操作,最终获得了性能上的增益。

需要注意的是,虽然平均性能有如此的关系。并不代表每个数或每组数都有这样的关系。实际上,我们每组产生1000个随机数,并对每组的算法性能进行了测试,各个算法都有获得优胜的时候。至于具体是什么场合用什么算法,需要分析和经验的支撑。目前,我所能归纳出的概要指导准则为:

(1)在大多数情况下,牛顿迭代都能获得不错的性能,

(2)逐比特确认法更适合运算数比较大的场合。

平方根手写算法

      此文算是为大整数开根做好理论准备
     1>    曾经写过迭代求平方根的算法,很是简单。假设求x的平方根,那么可以假设两个非常逼近的数a[n]和a[n+1],它们的平方都非常接近x:
     a[n]*a[n]≈a[n]*a[n+1]≈a[n+1]*a[n+1]≈x;   (1)
     2a[n+1]*a[n]≈a[n]*a[n]+x;                           (2)
     a[n+1]=0.5*(a[n]+x/a[n]);                            (3)
因此求x的平方根,只要有一个初始值,然后用式(3)无限迭代下去就可以就算出足够精度的开方值。迭代求平方根的算法对于浮点数64位范围内的数计算是比较准确且迅速的,C函数库math.h中的sqrt函数貌似就是使用这种算法计算平方根的。
       上述迭代求平方根的算法能够应用到大整数的开根中呢。理论上是没有任何问题的,实际中也是有人就这么做的,并且貌似还很开心的AC了。但使用此算法求大整数开根的话,要实现大整数加法和除法等基本运算,想想还是放弃了。
   2>    接下来介绍一种手动模拟求平方根算法。非常巧妙,非常神奇。可能自己智商有点问题哈,仔细琢磨了几个小时才彻底明白。先仔细介绍如何使用这种算法模拟求平方根,然后再简要说一说它的原理吧!
  (a)首先将要开方根的数从小数点分别向右及向左每两个位一组分开,如98765.432内小数点前的65是一组,87是一组,9是一组,小数点后的43是一组,之后是单独的一个2,要补一个0而得20是一组。
  (b)将最左一组的数减去最接近又不大于它的平方数,并将该平方数的开方(应该是个位数)记下。
  (c) 将上一步所得之差乘以100,和下一组数加起来。
  (d)将记下的数乘以20,然后将它加上某个个位数,再乘以这个个位数,令这个积不大于又最接近上一步所得之差,并将该个位数记下,且将上一步所得之差减去所得之积。
  (e)记下的数依次隔两位记下。
  (f)重复第3步,直到找到答案。
   (g) 可以在数字的最右补上多组的00,以求得理想的精确度未止。
   

原理网上说是(a+b)^2 = a^2 + b^2 + 2ab = a^2 + (2a+b)*b。并没有完全看明白,这里就不班门弄斧了,等以后搞清楚,再补上吧!

/* 大整数开方 ,模拟手工开方*/
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
# include <math.h>
# include <time.h># define MAXN 1001void bigN_sqrt(char *s);
int  bigN_cmp(char *a, char *b, int lim);
void bigN_mul(char *a, int k, int lim);
void bigN_add(char *a, int k);
void bigNN_minus(char *a, char *b, int lim);
int  Newtonsqrt(double x);  //牛顿迭代可求求64位数的平方根
char str[MAXN];int main()
{freopen("hugeint.in", "r", stdin);freopen("hugeint.out", "w", stdout);while (~scanf("%s", str))bigN_sqrt(str);//    printf("time cost %.3lfs.\n", (double)clock()/CLOCKS_PER_SEC);return 0;
}int bigN_cmp(char *a, char *b, int lim)
{int i;for (i = lim-1; i >= 0; --i)if (a[i] < b[i]) return 1;else if (a[i] > b[i]) return -1;return 0;
}
void bigN_mul(char *a, int k, int lim)
{int i, tmp, c;for (c=i=0; i < lim; ++i) {tmp = a[i]*k + c;c = tmp / 10;a[i] = tmp - 10*c;}
}
void bigN_add(char *a, int k)
{int i = 0;while (k > 0) {a[i++] += k%10;k /= 10;}
}
void bigNN_minus(char *a, char *b, int lim) // b = b - a;
{int i, tmp, c;for (c=i=0; i < lim; ++i) {tmp = b[i] - a[i] + c;c = (tmp<0 ? -1:0);b[i] = (tmp+10) % 10;}
}int Newtonsqrt(double x)
{double x1 = 1;//初值double x2 = x1/2.0+x/2.0/x1;while( fabs(x2-x1)>0.1){x1 = x2;x2 = x1/2.0+x/2.0/x1;}return floor(x1);
}
void bigN_sqrt(char *s)
{short int i, k, slen;           // 根的一个十进制位char res[MAXN];                 // 试方余数char cur[MAXN];                 // 试方上限char tmp[MAXN];int lim;memset(res, 0, sizeof(res));memset(cur, 0, sizeof(cur));lim = slen = strlen(s);if (slen < 18) {               //非大整数,直接调用sqrt()计算平方根,结束 。sqrt()计算平方根并非完全正确,在测试的过程中// sqrt(8456552264) =  91960 ,而实际上 8456552264的平方根是91959 ,因此采用Newton迭代法求解 //    printf("%.0lf\n", sqrt(atof(s)));//    printf("%.0lf\n", sqrt(8456552264));double value=atof(s);printf("%d",Newtonsqrt(value));return ;}if (slen & 0x1) {k = -1;cur[0] = s[0] - 48;} else {k = 0;cur[1] = s[0] - 48;cur[0] = s[1] - 48;}while (1) {i = 0;while (1) {++i;memcpy(tmp, res, MAXN);bigN_mul(tmp, i*20, lim);bigN_add(tmp, i*i);if (-1 == bigN_cmp(tmp, cur, lim)) break;     // break until tmp > cur;}--i;printf("%d", i);memcpy(tmp, res, MAXN);     //cur -= res*i*20+i*i;bigN_mul(tmp, i*20, lim);bigN_add(tmp, i*i);bigNN_minus(tmp, cur, lim);bigN_mul(res, 10, lim);        // res = res*10+i;bigN_add(res, i);k += 2;if (k >= slen) break;else {bigN_mul(cur, 100, lim);bigN_add(cur, ((s[k]-48)*10+(s[k+1]-48)));}}printf("\n");
}

整数平方根:整数开方及大整数开方解决方法相关推荐

  1. 应用程序错误电脑黑屏_电脑黑屏了怎么办,电脑硬件win黑屏的6大原因以及解决方法...

    Windows出现黑屏的情况,其实可以说是十分经典的一个电脑问题了,相信有很多朋友都遇到过.而Windows黑屏的情况,除了系统软件导致之外,还有可能是电脑硬件问题导致的,也会比较难解决.而小编也整理 ...

  2. vnc远程登录linux黑屏,linux VNC安装好后登录黑屏 鼠标显示黑色大叉的解决方法

    linux VNC安装好以后用VNC登录黑屏,鼠标显示黑色大叉的解决方法 这里的方法选择执行就可以了,我这里是执行了第二句,第三句就可以了,原因是系统在安装的时候没有安装桌面元素包 linux VNC ...

  3. 交换机组最常见的8大故障及解决方法

    在交换机组网时常见的故障比较多,为了帮助大家更好地排除这些故障,在此飞畅科技的小编来为大家介绍一些常见的典型故障案例和处理思路供大家参考.接下来大家就一起来看看交换机组最常见的8大故障及解决方法吧. ...

  4. git提交大文件的解决方法

    git提交大文件的解决方法 参考文章: (1)git提交大文件的解决方法 (2)https://www.cnblogs.com/perryxiong/p/5606525.html 备忘一下.

  5. Python xlrd 读取Excel数字 数字丢失精度 小数位太长 求大神的解决方法(已解决)

    Python xlrd 读取Excel数字 跟Excel显示不一样 小数位太长 求大神的解决方法(已解决) 十分想念顺店杂可... Excel显示的数据 Python代码读取 # -*- coding ...

  6. 局域网中最常见的十大错误及解决方法

    局域网中最常见的十大错误及解决方法 网络故障是在所难免的,重要的是应快速隔离和排除故障.网络维护人员应该配备相应的工具和相应的知识,以便及时.有效的找到和解决问题.本文讨论了网络技术人员和工程师经常遇 ...

  7. java大整数的加减乘除_关于大整数的加减乘除求余运算 java

    自己用java 实现了大字符串整数的加减乘除和求余运算, 加减法的算法是模拟手工笔算的计算过程, 除法就是从最高位不停的减操作, 乘法的算法 :遍历一个数的各个位数以及他所在位置,另一个数根据这个数的 ...

  8. java 超大整数加法,JAVA实现的大整数加法

    JAVA实现的大整数加法 public class Test{ public static String addBigNum(String str1,String str2){ //找出两字符串的长短 ...

  9. ASP无法上传大文件的解决方法

    一.错误提示--ASP 0104 : 80004005 (1)ASP 0104 : 80004005 问题:WIN2003无法上传较大的文件"Request 对象 错误 \'ASP 0104 ...

最新文章

  1. 第10章 嵌入式linux的调试技术
  2. java自学难点_java学习重难点
  3. PHP中empty,is_null,isset的区别
  4. 关于完美拖拽的问题三
  5. PHP调用数据库数据乱码问题
  6. Asp.Net Core使用Skywalking实现分布式链路追踪
  7. [置顶] Z-STACK之OSAL_Nv非易失性存储解读上
  8. python表达式的值是 y 和n是什么意思_python中^是什么意思
  9. qtableview与sqlite使用显示科学计算_使用MATLAB Profiler提升程序运行的效率
  10. Spring-基础设施类
  11. 没想到!谷歌排名第一的编程语言,这样的学的话,更容易成为高手!
  12. 网页监控系统设计之使用mjpg-streamer
  13. 【信息学奥赛一本通】网址链接
  14. javaee之用户信息增删改查案例
  15. 酷Q机器人,QQ机器人使用教程
  16. 计算机启动显示不正确的分区表,双硬盘启动失败提示“无效分区表”无法打开机器...
  17. 如何正确使用 Mac 电脑的“恢复模式”?
  18. VulnHub靶场系列:Flick
  19. 测试学习小结:测试的7种分类
  20. 江西省信息技术知识竞赛

热门文章

  1. 如何用html制作移动app,html移动开发app
  2. js+css制作导航栏下划线跟随动画,App+H5点击效果
  3. JavaWeb:HTML
  4. 神奇的月食画面 超级血月出现天文迷大兴奋
  5. 把 14 亿人都拉到一个微信群,在技术上能实现吗?
  6. 计算机毕设(附源码)JAVA-SSM绩效考核管理系统
  7. 好物推荐:2020 年常用软件锦集!帮你解决 90% 的软件问题
  8. 双子天蝎,爱情是不老的传说
  9. tdm的应用计算机,2021计算机考研备考知识:TDM时分复用技术
  10. ubuntu自动同步时间