f#中的int溢出

时间:2017-01-16 20:08:38

标签: f# overflow

我正在做一些功课,我们应该在F#中组合功能。我已经降低了阶乘函数,但是一旦我得到一个使用阶乘的大数字,它似乎就会溢出。 (让我们说20)我知道我可以使用int64或float,但这会改变代码上的所有输入。我应该使用什么数据类型?

let rec Fact (a:int)=
   if (a = 0) then 1 else a*Fact(a-1);;

let combo (n:int) (k:int)=
   if (n = 0) then 0 else (Fact n)/((Fact k)*(Fact (n-k)));;

现在代码,当我做组合20 5 ;;它给了我2147.这显然是错误的答案。我看了阶乘函数,当我把20放在那里时它给了我一个很大的负数。任何帮助将非常感激。提前谢谢。

2 个答案:

答案 0 :(得分:3)

首先,如果您想避免意外,可以打开文件顶部的Checked模块。这将重新定义数值运算符,以便它们执行溢出检查 - 并且您将获得异常而不是意外数字:

open Microsoft.FSharp.Core.Operators.Checked

正如Fyodor在评论中指出的那样,你不能在int中得到20的阶乘,你需要int64。但是,您的combo函数会执行除法,这会使combo 20 5的结果小到足以容纳int

一种选择是将Fact更改为使用int64,但保留combo作为获取和返回整数的函数 - 您需要将它们转换为{{1在调用int64之前,然后在执行除法后返回Fact

int

现在您可以致电let rec Fact (a:int64) = if (a = 0L) then 1L else a * Fact(a-1L) let combo (n:int) (k:int) = if (n = 0) then 0 else int (Fact (int64 n) / (Fact (int64 k) * Fact (int64 (n-k)))) ,结果会得到combo 20 5

编辑:正如@pswg在其他答案中指出的那样,15504也非常有限,因此您需要int64才能获得更大的因子。但是,同样的方法应该适用于BigInteger。您可以将BigInteger函数保留为通过从combo转换回int返回BigInteger的函数。

答案 1 :(得分:2)

你只是不能用32位整数(int)来做到这一点。 64位整数将使您达到20!,但会在21!失败。这些数字太大了,太快了。除此之外,您还需要使用System.Numerics.BigInteger(F#中缩写为bigint)。

参数可能保持int是合理的,但您需要返回bigint

let rec Fact (n : int) = 
    if n = 0 then bigint.One else (bigint n) * Fact (n - 1)

或者更加惯用:

let rec Fact = function | 0 -> bigint.One | n -> (bigint n) * Fact (n - 1)

现在,在您的Combo函数中,您需要在内部使用这些bigint进行所有数学运算(幸运的是,在这种情况下,您需要整数除法) )。

let Combo (n : int) (k : int) =
    if n = 0 then bigint.Zero else (Fact n) / ((Fact k) * (Fact (n - k)))

如果确实想让Combo返回int,您可以在此处进行转换:

let Combo (n : int) (k : int) =
    if n = 0 then 0 else (Fact n) / ((Fact k) * (Fact (n - k))) |> int

示例:

Combo 20 5 // --> 15504
Combo 99 5 // --> 71523144 (would break if you used int64)

修改:通过重新考虑Combo的实施,您可以从中获得一些重大的性能提升。有关此实现的基础,请参阅this question on Math.SE

let ComboFast (n : int) (k : int) =
    let rec Combo_r (n : int) = function 
        | 0 -> bigint.One 
        | k -> (bigint n) * (Combo_r (n - 1) (k - 1)) / (bigint k)
    Combo_r n (if (2 * k) > n then n - k else k)

快速基准测试表明,显着比基于Fact的版本更快

Function             Avg. Time (ms)
Combo 99 5            30.12570 
ComboFast 99 5         0.72364