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;
        }
    }
}
posted @ 2023-03-11 11:12  光風霽月  阅读(11)  评论(0编辑  收藏  举报