我怎样才能表达一个阶乘n!使用F#函数,递归还是其他?

时间:2010-11-06 00:08:08

标签: recursion f# tail-recursion factorial

自然数的阶乘(任何大于或等于0的数字)是该数乘以其自身的阶乘减1,其中0的阶乘被定义为{{1} }。

例如:

1

另一种写作方式是将0! = 1 1! = 1 * 0! 2! = 2 * 1! 3! = 3 * 2! 4! = 4 * 3! 5! = 5 * 4! 1之间的所有自然数相乘:

n

如何用F#中的递归函数表达这个?并且应该我用递归函数做到了吗?

n!

6 个答案:

答案 0 :(得分:23)

如何,选项1:

let rec factorial n =
    match n with
    | 0 | 1 -> 1
    | _ -> n * factorial(n-1)

如何,选项2(尾递归,编译成循环):

let factorial n =
    let rec loop i acc =
        match i with
        | 0 | 1 -> acc
        | _ -> loop (i-1) (acc * i)
    loop n 1

应该:不,请看我的回答:

While or Tail Recursion in F#, what to use when?

我提倡经常避免迭代和递归,而支持高阶函数。但是如果你刚刚开始,也许不要过多担心这个建议。 (但后来看到例如@ ChaosPandion的答案,或者例如。

let factorial n = [1..n] |> List.fold (*) 1

甚至:

let factorial n = [1..n] |> List.reduce (*) // doesn't require the 2nd parameter

答案 1 :(得分:8)

这是另一个例子:

let factorial (num:int) =
    seq { for n in [1..num] -> n }
    |> Seq.reduce (fun acc n -> acc * n)

这个例子可能更清楚一点:

let factorial num =
    [1..num] |> Seq.fold (fun acc n -> acc * n) 1

答案 2 :(得分:5)

Brian的回答是最实用的,但这是继续传递风格的解决方案:

let rec factorial n = 
  let rec loopk i k = 
    match i with
    | 0 | 1 -> k i
    | _ -> loopk (i-1) (fun r -> k (i * r))
  in loopk n (fun r -> r)

答案 3 :(得分:3)

  

我如何为此声明一个递归函数?

首先,要定义递归函数,您需要使用let rec而不是let(因为let不允许您引用递归定义的函数)。

要递归地定义阶乘函数,最简单(但不是最有效)的方法是使用阶乘函数的标准数学定义。

更有效的方法是定义一个尾递归辅助函数,使用第二个参数存储到目前为止计算的结果。

答案 4 :(得分:3)

我最喜欢的递归序列的F#解决方案是......无限的尾递归序列!:

let factSeq =    
    let rec factSeq m n = 
        seq { let m = m * n
              yield m
              yield! factSeq m (n+1I) }
    seq { yield 1I ; yield 2I ; yield! (factSeq 2I 3I) }

let factTake n = factSeq |> Seq.take n //the first n terms
let fact n = factSeq |> Seq.nth (n-1) //the nth term

我在这里使用BigIntegers,因为阶乘序列增长如此之快(继续,尝试第20,000个术语)。

我通常同意Brian的建议,即尽可能在迭代循环或递归循环(尾递归+累加器)上使用高阶函数。但我认为在这种情况下,我所展示的无限序列更灵活,因为它产生了阶乘序列的所有项,直到所需的项(factTake),并且每个项只需要一个乘法步骤( N *(N-1))。然而,如果您想要使用折叠解决方案的前n个术语,则每个计算将独立完成,并且不会从先前的计算中受益。

答案 5 :(得分:0)

这是一个更简单的实现

let rec bfact (n):bigint = 
    match n with
        | i when i<0 -> bigint.Zero
        | 0 | 1 -> bigint(1)
        | _ -> ( bfact(n-1) * bigint(n) )

并测试

bfact(50)

val bfact : n:int -> bigint
val it : bigint =
  30414093201713378043612608166064768844377641568960512000000000000