算法之——素数(干货+例题)

有关素数的一些算法和定理
素数的判定

  1. 费马小定理——用于素数的快速判定
    对于质数p,任意整数a均满足:ap ≡ a( mod p)

素数的筛选

  1. 素数埃氏筛法
    求区间【2,n】中的所有素数
    1)输出最小的2,再筛掉序列中所有2的倍数,剩下{3,5,7,9,11……n}
    2)输出最小的3,再筛掉序列中所有3的倍数,剩下{5,7,11……n}
    3)输出最小的5,再筛掉序列中所有5的倍数,剩下{7,11……n}
    不断重复上述步骤,直到剩下n

  2. 素数线性筛法(欧拉筛法)
    这是为了解决素数埃氏筛法的不足——对于一个合数来说,例如56,在埃氏筛法中会被多次筛——2、7,但这些重复是没有必要的

    欧拉筛法思想——
    在艾氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达不重复的目的。

    代码——

//欧拉筛法
const int N = 1000001;
int primes[N],cnt;
bool ct[N];

void eluer(int n)
{
    //初始化数组
    memset(ct,false,sizeof(ct));
    cnt = 0;
    for(int i=2; i<n; i++)
    {
        if(!ct[i])
        {
            primes[cnt++] = i;
            //cout << i << endl;
        }
        for(int j=0; j<cnt&&i*primes[j]<n; i++)
        {
            ct[i*primes[j]] = true;
            if(i%primes[j] == 0)
                break;
        }
    }
}

if(i%primes[j] == 0)
break;

这一句是重点——
i%primes[j] == 0说明i是primes[j]的倍数,即 i= k*primes[j] 。如果不break的话,下一次应该就是置 ct[i*primes[j+1]] 为true。

i * primes[j+1] = k * primes[j] * primes[j+1] = primes[j] * k * primes[j+1]

而欧拉筛法所采用的思想就是只用合数的最小素因子去筛,显然不break的话,黄色部分就采用了另一个素因子重复在筛这个合数。

为检验学习成果,特地去找了一道题来刷……果然,我还是个弟弟

很经典的一道例题——Prime Distance
链接:POJ 2689
题目大意:输入两个数表示范围,求这个范围内的相邻素数中间隔最大和最小的两个素数。
解题过程:由于所给数据太大了,不能直接进行求解,可以采取先求出前50000中的所有素数,然后再映射到所给的区间。前一步很容易做到,直接调用模板函数就可以了,后面的映射就有一些麻烦。

进行区间映射:前50000内的素数我们是已知的,如今的关键就是去看所给的范围究竟是前50000得到的素数的几倍,然后把其中的合数筛出来,这里有两种做法:
1)采用求模的方法

2)采用直接相除的方法
左边界的倍数:a = (l-1) / su[i] +1
右边界的倍数: b = r / su[i]

代码——
定义MM上界的时候一定要多加注意,不然会wa

#include <stdio.h>
#include <string.h>
#include <iostream>
#include <algorithm>
using namespace std;

#define MAX 50005
#define MM 1000005 //啊啊啊这个地方坑死我了,千万不要漏掉0不然会报错超时
#define INF 1e9

int su[MAX],f[MM];
bool primes[MAX];
int cnt = 0;

//先筛50000内的素数——欧拉筛法
void get_primes()
{
    //初始化数组
    memset(primes,false,sizeof(primes));

    for(int i=2; i<MAX; i++)
    {
        if(!primes[i])
            su[cnt++] = i;
        for(int j=0; j*su[i]<MAX && j<cnt; j++)
        {
            primes[j*su[i]] = true;
            if(i%su[j] == 0)
                break;
        }
    }
}

int main()
{
    get_primes();
    int l,r;
    while(cin >> l >> r)
    {
        //第一步是映射
        if(l==1)
            l = 2;
        //l如果是1的话,求倍数的时候就会把素数本身当作合数进行标记
        memset(f,0,sizeof(f));
        int a,b;
        for(int i=0; i<cnt; i++)
        {
            a = (l-1)/su[i] +1;
            b = r/su[i];
            for(int j=a; j<=b; j++)
                if(j>1)
                    f[su[i]*j -l] = 1;
        }
        //此时是合数的已经置为1

        //下面可以采取暴力枚举的方法
        int x1,x2,x3,x4,p=-1,max_ele=-1,min_ele=INF;
        for(int i=0; i<=r-l; i++)
        {
            if(f[i]==0)
            {
                if(p==-1)
                {
                    p = i;
                    continue;
                }
                if((i-p)>max_ele)
                {
                    max_ele = f[i]-p;
                    x1 = p+l;
                    x2 = i+l;
                }
                if((i-p)<min_ele)
                {
                    min_ele = f[i]-p;
                    x3 = p+l;
                    x4 = i+l;
                }
                p = i;
            }
        }
        if(max_ele==-1)cout<<"There are no adjacent primes."<<endl;
        else cout<<x3<<","<<x4<<" are closest, "<<x1<<","<<x2<<" are most distant."<<endl;
    }
    return 0;
}


写一道题不容易啊呜呜呜~

  1. 还有一个知识点是反素数
    反素数定义
    对于任意的小于x的正整数 i,都有g(x)>g(i) ,则称x为反素数。
    反素数可化为:? = ?1?1?2?2 … ????
    性质
    1)质因子 ?1, ?2, …, ?? 是从2开始连续的素数
    2)质因子的指数递减?1 ≥ ?2 ≥ ?3 … ≥ ?r

    这里给出一个大佬的博客链接,专门总结了反素数,太腻害了——博客地址

    有关于反素数的例题——
    题目链接BZOJ1053 反素数
    题意:给定一个N,求出不超过N的最大的反素数
    解题过程:刚刚给出的链接大佬博客里面有讲这题,并进行了专门分析,我也拙略的分析一下——
    题目范围≤2∗109,素数相乘得:2∗3∗5∗7∗11∗13∗17∗19∗23∗29∗31>2∗109,且根据性质知素数的指数是递减的,所以可以采取dfs方式。

    代码——

#include <iostream>
#include <string.h>
#include <stdio.h>

using namespace std;
typedef unsigned long long ULL;
const ULL INF = ~0ULL;//无符号长整型的0

int su[]={2,3,5,7,11,13,17,19,23,29,31};

ULL best,p,n;

void dfs(int dept, int plu, int num)
{
    //dept表示数的层数,从0开始,即10
    //plu表示当前的相乘得到的数
    //num表示所含有的质数的个数
    if(dept>=11)    //到达边界值
        return;
    if(num > best)
    {
        //best记录原先所含的最多的质因子
        best = num;
        p = plu;
        //更新反素数的值
    }
    if(num==best && plu<p)
        p = plu;

    //开始递归部分
    for(int i=1; i<=45; i++)
    {
        if(n/su[dept] < plu)
            break;
        dfs(dept+1, plu*=su[dept], num*(i+1));
    }
}


int main()
{
    while(cin >> n)
    {
        p = INF;
        best = 0;
        dfs(0,1,1);
        cout << p << endl;
    }
    return 0;
}



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值