在 F# 编程语言中,处理大整数时,经常会遇到需要计算其平方根的情况。尽管 .NET 框架为大整数提供了支持,但遗憾的是它并没有内置计算大整数平方根的函数。因此,决定自己实现一个这样的函数。这个函数接受一个 BigInteger
类型的参数,并返回一个 BigInteger
类型的结果。在 F# 中,这意味着函数的签名是 BigInteger -> BigInteger
。目标是仅使用 BigInteger
类型,因为如果使用其他类型,比如 float
,那么函数能够处理的数字大小就会受到限制。
也关注了性能问题,因为大整数运算通常非常慢。采用了牛顿-拉弗森方法(Newton-Raphson method)来逼近结果,这不是什么新鲜事。真正的性能提升在于如何计算第一个猜测值来初始化牛顿-拉弗森方法。
就像中的许多人一样,也是F#的新手,所以任何建设性的建议来改进代码或者以更函数式的方式编写代码都是受欢迎的。
平方根函数是各种数学计算中经常使用的函数,从勾股定理中找到斜边到计算网格上两点之间的距离。它有很多用途。为什么它没有被包含在库中,谁也说不准。也许最终会被添加到 .NET 库中。
下面是整数平方根的定义:给定一个正整数 S,返回一个正整数 R,它是小于或等于 S 的平方根的最大整数。这基本上意味着如果 S 不是一个完全平方数,那么答案会被截断到最近的整数。如果 S 是一个完全平方数,那么会得到确切的答案。例如,25 的整数平方根是 5,26 的整数平方根也是 5。
将从在F#中构建牛顿-拉弗森方法开始。这是许多计算器广泛使用的标准函数。不会解释牛顿-拉弗森方法来找到根,因为有很多其他网站已经很好地解释了。这种方法的复杂度是 O(Log n),即以 10 为底的对数;而寻找根的二分法的复杂度是 log base 2。以下是代码的主要部分:
let bigintSqrt(bigNum : BigInteger) =
let rec converge prevGuess =
let nextGuess = (bigNum / prevGuess + prevGuess) >>> 1
match BigInteger.Abs (prevGuess - nextGuess) with
| x when x < 2I -> nextGuess
| _ -> converge nextGuess
if bigNum.Sign = -1 then
failwith "Square root of a negative number is not defined."
else
let root = converge (sqrtRoughGuess bigNum)
if root * root > bigNum then root - 1I else root
使用右移操作符(>>>)而不是除以 2 来获得稍微更好的执行性能。添加了最后的 if 语句(在最后四行)的原因是因为它正在做整数平方根。所以答案必须小于或等于十进制平方根。由于这个循环如果在一个之内会收敛,可能只需要改变一个。还要注意,名为 converge 的内部函数是尾递归的,所以不用担心栈溢出。(递归调用后没有其他处理。)
现在剩下的就是创建 sqrtRoughGuess 函数,用于种子平方根函数。由于处理的是 BigInteger 类型,输入数字可能有几百或几千位,甚至更多。如果第一个猜测是 1,那将是一个糟糕的猜测。或者如果原始数字 S 或 S/2 作为第一个猜测,那将太高。尽管任何大于零的数字都可以工作,第一个猜测越接近最终答案,函数在 converge 内部函数中的迭代次数就越少。
为了得到平方根的一个好猜测,使用了一个简单的对数恒等式在以下公式中。
Sqrt(x) = b^(1/2 * log(x)),其中 b 是这个公式中的对数底数。将使用 b = 10 作为这个公式的底数,以简化事情。
得出了以下结果来得到一个接近的第一个猜测。
let sqrtRoughGuess (num : BigInteger) =
let log10x = log10 (num + 1I)
let halfLog = (log10x + 1I) >>> 1
(power10 halfLog)
let ten = 10I // 10^1
let tenBillion = 10000000000I // 10^10
let googol = 100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000