完全平方数的判定及整数平方根的快速求解

2024-05-04 23:18

本文主要是介绍完全平方数的判定及整数平方根的快速求解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在上个星期的“有道难题”网络预赛中,某些Group的第二题涉及到了完全平方数的判定问题。相信大多数人的判定语句都差不多是这样写的:if(sqr((int)sqrtl(n))==n){...},把这条语句整理一下,大概是这样:

inline __int64 sqr( int n){ return  static_cast<__int64>(n)*n;} 

inline  int directSqrt( int n) 

     int root =  static_cast< int>(sqrtl(n)); //求n的开方,但是double类型的
     if (sqr(root+ 1) <= n) 
     { 
         return root +  1
     } 
     else  return root; 


inline  bool isSquareNumber( int n) 

   int root = directSqrt(n); 
   return sqr(root) == n; 
}

这个判定方法主要是利用标准库的sqrt函数来求整数平方根(integer square root),正确性不用怀疑,效率也足够。但是如果想要更快呢?通常来说,要判定任意一个数是否是完全平方数,求根的过程在所难免。但是,标准库的sqrt函数是求浮点平方根的,而我们只需要获取整数平方根即可,所以在这里肯定还有性能提升的空间。于是接下来就列举一些计算整数平方根的方法(后两个算法不是我写的),按照计算速度从慢到快依次排列,其中最后一个算法的效率大概是上述直接利用sqrtl的方法的四倍。

1. 线性搜索,直接从小到大依次取数试探,最原始的办法,代码如下:

inline  int linearSqrt( int n) 

     int i =  0
     while (sqr(i) <= n) ++i; 
     return i- 1
}


2. 二分法寻找根

inline  int bisectionSqrt( int n) 

     int l =  0, r = n/ 2 +  1
     while (r - l >  1
     { 
         int m = (l+r)/ 2
         __int64 sqrm = sqr(m); 
         if (sqrm == n) 
         { 
             return m; 
         } 
         else  if (sqrm > n) r = m; 
         else l = m; 
     } 
     
     if (sqr(r) == n) 
     { 
         return r; 
     } 
     else  return l; 
}

3. 牛顿迭代法,令方程 x^2 - n = 0,则求sqrt(n)也即是求这个方程的根,根据牛顿法的迭代公式有:x' = x - (x^2-n)/(2x) = (x^2+n)/(2x),选定合适的初始值迭代,很快就能收敛到根。另外,这个迭代其实也就是不动点迭代的松弛形式,由 x^2= n => x = n/x, 得不动点迭代x' = n/x,加上松弛系数1/2(算数平均),得x'= 1/2 * (x + n/x),即 x' = (x^2+n)/(2x),与前面牛顿法的迭代公式相同。算法的代码如下:

inline  int newtonSqrt( int n) 

     static  int table[ 16] = { 
         11<< 21<< 41<< 61<< 81<< 101<< 121<< 141<< 16
         1<< 181<< 201<< 221<< 241<< 261<< 281<< 30
     }; 
     unsigned i =  0, x0 =  0, x1 =  0
     while (table[i] < n && i!= 16) ++i; x0 =  1 << i; //寻找迭代的初识值
     while (x0 !=  0
     {     
         x1 = static_cast< int>((sqr(x0) + n)/ ( 2*x0)); 
         if (x1 >= x0) 
         { 
             break
         } 
         x0 = x1; 
     } 
     return x0; 
}

4. 逐位确定法,从高到低依次确定根的各个二进制位。见下面这个代码,看起来似乎很飘逸,但实际上算法很简单明晰,具体的解释可以搜索James Ulery的论文:"Computing Integer Square Roots".

inline  int fastIntSqrt( int n) 

     int temp, nHat =  0, b =  0x8000, bshft =  15
     do 
     { 
         if (n >= (temp = (((nHat<< 1)+b)<<bshft--))) 
         { 
             nHat += b; 
             n -= temp; 
         } 
     }  while (b >>=  1); 
     return nHat; 
}

