一文搞懂质数以及质数的筛法

唯一分解定理: 对于任何一个数 n = ∏ i = 1 s p i k i 对于任何一个数n = \prod_{i=1}^{s}p_i^{k_i} 对于任何一个数n=i=1spiki

1. 质数:

大于1的整数中,如果只包含1和本身的这两个约数,那么就被叫做质数(素数)

找质数的方法:

  1. 质数的判定 – 试除法; 暴力
  2. 优化版, 对于 d | n , 那么n / d | n, 这些约数是成对出现的, 推导 对于所有存在的d 都有 d ≤ n d d \le \frac{n}{d} ddn 得出 d 2 ≤ n d^2 \le n d2n ,即 d ≤ n d \le \sqrt{n} dn

试除法

function devide(n){
    for(let i = 2;i <= n;i ++){
        if(n % i === 0){
            let s = 0;
            while(n % i === 0){
                n /= i;
                s ++;
            }
            console.log(i, s);
        }
    }
}

一些推论: n中最多只包含一个大于sqrt(n)的质因子

因此只需要枚举到sqrt(n), 然后再判断是否n> 1(即看有没有除尽), 有则剩余的这个数一定是那个大于sqrt(n)的质因子

优化后的代码:

function devide(n){
    for(let i = 2;i <= n / i;i ++){
        let s = 0;
        while(n % i === 0){
            n /= i;
            s ++;
        }
        console.log(i, s);
    }
    if(n > 1) console.log(n, 1);
}

筛素数的方法:

2. 筛质数

1. 朴素筛法

核心思想就是用当前数把他的倍数全部删掉

const primes = [];
const st = new Array(100).fill(false);


function get_primes(n){
    for(let i = 2;i <= n;i ++){
        if(!st[i]) {
            primes.push(i);
        }

        for(let j = 2; i * j <= n;j ++){
            st[i * j] = true;
        }
    }
}
2. 埃氏筛法

(这种算法的思想很重要, 用已经得到的结论去消除未来的部分不定数)

一个合数一定是由质数的多少次方构成的形式 x = p1s1p2s2 p3s3… pksk, 因此只需要拿质数去筛即可。

const primes = [];
const st = new Array(100).fill(false);

const rl = require("readline").createInterface({
    input: process.stdin,
    output: process.stdout
});

function get_primes(n){
    for(let i = 2;i <= n;i ++){
        if(!st[i]) {
            primes.push(i);
            for(let j = 2; i * j <= n;j ++){
                st[i * j] = true;
            }
        }        
    }

    return primes.length;
}

rl.on('line', str => {
    console.log(get_primes(+str))
})

3. 线性筛法

核心思想: 就是用质数去把当前的合数筛掉,并且保证是用当前最小的素数筛掉的

线性筛法的证明:

对于当前的数i, 我们得到了所有2-i的质数的primes数组

然后从小到大去枚举primes数组, i × p r i m e s [ j ] i \times primes[j] i×primes[j] 一定是合数,我们标记为true.

我们要遵循每一个合数只被最小的质数筛掉。(下面所有的primes[j] 都写成pj)

  • i   %   p j ≠ 0 i\ \%\ pj\neq0 i % pj=0; 说明i和pj是互质的,并且pj一定是小于i的(因为primes数组中的数是2-i当中所有的质数),因此i * pj的最小质因数是pj。
  • i   %   p j = 0 i\ \%\ pj=0 i % pj=0; 由于从小到大枚举的primes,pj是i的最小质因子, i × p j i \times pj i×pj的最小质因子是pj。然后我们再往下枚举 i × p j + 1 i \times p_{j+1} i×pj+1的时候,i的最小的质因子是pj, p j + 1 > p j p_{j+1} > pj pj+1>pj i × p j + 1 i\times p_{j+1} i×pj+1的最小质因子是pj,,因此该数应该被pj所筛掉,也就是该数在之前就被晒掉了。所以j之后的数不做处理。
const primes = [];
const st = new Array(100).fill(false);

function get_primes(n){
    for(let i = 2;i <= n;i ++){
        if(!st[i]) primes.push(i);
        for(let j = 0;primes[j] <= n / i;j ++){
            st[primes[j] * i] = true;
            if(i % primes[j] === 0) break; 
        }
    }
}
枚举

3. 筛约数

  1. 试除法求所有的约数
function get_divisors(n){
    const res = [];
    for(let i = 2;i <= n / i;i ++){
        if(n % i === 0){
            res.push(i);
            i != n / i && res.push(n / i);
        }
    }
    console.log(res.sort((a, b) => a - b))
}

get_divisors(300);

4. 约数个数

由于唯一分解定理 n = p 1 s 1 × p 2 s 2 × . . . × p k s k n = p_1^{s_1}\times p_2^{s_2}\times {...} \times p_k^{s_k} n=p1s1×p2s2×...×pksk, 对于 p 1 s 1 p_1^{s_1} p1s1的约数有 p 1 0 p_1^0 p10, p 1 1 p_1^1 p11, …, p 1 s 1 p_1^{s_1} p1s1共s1 + 1个,根据乘法原理得到 n n n的约数个数为 ∏ i = 1 k ( s i + 1 ) \prod_{i = 1}^{k}(s_i + 1) i=1k(si+1)

5. 约数之和

这个原理如出一辙,不多赘述 ,记公式。

( p 1 0 + p 1 1 . . . + p 1 s 1 ) ( p 2 0 + p 2 1 . . . + p 2 s 2 ) . . . ( p k 0 + p k 1 . . . + p k s k ) (p_1^0 + p_1^1 ... + p_1^{s_1})(p_2^0 + p_2^1 ... + p_2^{s_2})...(p_k^0 + p_k^1 ... + p_k^{s_k}) (p10+p11...+p1s1)(p20+p21...+p2s2)...(pk0+pk1...+pksk)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值