C语言程序设计漫谈之从最简真分数的个数谈起Word格式.docx

上传人:b****6 文档编号:19473994 上传时间:2023-01-06 格式:DOCX 页数:17 大小:23.69KB
下载 相关 举报
C语言程序设计漫谈之从最简真分数的个数谈起Word格式.docx_第1页
第1页 / 共17页
C语言程序设计漫谈之从最简真分数的个数谈起Word格式.docx_第2页
第2页 / 共17页
C语言程序设计漫谈之从最简真分数的个数谈起Word格式.docx_第3页
第3页 / 共17页
C语言程序设计漫谈之从最简真分数的个数谈起Word格式.docx_第4页
第4页 / 共17页
C语言程序设计漫谈之从最简真分数的个数谈起Word格式.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

C语言程序设计漫谈之从最简真分数的个数谈起Word格式.docx

《C语言程序设计漫谈之从最简真分数的个数谈起Word格式.docx》由会员分享,可在线阅读,更多相关《C语言程序设计漫谈之从最简真分数的个数谈起Word格式.docx(17页珍藏版)》请在冰豆网上搜索。

C语言程序设计漫谈之从最简真分数的个数谈起Word格式.docx

能同时被2、3和5整除的数有2010÷

5)=67

能同时被2、3和67整除的数有2010÷

67)=5

能同时被2、5和67整除的数有2010÷

67)=3

能同时被3、5和67整除的数有2010÷

67)=2

能同时被2、3、5和67整除的数有2010÷

67)=1

这样,1~2010中能被2或3或5或67整除的数有

(1005+670+402+30)-(335+201+15+134+10+6)+(67+5+3+2)-1

=2107-701+77-1 

=1482

因此,1~2010中既不能被2整除,也不能被3整除,也不能被5整除,也不能被67整除的数有2010-1482=528个。

即以2010为分母的最简真分数有528个。

我们可以看出,上面的计算过程是比较繁琐的,需要认真仔细。

学习过程序设计后,可以编写了一个简单的循环程序解决这个问题。

用一个变量cnt来保存最简真分数的个数,初始值为0。

对1~2010中的每一个数num,进行判断,这是一个循环,写成

for(num=1;

num<

=2010;

num++)

循环体中的判断方法为:

如果num既不能被2整除,也不能被3整除,也不能被5整除,也不能被67整除,则计数。

写成

if(num%2!

=0&

&

num%3!

num%5!

num%67!

=0) 

cnt++;

最后,输出结果cnt。

一个简单的程序,就得到问题的答案。

编写的源程序如下:

#include<

stdio.h>

intmain()

intcnt,num;

cnt=0;

for(num=1;

if(num%2!

cnt++;

printf("

%d\n"

cnt);

return0;

}

需要说明的是,当时竞赛的真题是:

所有以2010为分母的最简真分数的和为多少?

将上面的程序简单改写一下,可以很快得到答案的。

intnum;

doublesum;

sum=0;

if(num%2!

sum+=1.0*num/2010;

%lf\n"

sum);

程序运行后,输出264.000000。

即所有以2010为分母的最简真分数的和是264。

小朋友是没法像程序一样硬算的。

1/2010+7/2010+11/2010+…+2099/2010=264。

小朋友有小朋友的聪明,1/2010是最简真分数,那么2099/2010也一定是最简真分数。

i/2010是最简真分数,那么(2010-i)/2010也一定是最简真分数。

1/2010+2099/2010=1 

i/2010+(2010-i)/2010=1。

小朋友知道了以2010为分母的最简真分数有528个,因此它们的和为528/2=264。

因为2010分解质因数后,因数有2、3、5和67四个,用于考察集合的包含与容斥计算量略大但又可以完成,可以算是一道很好的竞赛试题。

在这道试题的基础上,我们看这样一个问题。

【例1】最简真分数。

任意输入一个正整数n,求以n为分母的最简真分数有多少个?

(1)编程思路1。

将输入的n作为分母,穷举分子i(1≤i≤n-1)。

因此,程序可先写成如下的循环:

for(i=1;

i<

=n-1;

i++)

{

对每一分数i/n,进行是否存在公因数的检测。

根据检测的结果决定是否计数;

}

在上面的循环体中需要对每一分数i/n,进行是否存在公因数的检测。

如果分子i与分母n存在大于1的公因数k,说明i/n不是最简真分数,不予计数。

怎样进行检测呢?

因为公因数k的取值范围为[2,i],因而设置u循环在[2,i]中穷举k,若满足条件

i%k==0&

n%k==0

说明分子分母存在公因数k,标记t=1后退出。

在对因子k进行循环穷举前,可设置标志t=0。

退出因子穷举循环后,若t=1,说明分子和分母存在公因子;

若保持原t=0,说明分子分母无公因数,统计个数。

(2)源程序1。

intn,i,k,t,cnt;

while(scanf("

%d"

&

n)&

n!

=0)

i<

i++) 

