Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/haskell/10.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/29.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Haskell 如何找到所有仅为2、3和5的幂的倍数的数字的列表?_Haskell_Hamming Numbers_Smooth Numbers - Fatal编程技术网

Haskell 如何找到所有仅为2、3和5的幂的倍数的数字的列表?

Haskell 如何找到所有仅为2、3和5的幂的倍数的数字的列表?,haskell,hamming-numbers,smooth-numbers,Haskell,Hamming Numbers,Smooth Numbers,我试图生成一个所有倍数的列表,可以用形式表示,其中a、b和c是整数。我试了以下方法 [ a * b * c | a <- map (2^) [0..], b <- map (3^) [0..], c <- map (5^) [0..] ] [a*b*c | a之所以只有5的幂,是因为Haskell试图对a=2^0和b=3^0的每一个可能的c进行求值,并且只有当求值完成时,才会求出a=2^0和b=3^1。 因此,这样您只能像这样构造一个有限列表: [a*b*c|a 但它只列出

我试图生成一个所有倍数的列表,可以用形式表示,其中a、b和c是整数。我试了以下方法

[ a * b * c | a <- map (2^) [0..], b <- map (3^) [0..], c <- map (5^) [0..] ] 

[a*b*c | a之所以只有5的幂,是因为Haskell试图对a=2^0和b=3^0的每一个可能的c进行求值,并且只有当求值完成时,才会求出a=2^0和b=3^1。
因此,这样您只能像这样构造一个有限列表:
[a*b*c|a
但它只列出了5的幂,从来没有继续到2或3

只解决这一点。 要计算数字
2^a*3^0b*5^c
您尝试生成三元组
(a,b,c)
,但无法生成形式为
(0,0,c)
。这就是为什么您的数字都是形式为
2^0*3^0*5^c
,即只有5的幂

从对开始更容易。要生成所有对
(a,b)
,您可以沿着表单的对角线

a+b = k
对于每个正
k
。每个对角线都很容易定义

diagonal k = [(k-x,x) | x <- [0..k]]
要生成这样的平面,只需沿其对角线工作

triagonal k = [(k-x,b,c) | x <- [0..k], (b,c) <- diagonal x]

triagnalk=[(k-x,b,c)| x另一种看待它的方式是你想要那些只能被2,3或5整除的数字。所以检查从1开始的每个数字是否满足这个条件。如果是,它就是列表的一部分

someList = [x| x<- [1..], isIncluded x]
PowRequires是一个函数,它接受数字和基数,并返回不能进一步除以基数的数字

powRemainder :: Int -> Int -> Int
powRemainder 1 b = 1
powRemainder n b = if (n `mod` b) == 0 then powRemainder (n `div` b) b else n

当我运行
take 20 someList
时,它返回
[1,2,3,4,5,6,8,9,10,12,15,16,18,20,24,25,27,30,32,36]
我的第一个想法是分别从2,3和5的幂列表开始:

p2 = iterate (2 *) 1
p3 = iterate (3 *) 1
p5 = iterate (5 *) 1
合并两个已排序的流也很容易:

fuse [] ys = ys
fuse xs [] = xs
fuse xs@(x : xs') ys@(y : ys')
    | x <= y    = x : fuse xs' ys
    | otherwise = y : fuse xs ys'
这是可行的,但我不喜欢它的某些方面:

  • 对于我们发出的每个元素(
    n:…
    ),我们将最多三个新元素添加到累加器(
    ns'S.union`.[2,3,5]
    )(“最多三个”,因为其中一些元素可能是重复的,将被过滤掉。)
  • 这意味着
    numbers
    承载着一个稳定增长的数据结构;我们从
    numbers
    中消耗的元素越多,累加器的增长就越大
  • 从这个意义上说,它不是一个纯粹的“流”算法。即使我们忽略了稳步增长的数字本身,我们需要更多的内存,并在序列中执行更多的计算
从您的代码:

[ a * b * c | a <- map (2^) [0..], b <- map (3^) [0..], c <- map (5^) [0..] ] 
为了方便起见,
让xs=map(2^[0..];让ys=map(3^[0..];让zs=map(5^[0..]

得到2和3的倍数,考虑下列数字的组织:

1, 2, 4, 8, 16, ...
3, 6, 12, 24, 48, ...
9, 18, 36, 72, 144, ...
...
从这一点来看,您可能希望以下几点起作用:

let xys = foldr (merge . flip fmap xs . (*)) [] ys
但这不起作用,因为从上面的组织来看,
merge
不知道哪一行包含生成的head元素,从而无限地使其未计算。我们知道上面的行包含所述head元素,因此通过以下小调整,它最终起作用:

let xys = foldr ((\(m:ms) ns -> m : merge ms ns) . flip fmap xs . (*)) [] ys
zs
执行同样的操作,下面是所需的列表:

let xyzs = foldr ((\(m:ms) ns -> m : merge ms ns) . flip fmap xys . (*)) [] zs
完整代码摘要:

merge []     ys     = ys
merge xs     []     = xs
merge (x:xs) (y:ys) = if x <= y then x : merge xs (y:ys) else y : merge (x:xs) ys

xyzs = let
    xs = map (2^) [0..]
    ys = map (3^) [0..]
    zs = map (5^) [0..]
    xys = foldr ((\(m:ms) ns -> m : merge ms ns) . flip fmap xs . (*)) [] ys
    in foldr ((\(m:ms) ns -> m : merge ms ns) . flip fmap xys . (*)) [] zs
merge[]ys=ys
合并xs[]=xs
合并(x:xs)(y:ys)=如果x m:合并ms ns)。翻转fmap xs。(*)[]ys
在foldr((\(m:ms)ns->m:merge ms ns)中,翻转fmap xys(*)[]zs

正如其他人所评论的,您的核心无法工作,因为它类似于以下命令伪代码:

for x in 0..infinity:
   for y in 0..infinity:
      for z in 0..infinity:
         print (2^x * 3^y * 5^x)
最里面的
for
需要无限的时间来执行,因此其他两个循环将永远无法通过它们的第一次迭代。因此,
x
y
都被固定在值
0

这是一个典型的问题:如果我们坚持在下一个
y
(或
x
)之前尝试
z
的所有值,我们就会在预期输出的子集上陷入困境。我们需要一种更“公平”的方法来选择
x,y,z
的值,这样我们就不会陷入困境:这种技术被称为“吻合”

其他人已经展示了一些燕尾技术。在这里,我只提到这个包,它实现了一个易于使用的燕尾单子。生成的代码与OP中发布的代码非常相似

import Control.Monad.Omega

powersOf235 :: [Integer]
powersOf235 = runOmega $ do
   x <- each [0..]
   y <- each [0..]
   z <- each [0..]
   return $ 2^x * 3^y * 5^z
import Control.Monad.Omega
powersOf235::[整数]
powerso235=runOmega$do

x您的解决方案满足您所述的要求。也许您可以更仔细地说明问题?例如,听起来您希望列表按某种特定顺序排列。是否有一种非愚蠢的方式比它更好?@melpomene是的,有。您的解决方案在选择时会过度生成序列,总是在序列中添加三倍一个。您可以有条件地只添加最小的一个,实际上是在正在生成的有序序列中保留三个反向指针。著名的规范代码可以在标记(我添加的)和Wikipedia上找到。@melpomene/contd./然后有一个代码,每个数字只生成一次。(也出现在上面链接的RosettaCode和WP页面上)好的,如果你认为这是值得的,我就不提了。很抱歉,我似乎没有充分澄清这个问题。我想要的是一个有序的无限列表,虽然我可以对有限列表进行排序,但我觉得可能有一个更有效的解。@robbie0630一个数学学家的解决方案是:把这些f递增的
n
(通过加倍、重复平方或其他方式)的初始列表;跳过之前阶段中已经找到的部分;还有你的无限序列,理论上也相当复杂。:)当然,在实践中,它会很快被卡住。但是,这样产生的每个有限序列在某个点上都是正确的,之后会有洞,所以将它们成对比较也会有帮助。同样,一个理论(非)解决方案。:)只是一个旁注:集合的大小i
let xys = foldr ((\(m:ms) ns -> m : merge ms ns) . flip fmap xs . (*)) [] ys
let xyzs = foldr ((\(m:ms) ns -> m : merge ms ns) . flip fmap xys . (*)) [] zs
merge []     ys     = ys
merge xs     []     = xs
merge (x:xs) (y:ys) = if x <= y then x : merge xs (y:ys) else y : merge (x:xs) ys

xyzs = let
    xs = map (2^) [0..]
    ys = map (3^) [0..]
    zs = map (5^) [0..]
    xys = foldr ((\(m:ms) ns -> m : merge ms ns) . flip fmap xs . (*)) [] ys
    in foldr ((\(m:ms) ns -> m : merge ms ns) . flip fmap xys . (*)) [] zs
for x in 0..infinity:
   for y in 0..infinity:
      for z in 0..infinity:
         print (2^x * 3^y * 5^x)
import Control.Monad.Omega

powersOf235 :: [Integer]
powersOf235 = runOmega $ do
   x <- each [0..]
   y <- each [0..]
   z <- each [0..]
   return $ 2^x * 3^y * 5^z