OCaml中的整数取幂

时间:2013-06-05 22:03:32

标签: integer ocaml floating-accuracy exponentiation

OCaml中是否存在整数取幂的函数? **仅适用于花车。虽然看起来大部分都是准确的,但是不存在精确错误的可能性,例如2. ** 3. = 8.有时会返回错误?是否有用于整数求幂的库函数?我可以写自己的,但效率问题也会出现,如果没有这样的功能,我也会感到惊讶。

4 个答案:

答案 0 :(得分:23)

不在标准库中。但是您可以轻松地自己编写一个(使用exponentiation by squaring快速编写),或者重用提供此功能的扩展库。在Batteries中,它是Int.pow

以下是建议的实施:

let rec pow a = function
  | 0 -> 1
  | 1 -> a
  | n -> 
    let b = pow a (n / 2) in
    b * b * (if n mod 2 = 0 then 1 else a)

如果由于操作非常大的数字而存在溢出风险,则应该使用大整数库,例如Zarith,它提供各种取幂函数。

(您可能需要“模幂运算”,计算(a^n) mod p;这可以通过在中间计算中应用mod来避免溢出,例如在上面的函数pow中完成。)

答案 1 :(得分:12)

关于问题的浮点部分:OCaml调用底层系统的pow()函数。浮点求幂是一个难以实现的函数,但它只需要忠实(即精确到Unit in the Last Place)才能使2. ** 3. = 8.求值为true,因为{{1} }}是数学上正确结果8的一个ULP中唯一的8.0

所有数学图书馆都应该(*)忠实,所以你不必担心这个特定的例子。但是not all of them actually are,所以你担心是对的。


更值得担心的是,如果使用的是63位整数或更宽,则取幂的参数或结果不能完全表示为OCaml浮点数(实际上是IEEE 754双精度数,不能代表{ {1}}或2 53 + 1)。在这种情况下,浮点取幂是整数取幂的不良替代,不是因为特定实现中的弱点,而是因为它不是为了表示那么大的整数。


(*)关于这个主题的另一个有趣的参考是William Kahan的“A Logarithm Too Clever by Half”。

答案 2 :(得分:3)

这是另一个通过平方使用取幂的实现(就像@gasche提供的那样),但是这个是尾递归

let is_even n = 
  n mod 2 = 0

(* https://en.wikipedia.org/wiki/Exponentiation_by_squaring *)
let pow base exponent =
  if exponent < 0 then invalid_arg "exponent can not be negative" else
  let rec aux accumulator base = function
    | 0 -> accumulator
    | 1 -> base * accumulator
    | e when is_even e -> aux accumulator (base * base) (e / 2)
    | e -> aux (base * accumulator) (base * base) ((e - 1) / 2) in
  aux 1 base exponent

答案 3 :(得分:1)

上述解决方案的更简单公式:

let pow =
  let rec pow' a x n =
    if n = 0 then a else pow' (a * (if n mod 2 = 0 then 1 else x)) (x * x) (n / 2) in
  pow' 1

pow' a x n计算ax的{​​{1}}倍。