利用移位、加减法实现整数开平方算法的方法(转)

利用移位、加减法实现整数开平方算法的方法(转)

本算法只采用移位、加减法、判断和循环实现,因为它不需要浮点运算,也不需要乘除运算,因此可以很方便地运用到各种芯片上去。

我们先来看看10进制下是如何手工计算开方的。
先看下面两个算式,
x = 10*p + q  (1)
公式(1)左右平方之后得:
x^2 = 100*p^2 + 20pq + q^2 (2)
现在假设我们知道x^2和p,希望求出q来,求出了q也就求出了x^2的开方x了。
我们把公式(2)改写为如下格式:
q = (x^2 - 100*p^2)/(20*p+q) (3)

这个算式左右都有q,因此无法直接计算出q来,因此手工的开方算法和手工除法算法一样有一步需要猜值。

我们来一个手工计算的例子:计算1234567890的开方

首先我们把这个数两位两位一组分开,计算出最高位为3。也就是(3)中的p,最下面一行的334为余数,也就是公式(3)中的(x^2 - 100*p^2)近似值
    3
  ---------------
 / 12 34 56 78 90
    9
  ---------------
 /  3 34

下面我们要找到一个0-9的数q使它最接近满足公式(3)。我们先把p乘以20写在334左边:
                           3  q
                         ---------------
                        / 12 34 56 78 90
                           9
                         ---------------
(20*3+q)*q      /  3 34

我们看到q为5时(60+q)*q的值最接近334,而且不超过334。于是我们得到:
      3  5
    ---------------
   / 12 34 56 78 90
      9
    ---------------
65 /  3 34
      3 25
    ---------------
         9 56

接下来就是重复上面的步骤了,这里就不再啰嗦了。

这个手工算法其实和10进制关系不大,因此我们可以很容易的把它改为二进制,改为二进制之后,公式(3)就变成了:
q = (x^2 - 4*p^2)/(4*p+q) (4)

我们来看一个例子,计算100(二进制1100100)的开方:
       1  0  1  0
      -----------
     / 1 10 01 00
       1
      -----------
 100 / 0 10 
       0 00 
      -----------
1001 /   10 01
         10 01
      -----------
          0 00

这里每一步不再是把p乘以20了,而是把p乘以4,也就是把p右移两位,而由于q的值只能为0或者1,所以我们只需要判断余数(x^2 - 4*p^2)和(4*p+1)的大小关系,如果余数大于等于(4*p+q)那么该上一个1,否则该上一个0。

下面给出完成的C语言程序,其中root表示p,rem表示每步计算之后的余数,divisor表示(4*p+1),通过a>>30取a的最高 2位,通过a<<=2将计算后的最高2位剔除。其中root的两次<<1相当于4*p。程序完全是按照手工计算改写的,应该不难理解。
unsigned short sqrt(unsigned long a){
  unsigned long rem = 0;
  unsigned long root = 0;
  unsigned long divisor = 0;
  for(int i=0; i<16; ++i){
    root <<= 1;
    rem = ((rem << 2) + (a >> 30));
    a <<= 2;
    divisor = (root<<1) + 1;
    if(divisor <= rem){
      rem -= divisor;
      root++;
    }
  }
  return (unsigned short)(root);
}

posted on 2008-01-23 14:21 QUIRE-0216 阅读(2127) 评论(1)  编辑 收藏 引用 所属分类: Arithmetic(算法)

Feedback

# re: 利用移位、加减法实现整数开平方算法的方法(转) 2008-01-23 14:46 QUIRE-0216

为了大家能理解我把上面 1234567890 给做完! 
              3 5 q 
               --------------- 
               / 12 34 56 78 90 
               9 
               --------------- 
               65 / 3 34 
               3 25 
               --------------- 
(20*35+q)*q /  9 56 
我们看到q为1时(700+q)*q的值最接近956,而且不超过956。于是我们得到: 
              3 5 1 q 
               --------------- 
               / 12 34 56 78 90 
               9 
               --------------- 
               65 / 3 34 
               3 25 
               --------------- 
701 /   9 56 
7 01 
---------------- 
(20*351+q)*q / 2 55 78

我们看到q为3时(20*351+q)*q的值最接近25578,而且不超过25578。于是我们得到:

              3 5 1 3 q 
               --------------- 
               / 12 34 56 78 90 
               9 
               --------------- 
               65 / 3 34 
               3 25 
               --------------- 
701 /   9 56 
7 01 
---------------- 
7023 / 2 55 78 
2 10 69 
---------------- 
(20*3513+q)*q / 45 0990

我们看到q为6时(20*3513+q)*q的值最接近450990,而且不超过450990。于是我们得到: 
              3 5 1 3 6 
               --------------- 
               / 12 34 56 78 90 
               9 
               --------------- 
               65 / 3 34 
               3 25 
               --------------- 
701 /   9 56 
7 01 
---------------- 
7023 / 2 55 78 
2 10 69 
---------------- 
70266 / 45 0990 
42 1596 
---------------- 
2 9394

至此1234567890的根为35136.我想能看明白吧!

