2009-12-10 53 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