![](/img/trans.png)
[英]An algorithm to find all nodes greater than some value X in a binary max heap
[英]algorithm to find products of a set of primes, in order, greater than x
考虑有限集{2,3,5,...,n}。 我对素数感兴趣,但这个问题可能适用于任何一组数字。 我想找到这些数字升序的所有可能乘积,尤其是大于或等于某个数字x的乘积。 有谁知道一个不错的算法吗?
编辑以澄清:
输入集中的每个因子可以使用多次。 如果输入为{2,3,5,7},则输出为{2,3,4,5,6,7,8,9,10,12,14,15,16,18,...} 。 一旦产生大于或等于某个数x的结果,该算法便会停止。
(编辑:使它按升序生产所有产品;让用户根据需要过滤它们。这是一个广义的汉明数问题)
genHamming :: Integral a => [a] -> [a]
genHamming zs = hmng where
hmng = 1 : foldr (||) [] [map (z*) hmng | z <- zs]
[] || ys = ys
xs || [] = xs
(x:xs) || (y:ys) | x==y = x : (xs || ys)
| x<y = x : (xs || (y:ys))
| y<x = y : (ys || (x:xs))
用法示例
Prelude Hamming> take 10 $ dropWhile (< 1000) $ genHamming [2,3,5]
[1000,1024,1080,1125,1152,1200,1215,1250,1280,1296]
Prelude Hamming>
如该答案所示的Haskell代码,
hamm :: [Integer] -> [Integer]
hamm [] = []
hamm (p:ps) = xs -- e.g. hamm [2,3,5]
where xs = merge (hamm ps) -- H({p} ∪ ps) = S,
(p : map (p*) xs) -- S ⊇ {p} ∪ H(ps) ∪ { p*x | x ∊ S }
merge a@(x:xs) b@(y:ys) | x < y = x : merge xs b
| otherwise = y : merge a ys
merge [] b = b
merge a [] = a
在这里merge
不会尝试消除倍数,因为不会有倍数-但前提是您仅在输入中使用素数 :
~> take 20 $ hamm [2,3,5,7]
[2,3,4,5,6,7,8,9,10,12,14,15,16,18,20,21,24,25,27,28]
如果不是,则需要使用union
,
union a@(x:xs) b@(y:ys) | x < y = x : union xs b
| x > y = y : union a ys
| otherwise = x : union xs ys
union [] b = b
union a [] = a
从上方开始有效地给定值可能是一个有趣的挑战。 该答案底部的直接生成切片的代码可以作为起点。
通常,很容易跳过有序序列,直到传递值为止。 在Haskell中,这是通过内置dropWhile (< n)
,
~> take 10 $ dropWhile (< 100) $ hamm [2,3,5,7]
[100,105,108,112,120,125,126,128,135,140]
您可能还希望在输出中包括2 ^ 0 * 3 ^ 0 * 5 ^ 0 * 7 ^ 0 = 1。
执行此操作的方法是使用优先级队列。 如果k在序列中,则2 k ,3 k ,5 k和7 k也是 。 从1开始输出,然后将2、3、5和7添加到优先级队列。 从队列顶部弹出2,然后将2 * 2 = 4、2 * 3 = 6、2 * 5 = 10和2 * 7 = 14添加到队列中; 该点处的队列将包含3、4、5、6、7、10和14。从队列顶部弹出3,然后添加3 * 2 = 6、3 * 3 = 9、3 * 5 = 15和3 * 7 = 21进入队列。 等等。
您会发现许多元素是重复的。 例如,在上例中,我们两次向优先级队列中添加了6。 您可以添加重复项,并在每次弹出队列时检查该元素是否与序列中的前一个成员相同,也可以在队列中保留单独的项目列表,并避免首先添加重复项。
我在博客中讨论了仅包含不同元素的优先级队列。
每个大于1的整数都是“素数集”的乘积,因为它是素数因子的乘积。 从所需的最小数字开始并删除所有不在初始集合中有素数的数字可能会更容易。 继续执行该过程,直到结果集足够大为止。 实际上,您正在做一个改进的Eratosthenes筛,删除了不在初始集中的所有素数的倍数。
因为我们的应用程序是用python编写的,所以我想分享以下实现:
def powers(x):
y = x
while True:
yield y
y *= x
def products(factors):
y0 = factors[0]
if len(factors) == 1:
yield from powers(y0)
else:
yield y0
g1 = products(factors)
y1 = y0 * next(g1)
g2 = products(factors[1:])
y2 = next(g2)
while True:
if y1 < y2:
yield y1
y1 = y0 * next(g1)
else:
yield y2
y2 = next(g2)
if __name__ == "__main__":
import itertools
for n in itertools.islice(products([2, 3, 5, 7]), 10**6):
print(n)
毫无疑问,可以对生成器使用递归进行改进,但是实际上对于我们的应用来说,性能已经足够好了。 除此之外,如Will Ness的答案所述,我仍然对如何有效地以给定的最小值开始感兴趣。 感谢所有的贡献者。
我想到了两种算法。 首先,您可以计算数字之间的所有可能乘积,然后对它们进行排序。 尽管这似乎是幼稚的方法,但您可以通过“记住”最后一个乘积,除以一个数字,然后在其位置乘以另一个数字来加快此过程。 如果使用正确的排列顺序(查看格雷码 ),可以最大程度地减少总乘法运算,那么这将大大减少所需的运算次数。
另一方面,您可以做的是计算所有原始数字的集合,(两个)原始数字对的乘积集合,3的乘积集合...依此类推。 然后,您可以对每个单独的集合进行排序(这不难确保无论如何都将它们几乎排序),然后将集合进行合并排序到一个排序的产品列表中。 这将需要更多的操作,但最终会导致列表几乎排序,从而可能花费更少的时间来构建整体。
另一种算法是获取所有感兴趣的素数的乘积,并将其称为P。构造所有原始素数平方的另一个列表。 现在,循环遍历所有数字,直到P,然后测试它们是否可以被质数平方数组中的任何值整除。 如果是的话,就扔他们。 如果不是,则将它们添加到输出数组。 您可以仅通过测试sqrt(i)的除数来优化此性能,其中i是for循环中的迭代。 但是,这仍然可能比上述方法慢。
由于每个素数都可以出现多次,因此序列是无限的。 因此,我们无法生成所有产品然后对其进行排序。 我们必须迭代生成序列。
如果a
是该序列的成员,则{2*a, 3*a, 5*a ... n*a}
也将是该序列的成员,稍后再介绍。
因此,我想到的算法是为下一个序列成员提供一个(排序的,无重复的)缓冲区。 我们提取并呈现出最小值,并将其所有倍数插入缓冲区。
由于很难预测起始数字x
的缓冲区内容,因此该算法应从头开始,并忽略结果,直到达到x
阈值为止。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.