时间: 2024-11-05 12:33:53

利用移位、加减法实现整数开平方算法的方法(转)的相关文章

COJ 1211 大整数开平方

手写求大整数开根号所得到的值,具体计算过程参考别人的资料,最后利用java的大整数得到答案 别人博客链接:http://www.cnblogs.com/Rinyo/archive/2012/12/16/2820450.html 1.举例 上式意为65536的开平方为256.手开方过程类似于除法计算.为了方便表述,以下仍称类似位置的数为“被除数”.“除数”.“商”. 以65536为例,其具体计算过程如下: Step1:将被开方数(为了形象,表述成“被除数”,此例中即为65536)从个位往高位每两位

无限大整数相加算法的C语言源代码

忙里偷闲,终于完成了无限大整数相加算法的C语言代码,无限大整数相加算法的算法分析在这里. 500位的加法运行1000次,不打印结果的情况下耗时0.036秒,打印结果的情况下耗时16.285秒. 下面是源码: #include <stdio.h> #include <stdlib.h> #include<string.h> #include <time.h> #define MAXNUM 1000000000000000000 /* 存储数据用的结构 long

更快的求整数幂算法

相信整数幂运算作为一个算法演变的例子是再合适不过的了为了节省访客们宝贵的学习时间省去介绍递归等可能涉及到的初级概念的定义.同时如果发现文中有错误的地方请敞开衣服指正. 因为在测试性能时合适的测试数据是必要的,所以本文用C++的大数类进行演示. 点击获取C++大数类源码 这里我们先列一下会提到的算法分析技术: 动态规划 减治法 测试平台: Linux g++ 4.7 原始递归方法 这就不花时间赘述什么了. BigInteger pow(BigInteger x, int N) { if (N ==

2.5 整数和算法

2.5.1 引言 正如2.1节所说, 算法这一术语最初指的是用整数的十进制法表示的用法进行算术运算的过程.修改后能处理二进制表示的这些算法是计算机算术的基础.这些算法为理解算法这一概念及算法复杂度提供了很好的实例.因此本书将讨论这些算法. 除算术中常用的整数算法以外,还有许多涉及整数的算法,包括欧里几德算法,这是最有用的算法之一,很可能是数学中最古老的算法.我们还将描述一个算法,用于对任意基数b求正整数的b进制展开和求同余幂,在密码学中这是个重要的算法. 2.5.2 整数的表示 日常生活中都用十

高效率开平方算法

1 UINT sqrt(long m) 2 { 3 4 BYTE i,j; 5 UINT z,temp0,temp1; 6 long x,y; 7 x = 1; 8 9 for(j =0;j<16;j++) 10 { 11 x <<= 2; 12 if(m<x) 13 break; 14 } 15 x = y =1; 16 x <<=2*j; 17 y = x<<1; 18 19 z = 0; 20 temp0 = 0; 21 for(i=0;i<=j

C 利用移位运算符 把十进制转换成二进制

#include <stdio.h> int main(void){ //利用移位运算符 把十进制转换成二进制 int c; printf("输入数字:");//8 scanf("%d",&c); //最高位和次高位都要移动到最低位 //补码:00 0000 1000 //逻辑右移>>:00 0000 1000 //printf("%d",8<<2);return; int i = sizeof(c)*

sqrt开平方算法的尝试,是的看了卡马克大叔的代码,我来试试用C#写个0x5f3759df和0x5f375a86跟System.Math.Sqrt到底哪个更强

今天笔试遇到一个代码题,要求写一个开平方算法,回来发现了雷神之锤里的一段神代码: 1 float Q_rsqrt( float number ) 2 { 3 long i; 4 float x2, y; 5 const float threehalfs = 1.5F; 6 x2 = number * 0.5F; 7 y = number; 8 i = * ( long * ) &y; // evil floating point bit level hacking 9 i = 0x5f3759d

【转载】算法设计之五大常用算法设计方法总结

转载自http://blog.csdn.net/zolalad/article/details/11393915 算法设计之五大常用算法设计方法总结 一.[分治法]  在计算机科学中,分治法是一种很重要的算法.字面上的解释是"分而治之",就是把一个复杂的问题分成两个或更多的相同或相似的子问题,再把子问题分成更小的子问题--直到最后子问题可以简单的直接求解,原问题的解即子问题的解的合并.这个技巧是很多高效算法的基础,如排序算法(快速排序,归并排序),傅立叶变换(快速傅立叶变换)--等.任

算法设计方法:递归的内涵与经典应用

摘要: 大师 L. Peter Deutsch 说过:To Iterate is Human, to Recurse, Divine.中文译为:人理解迭代,神理解递归.毋庸置疑地,递归确实是一个奇妙的思维方式.对一些简单的递归问题,我们总是惊叹于递归描述问题的能力和编写代码的简洁,但要想真正领悟递归的精髓.灵活地运用递归思想来解决问题却并不是一件容易的事情.本文剖析了递归的思想内涵,分析了递归与循环的联系与区别,给出了递归的应用场景和一些典型应用,并利用递归和非递归的方式解决了包括阶乘.斐波那契