生成在Haskell中给定`k`和`l`的所有可能数字`x^k*y^l`的流。

huangapple go评论74阅读模式
英文:

Generate a stream of all possible numbers `x^k*y^l` for given `k` and `l` in Haskell

问题

生成函数generateExponents k l,对于给定的 k 和 l,生成一个递增顺序中所有可能的唯一数 x^k*y^l 流。例如,generateExponents 2 3 = [1,4,8,9,16,25,27...]

由于显而易见的原因,以下方法不起作用:

generateExponents k l = sort [x^k*y^l | x <- [1..], y <- [1..]]

然后我尝试了这个方法,但也不起作用:

generateExponents k l = [n | n <- [1 ..], n `elem` products n]
  where
    xs n = takeWhile (\x -> x ^ k <= n) [1 ..]
    ys n = takeWhile (\y -> y ^ l <= n) [1 ..]
    products n = liftA2 (*) (xs n) (ys n)

我做错了什么?

英文:

> Generate the function generateExponents k l, which for given k and l generates a stream of all unique possible numbers x^k*y^l in increasing order. For example generateExponents 2 3 = [1,4,8,9,16,25,27...]

For obvious reasons this doesn't work:

generateExponents k l = sort [x^k*y^l | x &lt;- [1..], y &lt;- [1..]]

Then I tried this, which doesn't work either:

generateExponents k l = [n | n &lt;- [1 ..], n `elem` products n]
  where
    xs n = takeWhile (\x -&gt; x ^ k &lt;= n) [1 ..]
    ys n = takeWhile (\y -&gt; y ^ l &lt;= n) [1 ..]
    products n = liftA2 (*) (xs n) (ys n)

What am I doing wrong?

答案1

得分: 4

你的算法速度相当慢 - 它检查每个数字,对于每个数字,都搜索适当的因式分解!通过生成无限的表格答案,然后适当地折叠表格,你可以做得更好。例如,对于 x^2*y^3,表格如下所示:

 x  1    2    3    4    5
y
1   1    4    9   16   25
2   8   32   72  128  200
3  27  108  243  432  675
4  64  256  576 1024 1600
5 125  500 1125 2000 3125

注意这个表格的两个好处:每行都是排序的,而行本身也是排序的。这意味着我们可以通过简单地取左上角的值,然后重新插入第一行的尾部到其新的排序位置来高效地合并它们。例如,上面的表格,在发出 1 后,将如下所示:

  4    9   16   25   36
  8   32   72  128  200
 27  108  243  432  675
 64  256  576 1024 1600
125  500 1125 2000 3125

然后,在发出左上角的值 4 后:

  8   32   72  128  200
  9   16   25   36   49
 27  108  243  432  675
 64  256  576 1024 1600
125  500 1125 2000 3125

注意如何顶部行现在已经变成了第二行,以保持双重排序属性。

这是一种以正确顺序构造所有正确数字的高效方法。然后,唯一剩下的技巧是去重,对此你可以使用标准技巧 map head . group,因为重复的元素保证会相邻。以下是完整的代码:

import Data.List

generateExponents' k l = map head . group . go $ [[x^k*y^l | x <- [1..]] | y <- [1..]] where
    go ((x:xs):xss) = x:go (insert xs xss)

它要快得多。比较一下:

> sum . take 400 $ generateExponents 2 3
5994260
(8.26 secs, 23,596,249,112 bytes)
> sum . take 400 $ generateExponents' 2 3
5994260
(0.01 secs, 1,172,864 bytes)
> sum . take 1000000 {- 一百万 -} $ generateExponents' 2 3
72001360441854395
(6.99 secs, 13,460,753,616 bytes)
英文:

Your algorithm is pretty slow -- it checks every number, and for every number it searches for an appropriate factorization! You can do better by producing an infinite table of answers, and then collapsing the table appropriately. For example, for x^2*y^3, the table looks like:

 x  1    2    3    4    5
y
1   1    4    9   16   25
2   8   32   72  128  200
3  27  108  243  432  675
4  64  256  576 1024 1600
5 125  500 1125 2000 3125

Note two nice features of this table: each row is sorted, and the rows themselves are sorted. This means we can merge them efficiently by simply taking the top-left value, then re-inserting the tail of the first row in its new sorted position. For example, the table above, after emitting 1, would look like:

  4    9   16   25   36
  8   32   72  128  200
 27  108  243  432  675
 64  256  576 1024 1600
125  500 1125 2000 3125

Then, after emitting the top-left value 4:

  8   32   72  128  200
  9   16   25   36   49
 27  108  243  432  675
 64  256  576 1024 1600
125  500 1125 2000 3125

Note how the top row has now become the second row to keep the doubly-sorted property.

This is an efficient way to construct all the right numbers in the right order. Then, the only remaining trick needed is to deduplicate, and for that you can deploy the standard trick map head . group, since duplicates are guaranteed to be next to each other. Here's the full code:

import Data.List

generateExponents&#39; k l = map head . group . go $ [[x^k*y^l | x &lt;- [1..]] | y &lt;- [1..]] where
    go ((x:xs):xss) = x:go (insert xs xss)

It's much, much faster. Compare:

&gt; sum . take 400 $ generateExponents 2 3
5994260
(8.26 secs, 23,596,249,112 bytes)
&gt; sum . take 400 $ generateExponents&#39; 2 3
5994260
(0.01 secs, 1,172,864 bytes)
&gt; sum . take 1000000 {- a million -} $ generateExponents&#39; 2 3
72001360441854395
(6.99 secs, 13,460,753,616 bytes)

答案2

得分: 1

我认为你只是忘记在 xsys 上映射实际函数:

generateExponents k l = [n | n <- [1 ..], n `elem` products n]
  where
    xs n = takeWhile (<= n) $ map (^ k) [1 ..]
    ys n = takeWhile (<= n) $ map (^ l) [1 ..]
    products n = liftA2 (*) (xs n) (ys n)
英文:

I think you just forgot to map the actual function over the xs and ys:

generateExponents k l = [n | n &lt;- [1 ..], n `elem` products n]
  where
    xs n = takeWhile (&lt;= n) $ map (^ k) [1 ..]
    ys n = takeWhile (&lt;= n) $ map (^ l) [1 ..]
    products n = liftA2 (*) (xs n) (ys n)

huangapple
  • 本文由 发表于 2023年2月7日 00:39:49
  • 转载请务必保留本文链接:https://go.coder-hub.com/75364141.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定