在Haskell中整理列表理解

时间:2017-12-26 20:34:19

标签: haskell list-comprehension infinite

所以我试图在Haskell中生成一个出租车编号列表。出租车编号是可以用两种不同方式写成两个不同立方体之和的数字 - 最小的是 1729 = 1^3 + 12^3 = 9^3 + 10^3

现在,我只是想要生成“弥补”出租车编号的四个数字,例如(1,12,9,10),并被告知使用列表理解(我不太熟悉)。此函数将生成所有4元组,其中最大数字最多为n:

  

taxi n = [(a,b,c,d) | a <- [1..n], b <- [1..n], c <- [1..n], d <- [1..n], a^3 + b^3 == c^3 + d^3, a < b, a < c, c < d]

然而,由于以下几个原因,它很麻烦:

  • a,b,c,d的域名都相同但我无法弄清楚如何简化此代码,因此[1..n]只会写一次。
  • 我想要一个无限的列表,没有上限,所以我可以让程序尽可能长时间地运行并在我请的时候终止它。显然,如果我只设置a <- [1..]等,那么程序将永远不会最终评估任何内容。
  • 程序非常慢:仅taxi 50需要19秒。

任何速度优化都会很好,但如果不是我使用的天真方法就足够了。

3 个答案:

答案 0 :(得分:13)

您的约束意味着A。因此,让input[type='text' value=item.net_unit_price name="cart[cart_items][][id]"] 运行到最外面,让其他运行在适当的较低范围内:

input[type='text' value=item.net_unit_price name="cart[cart_items][price]"]

要变得无限,只需使用a < c < d < b

另一个重大改进是计算来自其他三个变量的四个变量之一:

b

使用taxi n = [ (a,b,c,d) | b <- [1..n], d <- [1..b-1], c <- [1..d-1], a <- [1..c-1], a^3 + b^3 == c^3 + d^3 ] 与GHCi进行基准b <- [1..]

taxi = [ (a,b,c,d) | b <- [1..],
                     c <- [1..b-1],
                     a <- [1..c-1],
                     let d3 = a^3 + b^3 - c^3,
                     let d = round(fromIntegral(d3)**(1/3)),
                     c < d,
                     d^3 == d3 ]

答案 1 :(得分:3)

  

a,b,c,d的域名都是相同的,但我无法解释如何简化此代码,因此[1..n]只会写一次。

使用[a,b,c,d] <- replicateM 4 [1..n]

  

程序非常慢:只需50秒的出租车需要19秒。

一个便宜的改进是将a<ba<cc<d条件融入理解中。

taxi n = [(a,b,c,d) | a <- [1..n], b <- [a+1..n], c <- [a+1..n], d <- [c+1..n], a^3 + b^3 == c^3 + d^3]

这使我的机器上的速度明显加快。

或者,为了与答案的下一个(和上一个)部分更好地兼容,请将bcd视为抵消。

taxi n =
    [ (a,b,c,d)
    | a <- [1..n]
    , b_ <- [1..n], let b = a+b_, b<=n
    , c_ <- [1..n], let c = a+c_, c<=n
    , d_ <- [1..n], let d = c+d_, d<=n
    , a^3 + b^3 == c^3 + d^3
    ]
  

我想要一个没有上限的无限列表。

请参阅我对Cartesian product of 2 lists in Haskell的回答。 tl; dr使用choices

答案 2 :(得分:2)

拿起斯蒂芬他的出色答案。给定a^3 + b^3 == c^3 + d^3我们只需查看保持0 < a < c < b的整数。现在介绍这个(无限)迭代结构

-- generates all integers x, y and z for which holds 0 < x < y < z
triplets = [(x, y, z) | z <- [3 .. ], y <- [2 .. z - 1], x <- [1 .. y - 1]]

这将使我们可以从列表理解中轻松访问三元组,我们将在此处介绍。对于具有Python背景的人来说,这应该等同于Python的yield

1 2 3
1 2 4
1 3 4
2 3 4
1 2 5
1 3 5
2 3 5
1 4 5
2 4 5
3 4 5
1 2 6
1 3 6
2 3 6
1 4 6
2 4 6
3 4 6
1 5 6
2 5 6
3 5 6
4 5 6

接下来我们需要一些东西(快速)找到最大的立方体并测试整数是多维数据集,也称为整数立方根。有这个包Math.NumberTheory.Powers.Cubes,它具有这些任务的功能。或者只使用这些

-- given integer x >= 0 find the largest integer r such that r^3 <= x
largestCube :: Integral a => a -> a
largestCube x = 
    let powers_of_two = iterate ((*) 2) 1
        upper         = head [j | j <- powers_of_two, x < j ^ 3]
    in  largestCubeSub 0 upper x


largestCubeSub :: Integral a => a -> a -> a -> a
largestCubeSub lower upper x
    | lower + 1 == upper   = lower
    | b ^ 3 <= x           = largestCubeSub b upper x
    | otherwise            = largestCubeSub lower b x
    where
    b = div (lower + upper) 2


-- test if an integer x >= 0 is a cube
isCube :: Integral a => a -> Bool
isCube x = (largestCube x) ^ 3 == x

现在您对前50个出租车编号的紧凑列表理解类似

*Main> condition = \a b c -> and [isCube (a^3 + b^3 - c^3), a^3 + b^3 - c^3 > c^3]
*Main> taxi = [(a, b, c, largestCube (a^3 + b^3 - c^3)) | (a, c, b) <- triplets, condition a b c]
*Main> first50 = take 50 taxi 

使用

打印它们
*Main> single_line = \(x, y, z, u) -> unwords [show i | i <- [x, y, z, u]]
*Main> putStrLn $ unlines $ map single_line first50 

会给出

1 12 9 10
2 16 9 15
2 24 18 20
10 27 19 24
4 32 18 30
2 34 15 33
9 34 16 33
3 36 27 30
17 39 26 36
12 40 31 33
6 48 27 45
4 48 36 40
12 51 38 43
8 53 29 50
20 54 38 48
17 55 24 54
9 58 22 57
3 60 22 59
5 60 45 50
8 64 36 60
30 67 51 58
4 68 30 66
18 68 32 66
42 69 56 61
6 72 54 60
17 76 38 73
5 76 48 69
34 78 52 72
10 80 45 75
15 80 54 71
24 80 62 66
30 81 57 72
51 82 64 75
7 84 63 70
2 89 41 86
11 93 30 92
23 94 63 84
12 96 54 90
50 96 59 93
8 96 72 80
20 97 33 96
47 97 66 90
35 98 59 92
24 98 63 89
29 99 60 92
6 102 45 99
27 102 48 99
23 102 60 95
24 102 76 86
1 103 64 94

在几秒钟内返回前50个出租车驾驶室号码。