线性筛法

线性筛法

1. 算法分析

关键思想:每个数字都被它的最小质因数筛掉

1.1 线性筛求素数

  1. i   %   p j   =   0 i\ \%\ p_j \ =\ 0 i % pj = 0,说明 p j p_j pj i i i 的最小质因数,因此 i ∗ 质 数 i*质数 i 的最小质因子也是 p j p_j pj,在 i i i 递增的时候也会被筛掉,因此不需要在这里判断,可以break
  2. i   %   p j ≠ 0 i\ \%\ p_j \ne 0 i % pj=0,说明 p j p_j pj i ∗ p j i*p_j ipj 的最小质因数,因此 p j p_j pj 可以筛掉 i ∗ p j i * pj ipj

1.2 线性筛求欧拉函数

  1. i   %   p j   =   0 i\ \%\ p_j \ =\ 0 i % pj = 0,说明 p j p_j pj i i i 的最小质因数, φ ( i ∗ p j ) = i ∗ p j ∗ ∏ i = 1 s p i − 1 p i = p j ∗ ( i ∗ ∏ i = 1 s p i − 1 p i ) = p j ∗ φ ( i ) \varphi(i * p_j) = i * p_j * \prod_{i=1}^{s}\frac{p_i-1}{p_i} = p_j * (i * \prod_{i=1}^{s}\frac{p_i-1}{p_i}) = p_j * \varphi(i) φ(ipj)=ipji=1spipi1=pj(ii=1spipi1)=pjφ(i)
  2. i   %   p j ≠ 0 i\ \%\ p_j \ne 0 i % pj=0, 说明 i i i p j p_j pj 互质, 那么 φ ( i ∗ p j ) = φ ( i ) ∗ φ ( p j ) = φ ( i ) ∗ ( p j − 1 ) \varphi(i*p_j) = \varphi(i) * \varphi(p_j) = \varphi(i) * (p_j - 1) φ(ipj)=φ(i)φ(pj)=φ(i)(pj1)

1.3 线性筛求莫比乌斯函数

莫比乌斯函数 μ ( n ) \mu(n) μ(n)
$\mu(n)= \begin{cases} 1&n=1\ 0&n\text{ 含有平方因子}\ (-1)^k&k\text{ 为 }n\text{ 的本质不同质因子个数}\ \end{cases} $

  1. i   %   p j   =   0 i\ \%\ p_j \ =\ 0 i % pj = 0,说明 p j p_j pj的幂次为2,因此 μ ( i ∗ p j ) = 0 \mu(i*p_j) = 0 μ(ipj)=0
  2. i   %   p j ≠ 0 i\ \%\ p_j \ne 0 i % pj=0, 说明 i i i p j p_j pj 互质, μ ( i ∗ p j ) = μ ( i ) ∗ μ ( p j ) = − μ ( i ) \mu(i * p_j) = \mu(i) * \mu(p_j) = -\mu(i) μ(ipj)=μ(i)μ(pj)=μ(i)

1.4 线性筛求约数个数

d(i) 表示 i 的约数个数,num[i] 表示 i 的最小素因子的个数

  1. i % p j ! = 0 i \% p_j != 0 i%pj!=0, 则 p j p_j pj i ∗ p j i * p_j ipj的最小质因数,且 p j p_j pj这个质因数第一次出现在 i ∗ p j i*p_j ipj内,次数为1,则 n u m [ i ∗ p j ] = 1 , d [ i ∗ p j ] = d [ i ] ∗ 2 num[i * p_j] = 1, d[i * p_j] = d[i] * 2 num[ipj]=1,d[ipj]=d[i]2
  2. i % p j = 0 i \% p_j = 0 i%pj=0, 则 p j p_j pj i ∗ p j i*p_j ipj中的次数加一,对于约数个数,先除以原来的 n u m [ i ] + 1 num[i] + 1 num[i]+1,再乘上新的 n u m [ i ] + 2 num[i] + 2 num[i]+2,则 n u m [ i ∗ p j ] = n u m [ i ] + 1 , d [ i ∗ p j ] = d [ i ] / ( n u m [ i ] + 1 ) ∗ ( n u m [ i ] + 2 ) = d [ i ] / n u m [ i ∗ p j ] ∗ ( n u m [ i ∗ p j ] + 1 ) num[i * p_j] = num[i] + 1, d[i * p_j] = d[i] / (num[i] + 1) * (num[i] + 2) = d[i] / num[i * p_j] * (num[i * p_j] + 1) num[ipj]=num[i]+1,d[ipj]=d[i]/(num[i]+1)(num[i]+2)=d[i]/num[ipj](num[ipj]+1)

1.5 线性筛求约束和