5.建表+牛顿迭代,这个算法号称是要用来替代java数学包中的(int)(java.lang.Math.sqrt(integer))函数的,速度奇快,首先建立了一张强大的表来确定初值,然后最多经过两次牛顿迭代后就得到精确解。

inline  int javaIntSqrt( int n) 

     static  int table[ 256] = { 
         0,     16,   22,   27,   32,   35,   39,   42,   45,   48,   50,   53,   55,   57
         59,    61,   64,   65,   67,   69,   71,   73,   75,   76,   78,   80,   81,   83
         84,    86,   87,   89,   90,   91,   93,   94,   96,   97,   98,   99101102
         103104106107108109110112113114115116117118
         119120121122123124125126128128129130131132
         133134135136137138139140141142143144144145
         146147148149150150151152153154155155156157
         158159160160161162163163164165166167167168
         169170170171172173173174175176176177178178
         179180181181182183183184185185186187187188
         189189190191192192193193194195195196197197
         198199199200201201202203203204204205206206
         207208208209209210211211212212213214214215
         215216217217218218219219220221221222222223
         224224225225226226227227228229229230230231
         231232232233234234235235236236237237238238
         239240240241241242242243243244244245245246
         246247247248248249249250250251251252252253
         253254254255 
     }; 

     int xn; 
     if (n >=  0x7FFEA810return  0xB504
     if (n >=  0x10000) { 
         if (n >=  0x1000000) { 
             if (n >=  0x10000000) { 
                 if (n >=  0x40000000) { 
                     xn = table[n>>  24] <<  8
                 }  else { 
                     xn = table[n>>  22] <<  7
                 } 
             }  else { 
                 if (n>=  0x4000000) { 
                     xn = table[n>>  20] <<  6
                 }  else { 
                     xn = table[n>>  18] <<  5
                 } 
             } 

             xn = (xn +  1 + (n/ xn)) >>  1
             xn = (xn +  1 + (n/ xn)) >>  1
             return ((xn * xn) > n) ? --xn : xn; 
         }  else { 
             if (n>=  0x100000) { 
                 if (n>=  0x400000) { 
                     xn = table[n>>  16] <<  4
                 }  else { 
                     xn = table[n>>  14] <<  3
                 } 
             }  else { 
                 if (n>=  0x40000) { 
                     xn = table[n>>  12] <<  2
                 }  else { 
                     xn = table[n>>  10] <<  1
                 } 
             } 

             xn = (xn +  1 + (n/ xn)) >>  1
             return ((xn * xn) > n) ? --xn : xn; 
         } 
     }  else { 
         if (n>=  0x100) { 
             if (n>=  0x1000) { 
                 if (n>=  0x4000) { 
                     xn = (table[n>>  8]) +  1
                 }  else { 
                     xn = (table[n>>  6] >>  1) +  1
                 } 
             }  else { 
                 if (n>=  0x400) { 
                     xn = (table[n>>  4] >>  2) +  1
                 }  else { 
                     xn = (table[n>>  2] >>  3) +  1
                 } 
             } 
             return ((xn * xn) > n) ? --xn : xn; 
         }  else { 
             if (n>=  0) { 
                 return table[n] >>  4
             } 
         } 
     } 
     return - 1
}

依次计算从1到1000000的每个数的整数平方根,下面分别是上面6个算法所用的时间:

0. 利用标准库sqrtl, directSqrt : 610 ms
1. 线性搜索, linearSqrt : 81256 ms
2. 二分法, bisectionSqrt : 2734 ms
3. 牛顿迭代法, newtonSqrt : 691 ms
4. 逐位确定法, fastIntSqrt : 301 ms
5. 建表+牛顿迭代, javaIntSqrt : 150 ms

可见牛顿迭代法与利用标准库sqrtl的方法速度相当,逐位确定法的速度大概是牛顿迭代法的两倍。当然,最快的还是建表+牛顿迭代,看来以其来替代(int)(java.lang.Math.sqrt(integer))也不是没有道理的。另,以上测试数据相对来说都比较小,可以想象,若是计算接近于2^31这样的大数的整数平方根,逐位确定法和建表+牛顿迭代应该更占据优势。

这篇关于完全平方数的判定及整数平方根的快速求解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/960313

