【问题标题】:Create sequence based on previous value根据先前的值创建序列
【发布时间】:2016-06-03 16:30:18
【问题描述】:

我正在学习 F#(第二天 O:-))并且我想创建 Collatz sequence,其中每个值都是根据前一个值计算的。我知道,用 C# 来做吧

public static void Main(string[] args)
{
    Console.WriteLine(string.Join(", ", collatz(13)));
}

static IEnumerable<int> collatz(int n)
{
    while (n != 1)
    {
        yield return n;

        if (n % 2 == 0)
            n /= 2;
        else
            n = 3 * n + 1;
    }

    yield return 1;
}

或者如何在 F# 中创建类似的数组

let rec collatz = function
    | 1 -> [ 1 ]
    | n when n % 2 = 0 -> n::collatz (n / 2)
    | n -> n::collatz (3 * n + 1)

collatz 13 |> Seq.map string |> String.concat ", " |> printfn "%s"

但不知道顺序解决方案...

【问题讨论】:

  • 你有什么问题?

标签: f# sequence


【解决方案1】:

您可以使用Seq.unfold...非常简洁地做到这一点...

let collatzSeq = Seq.unfold <| function 
    | 1 -> Some(1, -1) 
    | -1 -> None 
    | n when n % 2 = 0 -> Some(n, n / 2) 
    | n -> Some(n, 3 * n + 1)

【讨论】:

    【解决方案2】:

    您可以使用序列表达式:

    let rec collatzSeq n = seq {
        match n with
        | 1 -> yield 1
        | n when n % 2 = 0 ->
            yield n
            yield! collatzSeq (n / 2)
        | n ->
            yield n
            yield! collatzSeq (3 * n + 1)
    }
    

    【讨论】:

      【解决方案3】:

      上面的答案可能是最简单的方法,但纯粹出于兴趣,有一个非递归的解决方案:

      type CollatzState = 
          |Continue of int
          |Stop
      
      let collatz v =
          let unfolder =
              function
              |Stop -> None
              |Continue 1 -> Some (1, Stop)
              |Continue n when n % 2 = 0 -> Some(n, Continue <| n/2)
              |Continue n -> Some (n, Continue <| 3*n+1)
          Seq.unfold (unfolder) (Continue v)
      

      我们使用Seq.unfold 生成数字,直到达到1 的值,此时我们发出停止信号。

      CollatzState 类型同样可以是另一个 Option 而不是自定义类型,但我认为这样会更清楚一点。

      【讨论】:

        【解决方案4】:
        let collatz n =
            let nr = ref n
            seq{
                while (!nr > 1) do
                    yield !nr
                    if !nr % 2 = 0 then 
                        nr := !nr / 2 
                    else 
                        nr := 3 * !nr + 1 }
        collatz 13
        // val it : seq<int> = seq [13; 40; 20; 10; ...]
        

        为了相当忠实地翻译您的命令式 C# 代码,您需要提供突变;作为ref 单元格或可变绑定。因此,您可以使用序列表达式和突变 - 但最好是recursion

        您还可以将递归解决方案与memoisation 的概念结合起来。字典有助于检索已计算的值,从而停止递归。

        let memoRec f =
            let d = new System.Collections.Generic.Dictionary<_,_>()
            let rec g x =
                match d.TryGetValue x with
                | true, res -> res
                | _ -> let res = f g x in d.Add(x, res); res
            g
        
        let collatzRec =
            memoRec (fun f n -> seq{
                if n <= 1 then
                    yield 1
                else
                    yield n
                    if n % 2 = 0 then yield! f (n / 2)
                    else yield! f (3 * n + 1) } )
        

        【讨论】:

        • 我在某处读到,首选的解决方案是这些没有突变的解决方案。第二个代码示例是 O(n * log(n)) 我认为...
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多