我正在尝试生成一个可以用形式表示的所有倍数的列表,其中a,b和c是整数。我尝试了以下方法,
[ a * b * c | a <- map (2^) [0..], b <- map (3^) [0..], c <- map (5^) [0..] ]
但是它仅列出5的幂,而从不继续2或3。
编辑:抱歉,我没有足够清楚地阐明这个问题。我想要的是一个有序的无限列表,尽管我可以对一个有限列表进行排序,但我觉得好像有一种更有效的解决方案。
答案 0 :(得分:4)
之所以只有5的幂,是因为Haskell尝试评估a = 2 ^ 0和b = 3 ^ 0的每个可能的c,并且只有在完成时才对a = 2 ^ 0和b = 3 ^ 1。
因此,您只能以这种方式构造一个有限列表:
document.addEventListener('DOMContentLoaded', function() {
var elems = document.querySelectorAll('.carousel');
var instances = M.Carousel.init(elems, options);
});
// Or with jQuery
$(document).ready(function(){
$('.carousel').carousel();
});
给定n。
答案 1 :(得分:3)
我的第一个想法是分别从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'
但是后来我陷入困境,因为fuse p2 (fuse p3 p5)
并没有做任何有用的事情。它只会产生2或3或5的倍数,而不会混合因子。
我想不出一个纯粹的生成解决方案,所以我以集合累加器的形式添加了一些过滤。该算法(非常必要)是:
{1}
。n
。n
。{2n, 3n, 5n}
添加到累加器。累加器是一个集合,因为它很容易让我找到并提取最小的元素(基本上,我将其用作优先级队列)。它还处理因同时计算2 * 3
和3 * 2
。
Haskell实现:
import qualified Data.Set as S
numbers :: [Integer]
numbers = go (S.singleton 1)
where
go acc = case S.deleteFindMin acc of
(n, ns) -> n : go (ns `S.union` S.fromDistinctAscList (map (n *) [2, 3, 5]))
这行得通,但是有些事情我不喜欢它:
n : ...
,我们向累加器(ns `S.union` ... [2, 3, 5]
)最多添加三个新元素。 (“最多三个”,因为其中一些可能是重复项,将被过滤掉。)numbers
携带着稳定增长的数据结构;我们从numbers
中消耗的元素越多,累加器就越大。答案 2 :(得分:2)
但是它仅列出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]]
因此,要生成所有对,您只需生成k<-[1..]
的所有对角线。虽然您需要三元组(a,b,c)
,但类似,只是沿飞机工作,
a+b+c = k
要生成这样的平面,只需沿其对角线工作,
triagonal k = [(k-x,b,c) | x <- [0..k], (b,c) <- diagonal x]
然后您就去了。现在只需生成所有“三角形”即可获得所有可能的三元组,
triples = [triagonal k | k <- [0..]]
答案 3 :(得分:2)
根据您的代码:
[ a * b * c | a <- map (2^) [0..], b <- map (3^) [0..], c <- map (5^) [0..] ]
由于map (5^) [0..]
是一个无限列表,因此在a
和b
的第一次迭代中,它将遍历该无限列表,该列表不会停止。这就是为什么它停留在5的幂数的原因。
这里是除算术之外的解决方案。请注意,map (2^) [0..]
,map (3^) [0..]
和map (5^) [0..]
都是按升序排列的列表。这意味着通常的合并操作是适用的:
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
为方便起见,let xs = map (2^) [0..]; let ys = map (3^) [0..]; let 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
答案 4 :(得分:1)
另一种查看方式是您希望数字只能被2,3或5整除。因此,请检查每个从1开始的数字是否满足此条件。如果是,则它是列表的一部分。
someList = [x| x<- [1..], isIncluded x]
其中isIncluded是确定x是否满足上述条件的函数。为此,方法isIncluded首先将数字除以2,直到无法将其进一步除以2。然后将其与3和5的新除数相同。在末尾有1,那么我们知道该数字只能被2整除,3或5,仅此而已。
这可能不是最快的方法,但仍然是最简单的方法。
isIncluded :: Int -> Bool
isIncluded n = if (powRemainder n 2 == 1) then True
else let q = powRemainder n 2
in if (powRemainder q 3 == 1) then True
else let p = powRemainder q 3
in if (powRemainder p 5 == 1) then True else False;
powRemainder是接受数字和底数并返回不能再除以底数的数字的函数。
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]
。
答案 5 :(得分:1)
正如其他人已经评论过的那样,您的核心无法工作,因为它类似于以下命令式伪代码:
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
上。
这是一个经典的dovetailing问题:如果我们坚持在尝试下一个z
(或y
)之前尝试使用x
的所有值,则会陷入困境预期输出的子集。我们需要一种更“公平”的方法来选择x,y,z
的值,以免陷入这种困境:这种技术被称为“燕尾加工”。
其他人显示了一些燕尾榫技术。在这里,我仅提及control-monad-omega
软件包,该软件包实现了易于使用的燕尾式monad。生成的代码与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