2009-12-10 54 views
0

我正在嘗試在Project Euler中執行problem 12F中的列表處理問題#

numDivisor64是計算除數的數量。

我寫這篇F#代碼:

let problem12 = 
    {1L..300000L} |> Seq.map (fun x->x*(x+1L)/2L) |> Seq.map numDivisor64 |> Seq.filter (fun x->x>500L) 

這道題找到了數量,而不是它的除數#。除了用不太緊湊的方式使用循環或遞歸來寫這個,任何漂亮的方法?

另一個問題,我偶爾會發現,我需要通過向所有數字加上'L'來將32位int代碼版本轉換爲64位版本。有沒有辦法避免這種情況?任何像C++模板?

我第一次寫

let numDivisor n = 
    let rec countd n d = 
     if n%d=0 then 
      let n2, cnt = countd (n/d) d 
      n2, cnt+1 
     else 
      n, 0 

    let rec collect n d = 
     if n < d then 1 
     elif n%d=0 then 
      let n2, cnt = countd n d 
      (cnt+1) * (collect n2 d) 
     else 
      collect n (d+1) 
    collect n 2 

後來我發現我需要更大的整數:

let numDivisor64 n = 
    let rec countd n d = 
     if n%d=0L then 
      let n2, cnt = countd (n/d) d 
      n2, cnt+1L 
     else 
      n, 0L 

    let rec collect n d = 
     if n < d then 1L 
     elif n%d=0L then 
      let n2, cnt = countd n d 
      (cnt+1L) * (collect n2 d) 
     else 
      collect n (d+1L) 
    collect n 2L 

回答

3

我會改寫爲第一通緝數搜索如下:

  • 開始用的Int64的

  • 無限流把它們變成三角形數字

  • 找到的第一個數字滿足條件(而不是映射到除數,這不是你想要的,你想要的數字本身)。

代碼:

let problem12 = 
    Seq.initInfinite int64 //the same as Seq.initInfinite (fun n -> int64 n) 
    |> Seq.map (fun x -> x*(x+1L)/2L) 
    |> Seq.find (fun x -> numDivisor64 x > 500L) 

關於第二個問題:當我解決項目歐拉問題,我通常默認使用的Int64的,因爲類型推斷的限制。

使用inline關鍵字可以編寫更通用的版本。在hubFS上查看例如this的線程。

編輯:這裏是一個更寬泛的版本,使用上面的鏈接描述的技術: NumDivisorG的類型簽名變得可怕,但應該對任何數據類型「知道」 *,+ 1和0

工作
module NumericLiteralG = 
    let inline FromZero() = LanguagePrimitives.GenericZero 
    let inline FromOne() = LanguagePrimitives.GenericOne 

let inline numDivisorG n = 
    let rec countd n d = 
     if n%d=0G then 
      let n2, cnt = countd (n/d) d 
      n2, cnt+1G 
     else 
      n, 0G 

    let rec collect n d = 
     if n < d then 1G 
     elif n%d=0G then 
      let n2, cnt = countd n d 
      (cnt+1G) * (collect n2 d) 
     else 
      collect n (d+1G) 
    collect n (1G+1G) 

let problem12L = 
    Seq.initInfinite int64 //the same as Seq.initInfinite (fun n -> int64 n) 
    |> Seq.map (fun x -> x*(x+1L)/2L) 
    |> Seq.find (fun x -> numDivisorG x > 500L) 

let problem12I = 
    Seq.initInfinite id //the same as Seq.initInfinite (fun n -> n) 
    |> Seq.map (fun x -> x*(x+1)/2) 
    |> Seq.find (fun x -> numDivisorG x > 500)  
1

,如果你有除數的列表中,您可以編寫一個函數來計算他們所有的最小公倍數(這應該是有問題的數字)。

在Haskell,這看起來像

lcmAll = foldl1 lcm 
在F#

我認爲它看起來像這樣

let rec lcmAll (head :: tail) = 
    Seq.fold lcm head tail 

我不知道,如果F#有一個內建的LCM。

另一種方法是通過使用產品類型或元組來攜帶原始數字。

let problem12 = 
    {1L..300000L} |> Seq.map (fun x->x*(x+1L)/2L) |> Seq.map (fun x->(x,numDivisor64 x)) |> Seq.filter (fun (x,y)->y>500L) 

在問候的64位數字的問題,如果你給你的功能一個明確的類型簽名可能迫使F#使用64位整數(前提是類型簽名是有效的函數定義) 。在Haskell中,這種事情再次發生,我無法確認它是否與F#一致。如果你可以仔細檢查,這將是真棒。

+0

與Haskell不同,數字文字(如1)在F#中具有特定類型(在本例中爲int = System.Int32),因此僅給該函數提供不同的簽名將不起作用。解決方案是使用用戶定義的數字文字類型,就像在cfern的文章中一樣。當然是 – kvb 2009-12-10 13:55:43

+0

。謝謝(你的)信息。 – barkmadley 2009-12-11 00:29:39