Bug -- 线性筛素数
发现 bug 的题目
bug 代码
void get_primes(int n)
{
for(int i = 2; i < n; i ++ )
{
if(!st[i]) primes[idx ++ ] = i;
for(int j = 0; primes[j] < n / i ; j ++ ) // (1)
{
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}
看起来人畜无害的样子,甚至还在 \((1)\) 将乘法变为除法防止溢出。但其实,问题就出现在这里!
除法,是可能不能整除的!如果不能整除,\(C\)++ 默认下取整。
又因为,我们的比较符号是小于等于,这就导致下取整得到的结果我们取不到了!
所以,在下面的 st[primes[j]*i]=true;
中,我们就会少判断了一些 primes[j]
因为下取整导致循环提前结束掉了!
但是防止溢出却是是个好技巧,我们不能丢弃了,那怎么办呢?上取整即可。
此时小于上取整的结果,等价于小于等于下取整的结果。
正确代码
如果我们 \(n\) 除 \(k\) 时希望上取整,将 \(n\) 加上 \(k-1\) 即可。
void get_primes(int n)
{
for(int i = 2; i < n; i ++ )
{
if(!st[i]) primes[idx ++ ] = i;
for(int j = 0; primes[j] < (N + i - 1) / i ; j ++ ) // (1) 防溢出 + 上取整
{
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}
或者说,在判断时不要用小于,而是小于等于。
void get_primes(int n)
{
for(int i = 2; i <= n; i ++ ) // (1) <=
{
if(!st[i]) primes[idx ++ ] = i;
for(int j = 0; primes[j] <= n / i ; j ++ ) // (2) <=
{
st[primes[j] * i] = true;
if(i % primes[j] == 0) break;
}
}
}