//穷举分子 

{

t=0;

for(k=2;

k<

=i;

k++) 

//穷举因数 

if(i%k==0&

n%k==0) 

t=1;

break;

//分子分母有公因数舍去 

if(t==0)

//统计最简真分数个数 

printf("

return0;

将上面的源程序提交给 

POJ2407“Relatives”,判定为TimeLimitExceeded。

POJ2407的题意是:

输入正整数N,求小于或等于N([1,N]),且与N互质的正整数(包括1)的个数。

这与求最简真分数的意思完全一致。

上面源程序1的方法简单直接,但对于N值较大的话,会超时的。

因此,我们应找到快速的求法。

在数论中,欧拉函数就很好地解决了这样的问题。

在数论,对于正整数n,欧拉函数是小于或等于n的正整数中与n互质的数的数目。

此函数以其首名研究者欧拉命名,一般简记为φ函数。

例如,φ(8)=4,因为1,3,5,7均和8互质。

一般来说,设正整数N分解质因数后,N=P1^q1*P2^q2*...*Pn^qn.

则 

φ(N)=N*(1-1/P1)*(1-1/P2)*...*(1-1/Pn)。

例如,10=2*5 

φ(10)=10×

(1-1/2)×

(1-1/5)=4;

这4个数是1,3,7,9。

30=2*3*5 

φ(30)=30×

(1-1/3)×

(1-1/5)=8;

这8个数是1,7,11,13,17,19,23,29。

按欧拉函数的求法,可以编写如下的源程序。

(3)源程序2。

intn,i,ans,t;

&

ans=n;

t=n;

for(i=2;

i*i<

=t;

i++)

if(t%i==0) 

//找到一个质因数i

ans-=ans/i;

while(t%i==0) 

//将质因数i全去掉

t/=i;

if(t!

=1)ans-=ans/t;

ans);

将源程序2提交给POJ2407,可以Accepted。

将此源程序的printf("

改写为 

n-ans-1);

后,提交给 

HDU1787“GCDAgain”,也可以Accepted。

【例2】还是最简真分数。

输入一个正整数n,求分母在指定区间[2,n]的最简真分数共有多少个?

例如,输入5,输出应为9。

这9个最简真分数是{1/5,1/4,1/3,2/5,1/2,3/5,2/3,3/4,4/5}。

(1)编程思路。

例1的源程序2可以求欧拉函数φ(n)的值。

用一个循环求出φ

(2)+φ(3)+…+φ(n)的累加和,即得本题的输出。

(2)源程序。

intn,i,k,ans,t,sum;

for(k=2;

=n;

k++)

ans=k;

t=k;

for(i=2;

if(t%i==0) 

//找到一个质因数i

//将质因数i全去掉

if(t!

sum+=ans;

将此源程序提交给 

POJ2478“FareySequence”,被判定为TimeLimitExceeded。

POJ2478的题意是:

求1~n的欧拉函数的和。

因为,例1中是在 

O(sqrt(n))的时间内求出一个数n的欧拉函数值。

如果要求100000以内所有正整数的欧拉函数值,上面程序采用的方法的复杂度将高达O(N*sqrt(N))。

因此,容易超时。

下面我们寻求更快的求欧拉函数值的方法。

我们知道,欧拉函数值 

φ(n)=n*(1-1/p1)*(1-1/p2)....*(1-1/pk),其中p1、p2…pk为n的所有质因子。

即欧拉函数的值与其质因子有关。

用筛法可以方便地求出n以内的所有质数。

那么我们能不能在筛法求质数的同时求出所有数的欧拉函数呢?

可以采用如下的方法:

用筛法一边筛出N以内的所有质数,一边以类似于筛法的思想用质数筛出每个数的欧拉函数φ值。

这里,利用了欧拉函数的几个基本性质:

①若N是质数p的k次幂,φ(N)=p^k-p^(k-1)=(p-1)p^(k-1),因为除了p的倍数外,其他数都跟N互质。

②当N是质数时,φ(N)=N-1。

显然,因为N是质数,1~N-1均与N互质。

③欧拉函数是积性函数——若m,n互质,φ(m*n)=φ(m)*φ(n)。

④ 

假设质数p能整除n,那么

如果p还能整除n/p, 

φ(n) 

=φ(n/p)*p;

如果p不能整除n/p, 

φ(n) 

=φ(n/p)*(p-1)。

定义数组phi[N],元素phi[i]表示正整数i的欧拉函数值φ(i)。

定义数组vis[N],元素vis[i]=True表示i在筛子中,vis[i]=false表示i不在筛子中,已被筛掉。

初始时,vis数组的元素全置为true,表示全部放在筛子中。

定义数组prime[N],元素prime[i]的值为第i个质数。

下面以求20以内所有质数及所有数的φ值为例,来描述筛法的使用。

1)从i=2开始循环,vis[2]==true,找到第一个质数2,prime[0]=2,质数个数PNum=1;

同时,phi[2]=2-1=1。

采用循环for(j=0;

j<

pNum&

prime[j]*i<

MAXN;

j++)将筛子中2的各质数倍数筛掉,同时求得相应数的欧拉函数值。

(注意这里与通常的筛法有改变,主要为了用质数筛出各数的欧拉函数值)。

筛去2*prime[0]=2*2=4,即vis[4]=false;

phi[4]=phi[2]*prime[0]=1*2=2;

2)i++,进行下次循环,vis[3]==true,找到第2个质数,prime[1]=3,pNum=2,同时phi[3]=3-1=2。

筛去3*prime[0]=3*2=6,即vis[6]=false;

置phi[6]=phi[3]*(prime[0]-1)=2*1=2;

筛去3*prime[1]=3*3=9,即vis[9]=false;

置phi[9]=phi[3]*(prime[1])=2*3=6;

3)i++,进行下次循环,vis[4]==false,4不是质数。

