UVA10200-Prime Time/HDU2161-Primes,例题讲解,牛逼的费马小定理和欧拉函数判素数。

                                               
10200
- Prime Time

此题极坑(本菜太弱),鉴定完毕,9遍过。

题意:很简单的求一个区间[a,b]内满足i*i+i+41(i>=a&&i<=b,0<=a<=b<=10000.)是素数的数有多个,求出百分比。

思路:直接裸判就行了(竟然不超时),但结果要加上1e-8(are you kidding me?)。

下面来说说我怎么跪了,开始也是直接裸判,我算的时间复杂度会超时,果然超时了。后来打个素数表对某些数特判一些,但表最多打1e7,可是数据可以达到1e8,还是超时了。后来又直接把1e4内的素数全部筛出来,一个个判,果断超时。没办法了,只能考虑其他方法了。想到kuangbin模板上有一个Miller_Rabin素数测试,我猜可能是考察这部分知识,我对这个还不熟,于是由又去看了看,结果发现其实这个算法依据的就是费马小定理。我瞬间明白了,依据费马小定理直接用快速幂判断,时间复杂度不高,但结果居然WA了,,我看模板上说的是可能存在伪素数,但WA的结果不得不让我相信(其实是结果没加1e-8)。

由费马小定理:如果p是一个素数,那么任意整数a的euler(p)与1模p同余,也就是模p等于1。那么可以依据这个:如果一个任意数的x-1模x与1同余那么可以判断x是否为素数(kuangbin模板随机素数测试不部分)。虽然没有找到证明,但我觉得前辈是可信的,但这种方法WA了。

于是我又换了一种思路:我们知道一个素数p的欧拉函数(比这个数小并且与这个数互质的的个数)是p-1,那么是否可以用判断一个数p的欧拉函数是否为p-1来判断p是否为素数呢,当然也没有找到相关证明。但我觉得这个逆推的过程与上面依据费马小定理逆推应该差不多。于是用这个方法试了试,,超时。。。。(are you kidding me?)欧拉函数要比快速幂更优啊。

以上两种思路因为没有可靠的证明,于是我和正在去往宁波路上的黄学长交流了一下,也没有得出什么可靠的结果,但彼此互相鼓励了一下,希望他们国赛有个好的发挥吧。没办法,看起来一个很水的题居然搞了,搜了一下题解发现居然就是坑爹的裸判就行了,但网上的方法是把结果打个表,查询方便点。后来把所有的方法都试过之后我才发现原来超时是由于多组测试数据。。。要打表才能过。但以上提到的方法全试了试,全部过了,这让我很兴奋。这是一个新的发现,从来没想过除了开方裸判或者筛法打表还有什么其他的方法来判素数。学了数论之后才发现还可以用牛逼的费马小定理和欧拉函数。

但由于怕结果没有可靠性,又找了个裸素数的题试了试,还是过了,这已经足够证明用欧拉函数或者费马小定理+快速幂判素数的可靠性了。

下面给出几个例题与AC代码:

UVA 10200 - Prime Time

①费马小定理+快速幂:

const int N=1e4+10;
int n,m,len=0,a[N],b[N];
int judge(ll x)
{
ll a=2,b=x-1,res=1;
while(b)
{
if(b&1) res=res*a%x;
a=a*a%x;
b=b>>1;
}
return res==1;
}
int get_ans()
{
int ans=0;
for(int i=n; i<=m; i++)
{
ll x=i*i+i+41;
if(judge(x)) ans++;
}
return ans;
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
int ans=get_ans();
printf("%.2f\n",ans*1.0/(m-n+1)*100+1e-8);//结果一定要加1e-8,原来中间WA的两次就是没加,但至今不造为什么。
}
return 0;
}

②裸判:

int n,m,sum[N];
int judge(int x)
{
for(int i=2; i*i<=x; i++)
if(x%i==0) return 0;
return 1;
}
void get_ans()
{
memset(sum,0,sizeof(sum));
for(int i=0; i<=1e4; i++)
{
int x=i*i+i+41;
if(judge(x)) sum[i]=1;
}
}
int main()
{
get_ans();
while(~scanf("%d%d",&n,&m))
{
int ans=0;
for(int i=n;i<=m;i++)
ans+=sum[i];
// printf("%d\n",ans);
printf("%.2f\n",ans*1.0/(m-n+1)*100+1e-8);
}
return 0;
}

③欧拉函数的可以自己试试,代码略。

HDU
2161
   Primes 

裸素数判定,筛法打表想怎么做就怎么做,但这里主要介绍用欧拉函数或费马小定理+快速幂法。

①欧拉函数法:

int euler(int x)
{
int ans=x,xx=x;
for(int i=2;i*i<=x;i++)
{
if(x%i==0)
{
ans-=ans/i;
while(x%i==0) x/=i;
}
}
if(x>1) ans-=ans/x;
return ans==xx-1;
}
int main()
{
int n,t=1;
while(~scanf("%d",&n)&&n>0)
{
printf("%d: ",t++);
if(euler(n)&&n!=2)
printf("yes\n");
else printf("no\n");
}
return 0;
}

②费马小定理+快速幂法:这个开始WA了一发,聪明的你知道用这种方法要注意什么吗?答案在下面。

int fm_fast(int x)
{
int a=2,b=x-1,res=1;
while(b)
{
if(b&1) res=res*a%x;
a=a*a%x;
b=b>>1;
}
return res==1;
}
int main()
{
int n,t=1;
while(~scanf("%d",&n)&&n>0)
{
printf("%d: ",t++);
if(fm_fast(n)&&n>2)//这里开始是n!=2,测样例的时候没看清,原来如果n为1也算,所以要n>2。这坑蛮有意思。
printf("yes\n");
else printf("no\n");
}
return 0;
}

一晚上没搞出什么,但这个发现让我惊喜不已,可能早有别的大牛研究出来了,但毕竟这是自己总结发现的,再次领略到数学的魅力精髓。

上一篇:iOS10 后 http 网页定位失效解决方案


下一篇:关于 GET、POST、表单、Content-Type