相关文章

numpy求解线性代数相关问题

《numpy求解线性代数相关问题》本文主要介绍了numpy求解线性代数相关问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 在numpy中有numpy.array类型和numpy.mat类型,前者是数组类型,后者是矩阵类型。数组

shell脚本快速检查192.168.1网段ip是否在用的方法

《shell脚本快速检查192.168.1网段ip是否在用的方法》该Shell脚本通过并发ping命令检查192.168.1网段中哪些IP地址正在使用,脚本定义了网络段、超时时间和并行扫描数量,并使用... 目录脚本:检查 192.168.1 网段 IP 是否在用脚本说明使用方法示例输出优化建议总结检查 1

Rust中的Option枚举快速入门教程

《Rust中的Option枚举快速入门教程》Rust中的Option枚举用于表示可能不存在的值,提供了多种方法来处理这些值,避免了空指针异常,文章介绍了Option的定义、常见方法、使用场景以及注意事... 目录引言Option介绍Option的常见方法Option使用场景场景一:函数返回可能不存在的值场景

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C

PTA求一批整数中出现最多的个位数字

作者 徐镜春 单位 浙江大学 给定一批整数,分析每个整数的每一位数字,求出现次数最多的个位数字。例如给定3个整数1234、2345、3456,其中出现最多次数的数字是3和4,均出现了3次。 输入格式: 输入在第1行中给出正整数N(≤1000),在第二行中给出N个不超过整型范围的非负整数,数字间以空格分隔。 输出格式: 在一行中按格式“M: n1 n2 ...”输出,其中M是最大次数,n

poj 1127 线段相交的判定

题意: 有n根木棍,每根的端点坐标分别是 px, py, qx, qy。 判断每对木棍是否相连,当他们之间有公共点时,就认为他们相连。 并且通过相连的木棍相连的木棍也是相连的。 解析: 线段相交的判定。 首先,模板中的线段相交是不判端点的,所以要加一个端点在直线上的判定; 然后,端点在直线上的判定这个函数是不判定两个端点是同一个端点的情况的,所以要加是否端点相等的判断。 最后

hdu 4565 推倒公式+矩阵快速幂

题意 求下式的值: Sn=⌈ (a+b√)n⌉%m S_n = \lceil\ (a + \sqrt{b}) ^ n \rceil\% m 其中: 0<a,m<215 0< a, m < 2^{15} 0<b,n<231 0 < b, n < 2^{31} (a−1)2<b<a2 (a-1)^2< b < a^2 解析 令: An=(a+b√)n A_n = (a +

整数Hash散列总结

方法:    step1  :线性探测  step2 散列   当 h(k)位置已经存储有元素的时候,依次探查(h(k)+i) mod S, i=1,2,3…,直到找到空的存储单元为止。其中,S为 数组长度。 HDU 1496   a*x1^2+b*x2^2+c*x3^2+d*x4^2=0 。 x在 [-100,100] 解的个数  const int MaxN = 3000

HDU 2159 二维完全背包

FATE 最近xhd正在玩一款叫做FATE的游戏,为了得到极品装备,xhd在不停的杀怪做任务。久而久之xhd开始对杀怪产生的厌恶感,但又不得不通过杀怪来升完这最后一级。现在的问题是,xhd升掉最后一级还需n的经验值,xhd还留有m的忍耐度,每杀一个怪xhd会得到相应的经验,并减掉相应的忍耐度。当忍耐度降到0或者0以下时,xhd就不会玩这游戏。xhd还说了他最多只杀s只怪。请问他能

v0.dev快速开发

探索v0.dev:次世代开发者之利器 今之技艺日新月异,开发者之工具亦随之进步不辍。v0.dev者,新兴之开发者利器也,迅速引起众多开发者之瞩目。本文将引汝探究v0.dev之基本功能与优势,助汝速速上手,提升开发之效率。 何谓v0.dev? v0.dev者,现代化之开发者工具也,旨在简化并加速软件开发之过程。其集多种功能于一体,助开发者高效编写、测试及部署代码。无论汝为前端开发者、后端开发者