【发布时间】:2021-12-19 21:28:31
【问题描述】:
我一直在玩 Haskell,发现它很吸引人,尤其是 Lazy Evaluation 功能,它允许我们使用(可能)无限列表。
由此推导出the Sieve of Eratosthenes 的漂亮实现,以获得无限的素数列表:
primes = sieve [2..]
where sieve (x:xs) = x : sieve [i | i <- xs, i `mod` x /= 0]
仍然使用haskell,我可以有:
takeWhile (<1000) primes
这给了我直到 1000 (n) 的素数,或者
take 1000 primes
这给了我前 1000 个素数
我尝试在 Javascript 中实现这一点,忘记了“无限”的可能性,这就是我想出的:
const sieve = list => {
if (list.length === 0) return []
const first = list.shift()
const filtered = list.filter(x => x % first !== 0)
return [first, ...sieve(filtered)]
}
const getPrimes = n => {
const list = new Array(n - 1).fill(null).map((x, i) => i + 2)
return sieve(list)
}
它工作得很好(如果我没有达到最大调用堆栈大小),但我只能得到素数“直到”n。
我如何使用它来实现一个返回“前 n 个”素数的函数?
我尝试了很多方法,但无法让它发挥作用
奖金
有什么方法可以使用尾调用优化或其他方法来避免大型 N 的 StackOverflows?
【问题讨论】:
-
可能值得将其转换为生成器。然后,您可以拥有一个非常通用的
take和takeUntil可迭代助手,它们将与您的主要生成器一起使用。 -
我会在明天试一试,除非在那之前有人尝试过。现在该睡觉了。但这里是步骤:保留以
2开头的素数列表。对于以下每个数字,检查它是否是素数的倍数,如果是(Erathostenes)则丢弃它,如果不是,则放弃并保留它。或者,您可以生成素数的所有倍数(直到某个限制)并将它们保存在一个集合中。然后检查nonPrimes.has(i)来决定。有了take和takeUntil就很容易了 - 第一个采用可迭代和n并产生n值,另一个采用可迭代和谓词并检查并产生 -
切记不要落入“运行递归代码而不缓存结果”的陷阱。无论是斐波那契数列还是埃拉托色尼筛,维护一个已找到值的全局列表,这样您就不会一遍又一遍地重新运行计算,并且 [...] - 请咨询您的 LUT 以查看“ up to where" 你已经知道结果,并直接使用它,而不是递归。
-
啊,抱歉,您需要
n素数,而不是n以下的所有素数。老实说...从更高的n=D 开始(感谢 Gauss,他给了我们π(x)) -
(请注意,从技术上讲,筛分方法,而且我们正在谈论数学,所以这很重要,不会计算“n 个素数”的 erestothenes 筛子。利用 π(x)=n 作为“预备步骤”筛分具有数学意义)
标签: javascript primes