您无法使用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
的实现,您可以从中获得一些重大的性能改进。请参见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