f[i]表示i的约数和,gi表示i的最小质因数的 p 0 + p 1 + . . . + p k p^0+p^1+...+p^k p0+p1+...+pk

  1. i % p j ! = 0 i \% p_j != 0 i%pj!=0, 则 p j p_j pj i ∗ p j i * p_j ipj的最小质因数,且 p j p_j pj这个质因数第一次出现在 i ∗ p j i * p_j ipj内,次数为1,则 f [ i ∗ p j ] = f [ i ] ∗ f [ p j ] , g [ i ∗ p j ] = 1 + p j f[i * p_j] = f[i] * f[p_j], g[i * p_j] = 1 + p_j f[ipj]=f[i]f[pj],g[ipj]=1+pj
  2. i % p j = 0 i \% p_j = 0 i%pj=0, 则 p j p_j pj i ∗ p j i * p_j ipj中的次数加一,则 g [ i ∗ p j ] = g [ i ] ∗ p j + 1 , f [ i ∗ p j ] = f [ i ] / g [ i ] ∗ g [ i ∗ p j ] g[i * p_j] = g[i] * p_j + 1, f[i * p_j] = f[i] / g[i] * g[i * p_j] g[ipj]=g[i]pj+1,f[ipj]=f[i]/g[i]g[ipj]

2. 模板

2.1 线性筛求素数

// 线性筛素数(以下把prime[j]这个数简写为pj)
// 如何保证线性:一个合数只能被筛掉一次
// 如何保证一个数字只被筛掉一次:这个数字只被它的最小质因数筛掉,当它的其他质因数想要筛掉它时,将无法进行筛除操作
void get_prime(int n) {
    for (int i = 2; i <= n; ++i) {
        if (!st[i]) prime[cnt++] = i; // 如果这个数字没有被记录,那么这个数字必然为素数,记录一下
        for (int j = 0; prime[j] <= n/i; ++j) {
            st[prime[j] * i] = true;  // 筛掉pj*i这个合数
            if (i % prime[j] == 0)  break; // i%pj==0,说明pj是i的最小素因子,因此i*素数的最小素因子也是pj,在i递增的时候也会被筛掉,因此不需要在这里判断
        }                
    }
}

2.2 线性筛求欧拉函数

void init(int n) {
    phi[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if (!st[i]) prime[cnt++] = i, phi[i] = i - 1;  // i是质数,phi[i] = i - 1
        for (int j = 0; prime[j] <= n / i; ++j) {
            st[prime[j] * i] = 1;
            if (i % prime[j] == 0) {
                phi[i * prime[j]] = phi[i] * prime[j];  // i % pj == 0, phi[i * pj] = phi[i] * pj
                break;
            }
            phi[i * prime[j]] = phi[i] * (prime[j] - 1);  // i % pj != 0, phi[i * pj] = phi[i] * (pj - 1)
        }
    }
    return;
}

2.3 线性筛求莫比乌斯函数

// 预处理得到莫比乌斯函数
void init(int n) {
    mu[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if (!st[i]) prime[cnt++] = i, mu[i] = -1;  // i是质数,mu[i] = -1
        for (int j = 0; prime[j] <= n / i; ++j) {
            st[prime[j] * i] = 1;
            if (i % prime[j] == 0) {
                mu[i * prime[j]] = 0;  // i % pj==0, mu[i*pj] = 0
                break;
            }
            mu[i * prime[j]] = -mu[i];  // i % pj!=0, mu[i * pj] = -mu[i]
        }
    }
    return;
}

2.4 线性筛求约数个数

// d(i) 表示 i 的约数个数,num[i] 表示 i 的最小素因子的个数 
void init(int n) {
    d[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if(!st[i]) prime[cnt++] = i, d[i] = 2, num[i] = 1;  //i是质数,d[i] = 2, num[i] = 1
        for (int j = 0; prime[j] <= n / i; ++j) {
            st[i * prime[j]] = 1;
            if (i % prime[j] == 0) {  // i % pj == 0, num[i * pj] = num[i] + 1, d[i *pj] = d[i] / num[i * pj] * (num[i * pj] + 1)
                num[i * prime[j]] = num[i] + 1;
                d[i * prime[j]] = d[i] / num[i * prime[j]] * (num[i * prime[j]] + 1);
                break;
            }
            // i % pj != 0, num[i * pj] = 1, d[i * pj] = d[i] * 2
            num[i * prime[j]] = 1;
            d[i * prime[j]] = d[i] * 2;
        }
    }
    return;
}

2.5 线性筛求约数和

