首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Haskell中无限列表上的燕尾迭代

Haskell中无限列表上的燕尾迭代
EN

Stack Overflow用户
提问于 2013-09-19 21:36:22
回答 9查看 1.1K关注 0票数 7

我想迭代2个(或3个)无限列表,并找到满足条件的“最小”对,如下所示:

代码语言:javascript
复制
until pred [(a,b,c) | a<-as, b<-bs, c<-cs]
  where pred (a,b,c) = a*a + b*b == c*c
        as = [1..]
        bs = [1..]
        cs = [1..]

以上操作不会走得太远,因为a == b == 1在整个程序运行过程中都是如此。有没有一个很好的方法来解决这个问题,比如构建无限序列[(1,1,1),(1,2,1),(2,1,1),(2,1,2),(2,2,1),(2,2,2),(2,2,3),(2,3,2),..]

奖励:可以推广到n-tuple吗?

EN

回答 9

Stack Overflow用户

发布于 2013-09-20 00:34:22

有一个monad可以做到这一点Omega

代码语言:javascript
复制
Prelude> let as = each [1..]
Prelude> let x = liftA3 (,,) as as as
Prelude> let x' = mfilter (\(a,b,c) -> a*a + b*b == c*c) x
Prelude> take 10 $ runOmega x'
[(3,4,5),(4,3,5),(6,8,10),(8,6,10),(5,12,13),(12,5,13),(9,12,15),(12,9,15),(8,15,17),(15,8,17)]

使用它的应用特性,您可以将其泛化为任意元组:

代码语言:javascript
复制
quadrupels = (,,,) <$> as <*> as <*> as <*> as   -- or call it liftA4

:当然,这本身并不能消除重复。它只会给你适当的对角化。也许您可以将monad comprehensions与类似Thomas的方法一起使用,或者只是使用另一个mfilter通道(在本例中,仅限于b /= c )。

票数 8
EN

Stack Overflow用户

发布于 2013-09-19 22:42:17

列表理解是解决这类问题的很好(而且简明)的方法。首先,你知道你想要所有可能满足a^2 + b^2 = c^2(a,b,c)组合-一个有用的观察是(只考虑正数) a <= c && b <= c总是这样的。

因此,为了生成我们的候选列表,我们可以说c的范围从1到无穷大,而ab的范围从1到c

代码语言:javascript
复制
[(a,b,c) | c <- [1..], a <- [1..c], b <- [1..c]]

为了得到解决方案,我们只需要添加您想要的方程式作为保护:

代码语言:javascript
复制
[(a,b,c) | c <- [1..], a <- [1..c], b <- [1..c], a*a+b*b == c*c]

这很低效,但输出是正确的:

代码语言:javascript
复制
[(3,4,5),(4,3,5),(6,8,10),(8,6,10),(5,12,13),(12,5,13),(9,12,15)...

有比盲测试更有原则的方法可以解决这个问题。

票数 7
EN

Stack Overflow用户

发布于 2013-09-19 22:10:17

{-这取决于什么是“最小”。但是这里有一个“最小”概念的解决方案,如果首先比较元组的最大值。数字,然后是它们的总和。(当我在注释中写入文本时,您可以将我的整个答案复制并粘贴到文件中。)

我们稍后将需要nub。-}

代码语言:javascript
复制
import Data.List (nub)

{-仅供说明:具有2元组的简单情况。-}

代码语言:javascript
复制
-- all the two-tuples where 'snd' is 'n'
tuples n = [(i, n) | i <- [1..n]]
-- all the two-tuples where 'snd' is in '1..n'
tuplesUpTo n = concat [tuples i | i <- [1..n]]

{-要获得所有结果,您需要将每个元组的翻转插入到流中。但让我们稍后再做,并首先进行推广。

构建任意长度的元组有些困难,所以我们将使用列表。如果它们的长度是'k‘,我称它们为’kList‘。-}

代码语言:javascript
复制
-- just copied from the tuples case, only we need a base case for k=1 and 
-- we can combine all results utilizing the list monad.
kLists 1 n = [[n]]
kLists k n = do 
       rest <- kLists (k-1) n
       add <- [1..head rest]
       return (add:rest)

-- same as above. all the klists with length k and max number of n
kListsUpTo k n = concat [kLists k i | i <- [1..n]]

-- we can do that unbounded as well, creating an infinite list.
kListsInf k = concat [kLists k i | i <- [1..]]

{-下一步是循环这些列表,因为到目前为止,最大的数字总是在最后。所以我们只看所有的旋转来得到所有的结果。无可否认,在这里使用nub很笨拙,您可以改进这一点。但是如果没有它,所有元素都相同的列表就会重复k次。-}

代码语言:javascript
复制
rotate n l = let (init, end) = splitAt n l 
             in end ++ init
rotations k l = nub [rotate i l | i <- [0..k-1]]

rotatedKListsInf k = concatMap (rotations k) $ kListsInf k

{-剩下的就是将这些列表转换成元组。这有点尴尬,因为每个n元组都是一个单独的类型。当然,这很简单。-}

代码语言:javascript
复制
kListToTuple2 [x,y]         = (x,y)
kListToTuple3 [x,y,z]       = (x,y,z)
kListToTuple4 [x,y,z,t]     = (x,y,z,t)
kListToTuple5 [x,y,z,t,u]   = (x,y,z,t,u)
kListToTuple6 [x,y,z,t,u,v] = (x,y,z,t,u,v)

{-一些测试:

代码语言:javascript
复制
*Main> take 30 . map kListToTuple2 $ rotatedKListsInf 2
[(1,1),(1,2),(2,1),(2,2),(1,3),(3,1),(2,3),(3,2),(3,3),(1,4),(4,1),(2,4),(4,2),(3,4),
(4,3),(4,4),(1,5),(5,1),(2,5),(5,2),(3,5),(5,3),(4,5),(5,4),(5,5),(1,6),(6,1),
(2,6), (6,2), (3,6)]
*Main> take 30 . map kListToTuple3 $ rotatedKListsInf 3
[(1,1,1),(1,1,2),(1,2,1),(2,1,1),(1,2,2),(2,2,1),(2,1,2),(2,2,2),(1,1,3),(1,3,1),
(3,1,1),(1,2,3),(2,3,1),(3,1,2),(2,2,3),(2,3,2),(3,2,2),(1,3,3),(3,3,1),(3,1,3),
(2,3,3),(3,3,2),(3,2,3),(3,3,3),(1,1,4),(1,4,1),(4,1,1),(1,2,4),(2,4,1),(4,1,2)]

编辑:我意识到有一个bug:当然,仅仅旋转有序列表是不够的。解决方案必须是这样的:

代码语言:javascript
复制
rest <- concat . map (rotations (k-1)) $ kLists (k-1) n

kLists中,但随后会出现一些重复输出的问题。我想你可以弄明白这一点。;-) -}

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/18896172

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档