我正在研究编程访谈的元素,我遇到了问题。 它是关于编写一个c ++函数,用于查找给定n的所有素数,从1到n。
vector<int> generate_primes_from_1_to_n(const int &n) {
int size = floor(0.5 * (n - 3)) + 1;
// is_prime[i] represents (2i+3) is prime or not
vector<int> primes; // stores the primes from 1 to n
primes.push_back(2);
vector<bool> is_prime(size, true);
for(long i = 0; i < size; ++i) {
if(is_prime[i]) {
int p = (i << 1) + 3;
primes.push_back(p);
// sieving from p^2, whose index is 2i^2 + 6i + 3
for (long j = ((i * i) << 1) + 6 * i + 3; j < size; j += p) {
is_prime[j] = false;
}
}
}
}
特别是,我无法理解p ^ 2的评论'sieving,其索引是2i ^ 2 + 6i + 3'部分。对于其他部分,我可以粗略地了解它们是如何工作的,但我不知道这个'2i ^ 2 + 6i + 3'来自哪里,它做了什么,以及它和它的相关部分代码工作。
有人能更好地解释这段代码吗? 谢谢。
+
我得到了这个输出(+'cout'可以更好地理解它)
./a.out 100
size is: 49
for i = 0 is_prime[i] is 1
pushing back p of size 3
((i * i) << 1) + 6 * i + 3 for i of 0 is 3
((i * i) << 1) + 6 * i + 3 for i of 0 is 6
((i * i) << 1) + 6 * i + 3 for i of 0 is 9
((i * i) << 1) + 6 * i + 3 for i of 0 is 12
((i * i) << 1) + 6 * i + 3 for i of 0 is 15
((i * i) << 1) + 6 * i + 3 for i of 0 is 18
((i * i) << 1) + 6 * i + 3 for i of 0 is 21
((i * i) << 1) + 6 * i + 3 for i of 0 is 24
((i * i) << 1) + 6 * i + 3 for i of 0 is 27
((i * i) << 1) + 6 * i + 3 for i of 0 is 30
((i * i) << 1) + 6 * i + 3 for i of 0 is 33
((i * i) << 1) + 6 * i + 3 for i of 0 is 36
((i * i) << 1) + 6 * i + 3 for i of 0 is 39
((i * i) << 1) + 6 * i + 3 for i of 0 is 42
((i * i) << 1) + 6 * i + 3 for i of 0 is 45
((i * i) << 1) + 6 * i + 3 for i of 0 is 48
for i = 1 is_prime[i] is 1
pushing back p of size 5
((i * i) << 1) + 6 * i + 3 for i of 1 is 11
((i * i) << 1) + 6 * i + 3 for i of 1 is 16
((i * i) << 1) + 6 * i + 3 for i of 1 is 21
((i * i) << 1) + 6 * i + 3 for i of 1 is 26
((i * i) << 1) + 6 * i + 3 for i of 1 is 31
((i * i) << 1) + 6 * i + 3 for i of 1 is 36
((i * i) << 1) + 6 * i + 3 for i of 1 is 41
((i * i) << 1) + 6 * i + 3 for i of 1 is 46
for i = 2 is_prime[i] is 1
pushing back p of size 7
((i * i) << 1) + 6 * i + 3 for i of 2 is 23
((i * i) << 1) + 6 * i + 3 for i of 2 is 30
((i * i) << 1) + 6 * i + 3 for i of 2 is 37
((i * i) << 1) + 6 * i + 3 for i of 2 is 44
for i = 3 is_prime[i] is 0
for i = 4 is_prime[i] is 1
pushing back p of size 11
for i = 5 is_prime[i] is 1
pushing back p of size 13
for i = 6 is_prime[i] is 0
for i = 7 is_prime[i] is 1
pushing back p of size 17
for i = 8 is_prime[i] is 1
pushing back p of size 19
for i = 9 is_prime[i] is 0
for i = 10 is_prime[i] is 1
pushing back p of size 23
for i = 11 is_prime[i] is 0
for i = 12 is_prime[i] is 0
for i = 13 is_prime[i] is 1
pushing back p of size 29
for i = 14 is_prime[i] is 1
pushing back p of size 31
for i = 15 is_prime[i] is 0
for i = 16 is_prime[i] is 0
for i = 17 is_prime[i] is 1
pushing back p of size 37
for i = 18 is_prime[i] is 0
for i = 19 is_prime[i] is 1
pushing back p of size 41
for i = 20 is_prime[i] is 1
pushing back p of size 43
for i = 21 is_prime[i] is 0
for i = 22 is_prime[i] is 1
pushing back p of size 47
for i = 23 is_prime[i] is 0
for i = 24 is_prime[i] is 0
for i = 25 is_prime[i] is 1
pushing back p of size 53
for i = 26 is_prime[i] is 0
for i = 27 is_prime[i] is 0
for i = 28 is_prime[i] is 1
pushing back p of size 59
for i = 29 is_prime[i] is 1
pushing back p of size 61
for i = 30 is_prime[i] is 0
for i = 31 is_prime[i] is 0
for i = 32 is_prime[i] is 1
pushing back p of size 67
for i = 33 is_prime[i] is 0
for i = 34 is_prime[i] is 1
pushing back p of size 71
for i = 35 is_prime[i] is 1
pushing back p of size 73
for i = 36 is_prime[i] is 0
for i = 37 is_prime[i] is 0
for i = 38 is_prime[i] is 1
pushing back p of size 79
for i = 39 is_prime[i] is 0
for i = 40 is_prime[i] is 1
pushing back p of size 83
for i = 41 is_prime[i] is 0
for i = 42 is_prime[i] is 0
for i = 43 is_prime[i] is 1
pushing back p of size 89
for i = 44 is_prime[i] is 0
for i = 45 is_prime[i] is 0
for i = 46 is_prime[i] is 0
for i = 47 is_prime[i] is 1
pushing back p of size 97
for i = 48 is_prime[i] is 0
这对我来说也没有意义。
例如,为什么对于p = 5,它开始从11中删除它,而不是5 ^ 2 = 25,在下面的行中? 推回5号的p ((i * i)&lt;&lt; 1)+ 6 * i + 3 for i of 1 is 11
另外,11不是素数? 这真的令人困惑。 请帮我。 谢谢。
答案 0 :(得分:20)
您的素数生成器代码使用的算法称为“Eratosthenes的筛子”。通常,它会创建一个数字列表,并在列表上进行迭代。从列表中删除当前数字的所有乘法,其余数字为素数。
例如,让我们考虑[2,3,4,5,6,7,8,9,10,11,12,13,14,15]
。我们遇到2,所以我们从列表中删除所有偶数:
[2,3,5,7,9,11,13,15]
同样的3:
[2,3,5,7,11,13]
5
,7
,11
和13
在列表中没有乘法,因此我们不删除任何内容并保留一个素数列表。
在这个例子中(由维基百科提供),2,3和5的所有乘法都从列表中删除 - 2的乘法绘制为粉红色,3的乘法绘制为绿色,乘以5为深蓝色。接下来将重复7,因此它会突出显示。深色数字是素数,浅色数字不是素数,灰色数字已经达到净值。
如@BenJackson所述,您的代码优化了两次:
n<p
当n*p
的乘法被筛选时,n
已被筛选。这就是为什么这个神秘的评论:
// sieving from p^2, whose index is 2i^2 + 6i + 3
假设我们的算法已到达向量中的第二项,因此i=2
。有问题的数字是5,因为索引i
表示数字2i+3
(第一次优化)。
我们应该从5
开始筛选25
的所有乘法。代表25
的索引为11
,因为25=2*11+3
。在打印输出后,它会移除索引11, 16, 21, 26, ...
,它们对应于数字25, 35, 45, 55, ..
- 我们想删除的所有5的奇数乘法。
您可以在Wikipedia或Wolfram MathWorld阅读更多相关信息,并且有一个很好的javascript on-line demonstration here。
答案 1 :(得分:8)
素数表只存储从3开始的奇数值(显然,偶数值不能为素数)。关系在行int p = (i << 1) + 3
或p = 2i + 3
中给出。现在解决i
的问题,得到i = (p - 3)/2
。现在i
对应p^2
是什么?将(2i+3)^2
插入第二个公式并简化。现在i
p^2
i
为p
。
示例:假设为i=1
,因此条目is_prime[i]
是对素数p=2i+3
或p=5
的测试。所以,是的,这是最重要的。现在seive(在别处解释)想要开始在25处标记非素数。它需要知道i
对应于什么。现在你可以简单地计算p*p
然后将其插入{{1}得到i=(p-3)/2
。代码已跳过这些中间步骤(如上所示)来计算j=11
并直接获得j=2i^2+6i+3
。
答案 2 :(得分:1)
这样的行:
vector<int> generate_primes_from_1_to_n(const int &n) {
int size = floor(0.5 * (n - 3)) + 1;
...
for(long i = 0; i < size; ++i) {
if(is_prime[i]) {
int p = (i << 1) + 3;
是'hack',因此可以通过在i
中迭代0,1,2,3并使用p
来对应的潜在素数3,5,7等进行迭代可能的素数3,5,7,9等
除此之外,它是一个基本的主要筛子。
答案 3 :(得分:1)
正如其他人所说, i 映射数组位置, p 映射这些数组位置所代表的数字,根据公式 p = 2 i + 1.如果您在程序中明确地保留 i 和 p ,可能会更容易思考:
function primes(n)
m := floor((n-1)/2)
sieve := makeArray(0..m-1, True)
i := 0; p := 3; ps := [2]
while p * p <= n
if sieve[i]
append p to ps
j := (p*p - 3) / 2
while j < m
sieve[j] := False
j := j + p
i := i + 1; p := p + 2
while i < m
if sieve[i]
append p to ps
i := i + 1; p := p + 2
return ps
原始代码中 j 的奇怪公式是通过采用上面显示的 j 的公式并用 i 重写来形成的of p 。
如果您对使用素数进行编程感兴趣,我在我的博客上有一个essay,其中包括深入讨论这个确切的公式。