zoukankan      html  css  js  c++  java
  • #质数 ~20.8.18

    数学知识

    质数

    定义:在大于1的整数中,如果只包含1和本身这两个约数,就被称为质数(老版本叫素数)。

    质数的判定——试除法(时间O( sprt(n) ))

    ——例题1:AcWing 866. 试除法判定质数

    给定n个正整数ai,判定每个数是否是质数。
    
    输入格式
    第一行包含整数n。
    
    接下来n行,每行包含一个正整数ai。
    
    输出格式
    共n行,其中第 i 行输出第 i 个正整数ai是否为质数,是则输出“Yes”,否则输出“No”。
    
    数据范围
    1≤n≤100,
    1≤ai≤2∗109
    输入样例:
    2
    2
    6
    输出样例:
    Yes
    No 
    

    ————思路:在2 <= i <= n / i中找约数

    bool is_prime(int n){//朴素判定(暴力)
    	if(n < 2) return 0;//小于2的数不在范围内,直接排除
    	for(int i = 2; i < n; i ++)//枚举从2到n-1
    		if(n % i == 0)//如果可以整除某一个数
    			return 0;//就说明不是质数
    	return 1;
    }
    时间复杂度是O(n),效率低。
    

    优化:1,约数是一对一对的,所以每次枚举较小的一个约数就好
    n / d = 整数=> n / (n/d) = 整数
    枚举较小的一个约数即:枚举 d <= (n / d) => d <= sqrt(n) => d <= n / d
    时间O(sqrt(n))

    ————总而言之:d <= n / d 当中一定可以找到所有的约数组当i <= n/i 时,说明还没有遍历到重复的约数组

      bool is_prime(int n){//优化写法
    	if(n < 2) return 0;//小于2的数不在范围内,直接排除
    	for(int i = 2; i <= n / i; i ++)//当i <= n/i 时,说明还没有遍历到重复的约数组
    	/*注意不要写成for(int i = 2; i * i <= n; i ++),会溢出
    	  注意不要写成for(int i = 2; i <= sqrt(n); i ++),每次执行sqrt(n)都耗费时间*/
    		if(n % i == 0)//如果可以整除某一个数
    			return 0;//就说明不是质数
    	return 1;
    }
    

    解答:

    #include<bits/stdc++.h>
    using namespace std;
    
    bool i(int n){//优化写法
    	if(n < 2) return 0;//小于2的数不在范围内,直接排除
    	for(int i = 2; i <= n / i; i ++)//枚举从到n/i
    		if(n % i == 0)//如果可以整除某一个数
    			return 0;//就说明不是质数
    	return 1;
    }
    int main(){
        int n;
        cin >> n;
        while(n --){
            int a;
            cin >> a;
            if(i(a)) cout << "Yes" << endl;
            else cout << "No" << endl;
        }
        
        return 0;
    }
    

    分解质因数

    ——例题2 :AcWing 867. 分解质因数

    ————思路:在 i : 2 ~ (n/i)找到几乎所有的质因数,最后一个质因数就是除剩下的n

    质数定理n中最多只包含一个大于 sqrt(n)的质因子
    枚举:根据刚才的推论sqrt(n) == n / i只需枚举i:2 ~ (n/i);
    如果有大于 sqrt(n)的质因子就将其拎出来单独处理,即:
    当将所有小于sqrt(n)的质因子除尽后,如果余下的n仍大于1,此时n就是最后一个质因子。
    (时间O( log n ~ sprt(n) ))

    ————总而言之:先在2 ~ (n/i)找到几乎所有的质因数,最后一个质因数就是除剩下的n

    解答:

    #include<bits/stdc++.h>
    using namespace std;
    
    void divide(int n){
        for(int i = 2; i <= n / i; i ++)//当i <= n/i 时,说明还没有遍历到重复的约数组
            if(n % i == 0 && n){
                int s = 0;
                while(n % i == 0){
                    n /= i;
                    s ++;
                }
                cout << i <<' '<< s <<endl;
            }
        if(n > 1) cout << n <<' ' << 1 << endl;
        
    }
    
    int main(){
        int n;
        cin >> n;
        while( n --){
            int q;
            cin >> q;
            divide(q);
            cout <<endl;
        }
        return 0;
    }
    

    筛质数

    ——例题3:AcWing 868. 筛质数

    给定一个正整数n,请你求出1~n中质数的个数。
    
    输入格式
    共一行,包含整数n。
    
    输出格式
    共一行,包含一个整数,表示1~n中质数的个数。
    
    数据范围
    1≤n≤106
    输入样例:
    8
    输出样例:
    4
    

    ————思路:从2~n枚举 i,再从小到大枚举所有已知的质数 primes[j],筛掉合数 i*primes[j],遇到新的质数就入队

    ——————朴素做法

    枚举所有小于n的数i,将i的所有倍数筛掉
    筛完后剩下的数就是质数

    朴素做法
    void get_primes(int n ){
        for(int i = 2; i <= n; i ++){
        	if(!st[i])
        		primes[cnt ++] = i;//如果是质数,入队
        	for(int j = i + i; j <= n; j +=i)
        		st[j] = 1;//删掉它的所有倍数
    	}
    }
    时间分析:n/2 + n/3 +....+n/n = n log n(大概)
    
    ——————朴素做法的优化:埃氏筛法``当i不是质数时,没必要筛掉它的倍数

    当i不是质数时,没必要筛掉它的倍数
    质数定理:1~n当中有 n / logn 个质数
    时间是O(n log log n)约等于O(n),和O(n)一个级别
    此算法由一个埃及人发明,所以叫埃氏筛法
    注:没被筛掉,说明是质数:因为对于任意数N而言,如果没有被筛掉,就说从2到i-1中没有i的约数,即N是质数。

    埃氏筛法
    void get_primes(int n ){
        for(int i = 2; i <= n; i ++){
            if(!st[i]) {
            	primes[cnt ++] = n;//没被筛掉,说明是质数
            	for(int j = i + i; j <= n; j += i)//干掉它的所有倍数
            		st[j] = 1;
            }	
        }
    }
    时间是O(n log log n)O(n)一个级别
    
    ——————最好的方法----线性筛法求质数

    原理:
    核心思路:n只会被n的最小质因子筛掉.
    实现思路:
    第一种情况:
    i % primes[ j ] == 0 => primes[ j ] 一定是i的最小质因子. => primes[ j ] 一定是primes[ j ] * i的最小质因子.

    第二种情况:
    i % primes[ j ] != 0 由于是从小到大枚举的质数,且没有枚举到i的任何一个质因子,说明primes[ j ]一定小于i的最小质因子.
    那么primes[ j ] 也一定是primes[ j ] * i的最小质因子.

    也就是不管什么情况,if(!st[i]) primes[cnt ++] = i;这句话是一定成立的.

    这样下来,所有小于i的合数都一定会被筛掉:
    证明:对于任意一个合数x,假设primes[j]是x的最小质因数,i一定会在x之前枚举到x/primes[j],这时x就会被筛掉.
    举例:比如x=12时,x的最小质因数primes[j] = 2,那么i一定会在12之前枚举到x/primes[j] = 6,此时就会把2*6 = 12 = x删掉.

    时间:数据范围在1e7以上的时候,线性筛法埃氏筛法快一倍~

    void get_primes(int n ){
        for(int i = 2; i <= n; i ++){							//从2~n 枚举 i
            if(!st[i]) primes[cnt ++] = i;						//没被筛掉说明是质数,将这个新的质数加入primes[]里
            for(int j = 0; primes[j] <= n / i; j ++){			//从小到大枚举所有已知的质数 primes[j]
            /*	 当质数大于n / i的时候break;
           		 等价于 primes[j] * i <= n;也就是筛掉所有小于n的合数就可以了
    		*/
                st[primes[j] * i] = 1;							//筛掉合数 i*primes[j]
                if(i % primes[j] == 0) break;//当这句话发生的时候,就说明primes[j]一定是i的最小质因子,那么用i的最小质因子筛掉i的目的已经达成了,所以跳出循环.
            }
        }
    }
    
    ——————总而言之,筛质数最好的办法就是 :从2~n枚举 i,再从小到大枚举所有已知的质数 primes[j],筛掉合数 i*primes[j]

    解答3:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N = 1000010;
    
    int primes[N], cnt;
    bool st[N];
    
    void get_primes(int n ){
        for(int i = 2; i <= n; i ++){
            if(!st[i]) primes[cnt ++] = i;
            for(int j = 0; primes[j] <= n / i; j ++){
                st[primes[j] * i] = 1;
                if(i % primes[j] == 0) break;
            }
        }
    }
    
    int main(){
        int n;
        cin >> n;
        
        get_primes(n);
        
        cout << cnt << endl;
        
        return 0;
    }
    
  • 相关阅读:
    Sql Server 查询一段日期内的全部礼拜天
    struts-config message-resources配置问题总结
    ImageTag小案例
    Struts1小案例问题教训总结(含中文乱码问题及解决)
    资源文件properties的存放路径
    BodyTagSupport小案例1
    客户端跳转与服务器跳转的区别
    href中使用相对路径访问上级目录的方法
    Tomcat下配置多个Project的虚拟主机
    win7 telnet命令无法开启的解决方案(不是内部命令或外部命令)
  • 原文地址:https://www.cnblogs.com/yuanyulin/p/14026751.html
Copyright © 2011-2022 走看看