筛去4*prime[0]=4*2=8,即vis[8]=false;

置phi[8]=phi[4]*(prime[0])=2*2=4;

筛去4*prime[1]=4*3=12,即vis[12]=false;

置phi[12]=phi[4]*(prime[1]-1)=2*2=4;

与 

φ(12)=12*(1-1/2)(1-1/3)=4 

比较下更容易理解。

phi[12]=phi[4]*(prime[1]-1)=phi[2]*prime[0]*(prime[1]-1)=2*(1-1/2)*2*3*(1-1/3)。

4)i++,进行下次循环,vis[5]==true,找到第3个质数,prime[2]=5,pNum=3,同时phi[5]=5-1=4。

筛去5*prime[0]=5*2=10,即vis[10]=false;

置phi[10]=phi[5]*(prime[0]-1)=4*1=4;

筛去5*prime[1]=5*3=15,即vis[15]=false;

置phi[15]=phi[5]*(prime[1]-1)=4*2=8;

筛去5*prime[2]=5*5=25,即vis[25]=false;

当然我们以20为例的话,25已超出,循环不会执行到。

同理,6不是质数,筛去6*2=12,置phi[12]=phi[6]*(prime[0])=2*2=4。

筛去6*3=18,置phi[18]=phi[6]*(prime[1])=2*3=6。

7是质数,置prime[3]=7,pNum=4,phi[7]=6。

筛去7*2=14,置phi[14]=phi[7]*(prime[0]-1)=6*1=6。

…… 

(3)采用筛法思想的源程序。

string.h>

#defineMAXN1000005

intprime[MAXN],pNum,phi[MAXN];

__int64num[MAXN]={0};

boolvis[MAXN];

intn,i,j;

memset(vis,true,sizeof(vis));

//下面程序段既求MAXN以内的素数又求欧拉数。

pNum=0;

phi[1]=1;

for(i=2;

i<

MAXN;

if(vis[i]) 

// 

i是素数

prime[pNum++]=i;

phi[i]=i-1;

for(j=0;

j++)

vis[prime[j]*i]=false;

if(i%prime[j]==0)

phi[i*prime[j]]=phi[i]*prime[j];

else 

phi[i*prime[j]]=phi[i]*(prime[j]-1);

num[i]=num[i-1]+phi[i];

%I64d\n"

num[n]);

将用筛法思想改写的源程序提交给POJ2478,可以Accepted。

将上面的程序略作改动,可以顺便通过 

HDU2824“TheEulerfunction”。

(4)进一步讨论。

上面采用筛法求欧拉函数的值时,用了3个数组,用于表示数是否在筛子中的标记数组vis,用于保存质数的数组prime,用于保存欧拉函数值的数组phi。

虽然看起来直观,好像体现了欧拉函数值与分解质因数相关的概念,但有点繁琐。

能否只用一个数组phi,即保存欧拉函数的值,又表示筛子,当然在筛子中的最小数一定是质数,从而又表示了质数呢?

定义数组phi[N],初始值全为0,Phi[i]=0表示i在筛子中,i是质数。

前面介绍过欧拉函数值得标准求法。

设正整数N分解质因数后,N=P1^q1*P2^q2*...*Pn^qn.

则 

显然,若p1是n的质因数,phi[n]一定会作n*(p1-1)/p1这样的运算。

下面我以n=30为例介绍phi数组既保存欧拉函数值有当筛子用的方法。

由于筛法从i=2开始进行,所有phi[i]元素值初始全为0。

1)phi[2]=0,2在筛子中,2是质数,开始执行筛法过程,对指定范围内所有2的倍数进行处理。

phi[2]=0 

phi[2]=2 

(置初始值表示筛

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 小学教育 > 小升初

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1