// f[i] 表示i的约数和,g[i]表示i的最小质因数的p^0 + p^1 + ... + p^k
void init(int n) {
    g[1] = f[1] = 1;
    for (int i = 2; i <= n; ++i) {
        if (!st[i]) prime[cnt++] = i, g[i] = 1 + i, f[i] = i + 1;  // i是质数,g[i] = 1 + i, f[i] = i + 1
        for (int j = 0; prime[j] <= n / i; ++j) {
            st[i * prime[j]] = 1;
            if (i % prime[j] == 0) {  // i % pj == 0, g[i * pj] = g[i] * pj + 1, f[i * pj] = f[i] / g[i] * g[i * pj]
                g[i * prime[j]] = g[i] * prime[j] + 1;
                f[i * prime[j]] = f[i] / g[i] * g[i * prime[j]];
                break;
            }
            // i % pj != 0, f[i * pj] = f[i] * f[pj], g[i * pj] = 1 + pj
            f[i * prime[j]] = f[i] * f[prime[j]];
            g[i * prime[j]] = 1 + prime[j];
        }
    }
    return ;
}
  • 12
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 线性筛是一种常用的求素数的方,它的思想简单且高效。下面是使用Java语言实现线性筛求素数的代码: ```java import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class LinearSieve { public static List<Integer> getPrimes(int n) { boolean[] isComposite = new boolean[n + 1]; List<Integer> primes = new ArrayList<>(); for (int i = 2; i <= n; i++) { if (!isComposite[i]) { primes.add(i); } for (int j = 0; j < primes.size() && i * primes.get(j) <= n; j++) { isComposite[i * primes.get(j)] = true; if (i % primes.get(j) == 0) { break; } } } return primes; } public static void main(String[] args) { int n = 100; List<Integer> primes = getPrimes(n); System.out.println("从 2 到 " + n + " 的素数为:"); for (int prime : primes) { System.out.print(prime + " "); } } } ``` 以上代码中,我们使用了一个布尔数组`isComposite`来标记是否为合数。初始时,将所有数都标记为非合数。然后从2开始,遍历到n,如果某个数i是合数,则跳过;如果是素数,则将其加入到素数列表中,并标记它的倍数为合数。遍历结束后,我们得到了从2到n的所有素数。 在main函数中,我们设置n为100,调用`getPrimes`函数获取从2到100的素数,并打印出来。 运行结果为:从 2 到 100 的素数为:2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97。 ### 回答2: 线性筛是一种高效地筛选出一定范围内的素数的算法。下面是用Java实现线性筛求素数的代码: ```java import java.util.*; public class LinearSieve { public static List<Integer> sieve(int n) { boolean[] isPrime = new boolean[n + 1]; Arrays.fill(isPrime, true); // 将所有数初始化为素数 List<Integer> primes = new ArrayList<>(); for (int i = 2; i <= n; ++i) { if (isPrime[i]) { primes.add(i); // 将素数加入结果列表 } for (int j = 0; j < primes.size() && i * primes.get(j) <= n; ++j) { isPrime[i * primes.get(j)] = false; // 将当前素数倍数标记为非素数 if (i % primes.get(j) == 0) { break; // 若当前数为素数倍数,跳出内层循环 } } } return primes; } public static void main(String[] args) { int n = 100; // 范围上限 List<Integer> primes = sieve(n); System.out.println("范围[2, " + n + "]内的素数有:"); for (int prime : primes) { System.out.print(prime + " "); } } } ``` 通过线性筛,我们首先将所有数初始化为素数,然后从2开始,将每个素数的倍数标记为非素数,直到筛选结束。最后,将筛选出的素数存入结果列表中。在上述代码中,我们以100为例,调用`sieve`方求解范围内的素数,并输出结果。 当我们运行上述代码时,将会得到范围[2, 100]内的素数列表: ``` 范围[2, 100]内的素数有: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 ``` 以上就是使用Java实现线性筛求素数的代码及结果。 ### 回答3: 线性筛是一种用于求解素数的算法,可以高效地找出某一个范围内的所有素数。下面是使用Java语言实现线性筛求素数的代码: ```java import java.util.ArrayList; import java.util.List; public class PrimeNumbers { public static List<Integer> getPrimeNumbers(int n) { List<Integer> primeNumbers = new ArrayList<>(); boolean[] isComposite = new boolean[n + 1]; for (int i = 2; i <= n; i++) { if (!isComposite[i]) { primeNumbers.add(i); } for (int j = 0; j < primeNumbers.size() && i * primeNumbers.get(j) <= n; j++) { isComposite[i * primeNumbers.get(j)] = true; if (i % primeNumbers.get(j) == 0) { break; } } } return primeNumbers; } public static void main(String[] args) { int n = 100; List<Integer> primeNumbers = getPrimeNumbers(n); System.out.println("在[2, " + n + "]范围内的素数有:"); for (int number : primeNumbers) { System.out.println(number); } } } ``` 这段代码使用了一个布尔数组isComposite来记录某个数是否为合数(非素数),初始时假设所有数都是质数,然后从2开始遍历到n,如果某个数i没有被标记为合数,就将其添加到素数列表中,并将i与已有的质数依次相乘,将其标记为合数。 运行以上代码,可以求解出2到100之间的所有素数。输出结果如下: ``` 在[2, 100]范围内的素数有: 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 ``` 通过线性筛,我们可以高效地找到某个范围内的素数,而不需要遍历所有的数进行判断。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值