1. 程式人生 > >C/C++語言經典、實用、趣味程式設計程式設計百例精解

C/C++語言經典、實用、趣味程式設計程式設計百例精解

1.繪製餘弦曲線

在螢幕上用“*”顯示0~360度的餘弦函式cos(x)曲線

*問題分析與演算法設計
如果在程式中使用陣列,這個問題十分簡單。但若規定不能使用陣列,問題就變得不容易了。
關鍵在於餘弦曲線在0~360度的區間內,一行中要顯示兩個點,而對一般的顯示器來說,只能按行輸出,即:輸出第一行資訊後,只能向下一行輸出,不能再返回到上一行。為了獲得本文要求的圖形就必須在一行中一次輸出兩個“*”。
為了同時得到餘弦函式cos(x)圖形在一行上的兩個點,考慮利用cos(x)的左右對稱性。將螢幕的行方向定義為x,列方向定義為y,則0~180度的圖形與180~360度的圖形是左右對稱的,若定義圖形的總寬度為62列,計算出x行0~180度時y點的座標m,那麼在同一行與之對稱的180~360度的y點的座標就 應為62-m。程式中利用反餘弦函式acos計算座標(x,y)的對應關係。
使用這種方法編出的程式短小精煉,體現了一定的技巧。

*程式說明與註釋
#include<stdio.h>
#include<math.h>
int main()
{
double y;
int x,m;
for(y=1;y>=-1;y-=0.1) /*y為列方向,值從1到-1,步長為0.1*/
{
m=acos(y)*10; /*計算出y對應的弧度m,乘以10為圖形放大倍數*/
for(x=1;x<m;x++) printf(" ");
printf("*"); /*控制列印左側的 * 號*/
for(;x<62-m;x++)printf(" ");
printf("*\n"); /*控制列印同一行中對稱的右側*號*/
}

return 0;
}

*思考題
如何實現用“*”顯示0~360度的sin(x)曲線。

在螢幕上顯示0~360度的cos(x)曲線與直線f(x)=45*(y-1)+31的迭加圖形。其中cos(x)圖形用“*”表示,f(x)用“+”表示,在兩個圖形相交的點上則用f(x)圖形的符號。

2.繪製餘弦曲線和直線

*問題分析與演算法設計
本題可以在上題的基礎上進行修改。圖形迭加的關鍵是要在分別計算出同一行中兩個圖形的列方向點座標後,正確判斷相互的位置關係。為此,可以先判斷圖形的交點,再分別控制列印兩個不同的圖形。

*程式註釋與說明
#include<stdio.h>
#include<math.h>
int main()
{
double y;
int x,m,n,yy;
for(yy=0;yy<=20;yy++) /*對於第一個y座標進行計算並在一行中列印圖形*/
{
y=0.1*yy; /*y:螢幕行方向座標*/
m=acos(1-y)*10; /*m: cos(x)曲線上y點對應的螢幕列座標*/
n=45*(y-1)+31; /*n: 直線上y點對應的列座標*/
for(x=0;x<=62;x++) /*x: 螢幕列方向座標*/
if(x==m&&x==n) printf("+"); /*直線與cos(x)相交時列印“+”*/
else if(x==n) printf("+"); /*列印不相交時的直線圖形*/
else if(x==m||x==62-m) printf("*"); /*列印不相交時的cos(x)圖形*/
else printf(" "); /*其它情況列印空格*/
printf("\n");
}

return 0;
}

*思考題
如何實現sin(x)曲線與cos(x)曲線圖形的同時顯示。

3.繪製圓

在螢幕上用“*”畫一個空心的圓

*問題分析與演算法設計
列印圓可利用圖形的左右對稱性。根據圓的方程:
R*R=X*X+Y*Y
可以算出圓上每一點行和列的對應關係。

*程式說明與註釋
#include<stdio.h>
#include<math.h>
int main()
{
double y;
int x,m;
for(y=10;y>=-10;y–)
{
m=2.5*sqrt(100-y*y); /*計算行y對應的列座標m,2.5是螢幕縱橫比調節係數因為螢幕的
行距大於列距,不進行調節顯示出來的將是橢圓*/
for(x=1;x<30-m;x++) printf(" "); /*圖形左側空白控制*/
printf("*"); /*圓的左側*/
for(;x<30+m;x++) printf(" "); /*圖形的空心部分控制*/
printf("*\n"); /*圓的右側*/
}
return 0;
}

*思考題
實現函式y=x2的圖形與圓的圖形疊加顯示

在歌星大獎賽中,有10個評委為參賽的選手打分,分數為1~100分。選手最後得分為:去掉一個最高分和一個最低分後其餘8個分數的平均值。請編寫一個程式實現。

*問題分析與演算法設計
這個問題的演算法十分簡單,但是要注意在程式中判斷最大、最小值的變數是如何賦值的。
*程式說明與註釋
#include<stdio.h>
int main()
{
int integer,i,max,min,sum;
max=-32768; /*先假設當前的最大值max為C語言整型數的最小值*/
min=32767; /*先假設當前的最小值min為C語言整型數的最大值*/
sum=0; /*將求累加和變數的初值置為0*/
for(i=1;i<=10;i++)
{
printf("Input number %d=",i);
scanf("%d",&integer); /*輸入評委的評分*/
sum+=integer; /*計算總分*/
if(integer>max)max=integer; /*通過比較篩選出其中的最高分*/
if(integer<min)min=integer; /*通過比較篩選出其中的最低分*/
}
printf("Canceled max score:%d\nCanceled min score:%d\n",max,min);
printf("Average score:%d\n",(sum-max-min)/8); /*輸出結果*/
}

*執行結果
Input number1=90
Input number2=91
Input number3=93
Input number4=94
Input number5=90
Input number6=99
Input number7=97
Input number8=92
Input number9=91
Input number10=95
Canceled max score:99
Canceled min score:90
Average score:92

*思考題
題目條件不變,但考慮同時對評委評分進行裁判,即在10個評委中找出最公平(即評分最接返平均分)和最不公平(即與平均分的差距最大)的評委,程式應該怎樣實現?

問555555的約數中最大的三位數是多少?

*問題分析與演算法設計
根據約數的定義,對於一個整數N,除去1和它自身外,凡能整除N的數即為N的約數。因此,最簡單的方法是用2到N-1之間的所有數去除N,即可求出N的全部約數。本題只要求取約數中最大的三位數,則其取值範圍可限制在100到999之間。
*程式說明與註釋
#include<stdio.h>
int main()
{
long i;
int j;
printf("Please input number:");
scanf("%ld",&i);
for(j=999;j>=100;j–)
if(i%j==0)
{
printf("The max factor with 3 digits in %ld is:%d,\n",i,j);
break;
}
}

*執行結果
輸入:555555
輸出:The max factor with 3 digits in555555 is:777

求13的13次方的最後三位數

*問題分析與演算法設計
解本題最直接的方法是:將13累乘13次方擷取最後三位即可。
但是由於計算機所能表示的整數範圍有限,用這種“正確”的演算法不可能得到正確的結果。事實上,題目僅要求最後三位的值,完全沒有必要求13的13次方的完整結果。
研究乘法的規律發現:乘積的最後三位的值只與乘數和被乘數的後三位有關,與乘數和被乘數的高位無關。利用這一規律,可以大大簡化程式。
*程式說明與註釋
#include<stdio.h>
int main()
{
int i,x,y,last=1; /*變數last儲存求X的Y次方過程中的部分乘積的後三位*/
printf("Input X and Y(X**Y):");
scanf("%d**%d",&x,&y);
for(i=1;i<=y;i++) /*X自乘Y次*/
last=last*x%1000; /*將last乘X後對1000取模,即求積的後三位*/
printf("The last 3 digits of %d**%d is:%d\n",x,y,last%1000); /*列印結果*/
}

*執行結果
Input X and Y(X**Y):13**13
The last 3 digits of 13**13 is:253
Input X and Y(X**Y):13**20
The last 3 digits of 13**20 is:801

100!的尾數有多少個零?

*問題分析與演算法設計
  可以設想:先求出100!的值,然後數一下末尾有多少個零。事實上,與上題一樣,由於計算機所能表示的整數範圍有限,這是不可能的。
   為了解決這個問題,必須首先從數學上分析在100!結果值的末尾產生零的條件。不難看出:一個整數若含有一個因子5,則必然會在求100!時產生一個零。因此問題轉化為求1到100這100個整數中包含了多少個因子5。若整數N能被25整除,則N包含2個因子5;若整數N能被5整除,則N包含1個因子5。
*程式說明與註釋
#include<stdio.h>
int main()
{
int a,count =0;
for(a=5;a<=100;a+=5) //迴圈從5開始,以5的倍數為步長,考察整數
{
++count; //若為5的倍數,計數器加1
if(!(a%25)) ++count; //若為25的倍數,計數器再加1
}
printf("The number of 0 inthe end of 100! is: %d.\n",count); //列印結果
return 0;
}

*執行結果
The number of 0 in the endof 100! is: 24.

*問題進一步討論

本題的求解程式是正確的,但是存在明顯的缺點。程式中判斷整數N包含多少個因子5的方法是與程式中的100有關的,若題目中的100改為1000,則就要修改程式中求因子5的數目的演算法了。

*思考題

修改程式中求因子5的數目的演算法,使程式可以求出任意N!的末尾有多少個零。

小明有五本新書,要借給A,B,C三位小朋友,若每人每次只能借一本,則可以有多少種不同的借法?

*問題分析與演算法設計
本問題實際上是一個排列問題,即求從5箇中取3個進行排列的方法的總數。首先對五本書從1至5進行編號,然後使用窮舉的方法。假設三個人分別借這五本書中的一本,當三個人所借的書的編號都不相同時,就是滿足題意的一種借閱方法。
*程式說明與註釋
int main()
{
int a,b,c,count=0;
printf("There are diffrent methods for XM to distribute books to 3readers:\n");
for(a=1;a<=5;a++) /*窮舉第一個人借5本書中的1本的全部情況*/
for(b=1;b<=5;b++) /*窮舉第二個人借5本書中的一本的全部情況*/
for(c=1;a!=b&&c<=5;c++) /*當前兩個人借不同的書時,窮舉第三個人借5本書
中的1本的全部情況*/
if(c!=a&&c!=b) /*判斷第三人與前兩個人借的書是否不同*/
printf(count%8?"%2d:%d,%d,%d ":"%2d:%d,%d,%d\n ",++count,a,b,c);
/*列印可能的借閱方法*/
}

*執行結果
There are diffrent methods for XM to distribute books to 3 readers:
1: 1,2,3 2: 1,2,4 3: 1,2,5 4: 1,3,2 5: 1,3,4
6: 1,3,5 7: 1,4,2 8: 1,4,3 9: 1,4,5 10:1,5,2
11:1,5,3 12:1,5,4 13:2,1,3 14:2,1,4 15:2,1,5
16:2,3,1 17:2,3,4 18:2,3,5 19:2,4,1 20:2,4,3
21:2,4,5 22:2,5,1 23:2,5,3 24:2,5,4 25:3,1,2
26:3,1,4 27:3,1,5 28:3,2,1 29:3,2,4 30:3,2,5
31:3,4,1 32:3,4,2 33:3,4,5 34:3,5,1 35:3,5,2
36:3,5,4 37:4,1,2 38:4,1,3 39:4,1,5 40:4,2,1
41:4,2,3 42:4,2,5 43:4,3,1 44:4,3,2 45:4,3,5
46:4,5,1 47:4,5,2 48:4,5,3 49:5,1,2 50:5,1,3
51:5,1,4 52:5,2,1 53:5,2,3 54:5,2,4 55:5,3,1
56:5,3,2 57:5,3,4 58:5,4,1 59:5,4,2 60:5,4,3

在螢幕上顯示楊輝三角形

           1
          1 1
        1 2 1
       1 3 3 1
      1 4 6 4 1
   1 5 10 10 5 1
………………………………..

*問題分析與演算法設計
楊輝三角形中的數,正是(x+y)的N次方冪展開式各項的係數。本題作為程式設計中具有代表性的題目,求解的方法很多,這裡僅給出一種。
從楊輝三角形的特點出發,可以總結出:
1)第N行有N+1個值(設起始行為第0行)
2)對於第N行的第J個值:(N>=2)
當J=1或J=N+1時:其值為1
J!=1且J!=N+1時:其值為第N-1行的第J-1個值與第N-1行第J個值
之和
將這些特點提煉成數學公式可表示為:
1 x=1或x=N+1
c(x,y)=
c(x-1,y-1)+c(x-1,y) 其它

本程式應是根據以上遞迴的數學表示式編制的。
*程式說明與註釋
#include<stdio.h>
int main()
{
int i,j,n=13;
printf("N=");
while(n>12)
scanf("%d",&n); /*控制輸入正確的值以保證螢幕顯示的圖形正確*/
for(i=0;i<=n;i++) /*控制輸出N行*/
{
for(j-0;j<24-2*i;j++) printf(" "); /*控制輸出第i行前面的空格*/
for(j=1;j<i+2;j++) printf("%4d",c(i,j)); /*輸出第i行的第j個值*/
printf("\n");
}
}

void int c(int x,int y) /*求楊輝三角形中第x行第y列的值*/
{
int z;
if((y==1)||(y==x+1)) return 1; /*若為x行的第1或第x+1列,則輸出1*/
z=c(x-1,y-1)+c(x-1,y); /*否則,其值為前一行中第y-1列與第y列值之和*/
return z;
}

*思考題
自行設計一種實現楊輝三角形的方法

將任一整數轉換為二進位制形式

*問題分析與演算法設計
將十進位制整數轉換為二進位制的方法很多,這裡介紹的實現方法利用了C語言能夠對位進行操作的特點。對於C語言來說,一個整數在計算機內就是以二進位制的形式儲存的,所以沒有必要再將一個整數經過一系列的運算轉換為二進位制形式,只要將整數在記憶體中的二進位制表示輸出即可。
*程式說明與註釋
#include<stdio.h>
void printb(int,int);
int main()
{
int x;printf("Input number:");
scanf("%d",&x);
printf("number of decimal form:%d\n",x);
printf(" it's binary form:");
printb(x,sizeof(int)*8); /*x:整數 sizeof(int):int型在記憶體中所佔的位元組數
sizeof(int)*8:int型對應的位數*/
putchar('\n');
}

void printb(int x,int n)
{
if(n>0)
{
putchar('0'+((unsigned)(x&(1<<(n-1)))>>(n-1))); /*輸出第n位*/
printb(x,n-1); /*歸呼叫,輸出x的後n-1位*/
}
}

*執行結果
輸入:8
輸出:
number of decimal form:8
it's bunary form:0000000000001000
輸入:-8
輸出:number of decimal form:-8
it's binary form:1111111111111000
輸入:32767
輸出:number of decimal form:32767
it's binary form:0111111111111111
輸入:-32768
輸出:number of decimal form:-32768
it's binary form:1000000000000000
輸入:128
輸出:number of decimal form:128
it's binary form:0000000010000000

*問題的進一步討論
充分利用C語言可以對位進行操作的特點,可以編寫許多其它高階語言不便於編寫甚至根本無法編寫的程式。位操作是C語言的一大特點,在深入學習C語言的過程中應力求很好掌握。
程式中使用的位運算方法不是最佳的,也可以不用遞迴操作,大家可以自行對程式進行優化。

*思考題
將任意正整數轉換為四進位制或八進位制數

11.打魚還是晒網 

中國有句俗語叫“三天打魚兩天晒網”。某人從1990年1月1日起開始“三天打魚兩天晒網”,問這個人在以後的某一天中是“打魚”還是“晒網”。

*問題分析與演算法設計
根據題意可以將解題過程分為三步:
1)計算從1990年1月1日開始至指定日期共有多少天;
2)由於“打魚”和“晒網”的週期為5天,所以將計算出的天數用5去除;
3)根據餘數判斷他是在“打魚”還是在“晒網”;
若 餘數為1,2,3,則他是在“打魚”
否則 是在“晒網”
在這三步中,關鍵是第一步。求從1990年1月1日至指定日期有多少天,要判斷經歷年份中是否有閏年,二月為29天,平年為28天。閏年的方法可以用偽語句描述如下:
如果 ((年能被4除盡 且 不能被100除盡)或 能被400除盡)
則 該年是閏年;
否則 不是閏年。
C語言中判斷能否整除可以使用求餘運算(即求模)

*程式說明與註釋
#include<stdio.h>
int days(struct date day);
struct date{
int year;
int month;
int day;
};

int main()
{
struct date today,term;
int yearday,year,day;
printf("Enter year/month/day:");
scanf("%d%d%d",&today.year,&today.month,&today.day); /*輸入日期*/
term.month=12; /*設定變數的初始值:月*/
term.day=31; /*設定變數的初始值:日*/
for(yearday=0,year=1990;year<today.year;year++)
{
term.year=year;
yearday+=days(term); /*計算從1990年至指定年的前一年共有多少天*/
}
yearday+=days(today); /*加上指定年中到指定日期的天數*/
day=yearday%5; /*求餘數*/
if(day>0&&day<4) printf("he was fishing at thatday.\n"); /*列印結果*/
else printf("He was sleeping at that day.\n");
}

int days(struct date day)
{
static int day_tab[2][13]=
{{0,31,28,31,30,31,30,31,31,30,31,30,31,}, /*平均每月的天數*/
{0,31,29,31,30,31,30,31,31,30,31,30,31,},
};
int i,lp;
lp=day.year%4==0&&day.year%100!=0||day.year%400==0;
/*判定year為閏年還是平年,lp=0為平年,非0為閏年*/
for(i=1;i<day.month;i++) /*計算本年中自1月1日起的天數*/
day.day+=day_tab[lp][i];
return day.day;
}

*執行結果
Enter year/month/day:1991 10 25
He was fishing at day.
Enter year/month/day:1992 10 25
He was sleeping at day.
Enter year/month/day:1993 10 25
He was sleeping at day.

*思考題
請打印出任意年份的日曆

一輛卡車違反交通規則,撞人後逃跑。現場有三人目擊事件,但都沒有記住車號,只記下車號的一些特徵。甲說:牌照的前兩位數字是相同的;乙說:牌照的後兩位數字是相同的,但與前兩位不同; 丙是數學家,他說:四位的車號剛好是一個整數的平方。請根據以上線索求出車號。

*問題分析與演算法設計
按照題目的要求造出一個前兩位數相同、後兩位數相同且相互間又不同的整數,然後判斷該整數是否是另一個整數的平方。

*程式說明與註釋
#include<stdio.h>
#include<math.h>
int main()
{
int i,j,k,c;
for(i=1;i<=9;i++) /*i:車號前二位的取值*/
for(j=0;j<=9;j++) /*j:車號後二位的取值*/
if(i!=j) /*判斷二位數字是否相異*/
{
k=i*1000+i*100+j*10+j; /*計算出可能的整數*/
for(c=31;c*c<k;c++); /*判斷該數是否為另一整數的平方*/
if(c*c==k) printf("Lorry–No. is %d.\n",k); /*若是,列印結果*/

}
}

*執行結果
Lorry _No.is 7744

假設銀行一年整存零取的月息為0.63%。現在某人手中有一筆錢,他打算在今後的五年中的年底取出1000元,到第五年時剛好取完,請算出他存錢時應存入多少。

*問題分析與演算法設計
分析存錢和取錢的過程,可以採用倒推的方法。若第五年年底連本帶息要取1000元,則要先求出第五年年初銀行存款的錢數:
第五年初存款=1000/(1+12*0.0063)
依次類推可以求出第四年、第三年……的年初銀行存款的錢數:
第四年年初存款=(第五年年初存款+1000)/(1+12*0.0063)
第三年年初存款=(第四年年初存款+1000)/(1+12*0.0063)
第二年年初存款=(第三年年初存款+1000)/(1+12*0.0063)
第一年年初存款=(第二年年初存款+1000)/(1+12*0.0063)
通過以上過程就可以很容易地求出第一年年初要存入多少錢。

*程式說明與註釋
#include<stdio.h>
int main()
{
int i;
float total=0;
for(i=0;i<5;i++) /*i 為年數,取值為0~4年*/
total=(total+1000)/(1+0.0063*12); /*累計算出年初存款數額,第五次的計算
結果即為題解*/
printf("He must save %.2fat first.\n",total);
}

*執行結果
He must save 4039.44 at first

假設銀行整存整取存款不同期限的月息利率分別為:
0.63% 期限=1年
0.66% 期限=2年
0.69% 期限=3年
0.75% 期限=5年
0.84% 期限=8年
利息=本金*月息利率*12*存款年限。
現在某人手中有2000元錢,請通過計算選擇一種存錢方案,使得錢存入銀行20年後得到的利息最多(假定銀行對超過存款期限的那一部分時間不付利息)。

*問題分析與演算法設計
為了得到最多的利息,存入銀行的錢應在到期時馬上取出來,然後立刻將原來的本金和利息加起來再作為新的本金存入銀行,這樣不斷地滾動直到滿20年為止,由於存款的利率不同,所以不同的存款方法(年限)存20年得到的利息是不一樣的。
分析題意,設2000元存20年,其中1年存i1次,2年存i2次,3年存i3次,5年存i5次,8年存i8次,則到期時存款人應得到的本利合計為:
2000*(1+rate1)i1*(1+rate2)i2*(1+rate3)i3*(1+rate5)i5*(1+rate8)i8
其中rateN為對應存款年限的利率。根據題意還可得到以下限制條件:
0<=i8<=2
0<=i5<=(20-8*i8)/5
0<=i3<=(20-8*i8-5*i5)/3
0<=i2<=(20-8*i8-5*i5-3*i3)/2
0<=i1=20-8*i8-5*i5-3*i3-2*i2
可以用窮舉法窮舉所有的i8、i5、i3、i2和i1的組合,代入求本利的公式計算出最大值,就是最佳存款方案。

*程式說明與註釋
#include<stdio.h>
#include<math.h>
int main()
{
int i8,i5,i3,i2,i1,n8,n5,n3,n2,n1;
float max=0,term;
for(i8=0;i8<3;i8++) /*窮舉所有可能的存款方式*/
for(i5=0;i5<=(20-8*i8)/5;i5++)
for(i3=0;i3<=(20-8*i8-5*i5)/3;i3++)
for(i2=0;i2<=(20-8*i8-5*i5-3*i3)/2;i2++)
{
i1=20-8*i8-5*i5-3*i3-2*i2;
term=2000.0*pow((double)(1+0.0063*12),(double)i1)
*pow((double)(1+2*0.0063*12),(double)i2)
*pow((double)(1+3*0.0069*12),(double)i3)
*pow((double)(1+5*0.0075*12),(double)i5)
*pow((double)(1+8*0.0084*12),(double)i8);
/*計算到期時的本利合計*/
if(term>max)
{
max=term;n1=i1;n2=i2;n3=i3;n5=i5;n8=i8;
}
}
printf("For maxinum profit,he should so save his money in abank:\n");
printf(" made fixed deposit for 8 year: %d times\n",n8);
printf(" made fixed deposit for 5 year: %d times\n",n5);
printf(" made fixed deposit for 3 year: %d times\n",n3);
printf(" made fixed deposit for 2 year: %d times\n",n2);
printf(" made fixed deposit for 1 year: %d times\n",n1);
printf(" Toal: %.2f\n",max);
/*輸出存款方式*/
}

*執行結果
For maxinum profit,he should so save his money in a bank:
made fixed deposit for 8 year: 0times
made fixed deposit for 5 year: 4times
made fixed deposit for 3 year: 0times
made fixed deposit for 2 year: 0times
made fixed deposit for 1 year: 0times
Total:8841.01
可見最佳的存款方案為連續四次存5年期。
*思考題
某單位對職工出售住房,每套為2萬元。買房付款的方法是:
一次交清,優惠20%
從第一年開始,每年年初分期付款:
5年交清,優惠50%;
10年交清,優惠10%;
20年交清,沒有優惠。
現在有人手中正好有2萬元,若假定在今後20年中物價和銀行利率均保持不變,問他應當選擇哪種付款方式可以使應付的錢最少?

A、B、C、D、E五個人在某天夜裡合夥去捕魚,到第二天凌晨時都疲憊不堪,於是各自找地方睡覺。日上三杆,A第一個醒來,他將魚分為五份,把多餘的一條魚扔掉,拿走自己的一份。B第二個醒來,也將魚分為五份,把多餘的一條魚扔掉,保持走自己的一份。C、D、E依次醒來,也按同樣的方法拿走魚。問他們合夥至少捕了多少條魚?

*問題分析與演算法設計
根據題意,總計將所有的魚進行了五次平均分配,每次分配時的策略是相同的,即扔掉一條魚後剩下的魚正好分成五份,然後拿走自己的一份,餘下其它的四份。
假定魚的總數為X,則X可以按照題目的要求進行五次分配:X-1後可被5整除,餘下的魚為4*(X-1)、5。若X滿足上述要求,則X就是題目的解。

*程式說明與註釋
#include<stdio.h>
int main()
{
int n,i,x,flag=1; /*flag:控制標記*/
for(n=6;flag;n++) /*採用試探的方法。令試探值n逐步加大*/
{
for(x=n,i=1&&flag;i<=5;i++)
if((x-1)%5==0) x=4*(x-1)/5;
else flag=0; /*若不能分配則置標記falg=0退出分配過程*/
if(flag) break; /*若分配過程正常結束則找到結果退出試探的過程*/
else flag=1; /*否則繼續試探下一個數*/
}
printf("Total number of fish catched=%d\n",n); /*輸出結果*/
}

*執行結果
Total number of fish catched = 3121

*問題的進一步討論
程式採用試探法,試探的初值為6,每次試探的步長為1。這是過分保守的做法。可以在進一步分析題目的基礎上修改此值,增大試探的步長值,以減少試探次數。

*思考題
請使用其它的方法求解本題。

買賣提將養的一缸金魚分五次出售系統上一次賣出全部的一半加二分之一條;第二次賣出餘下的三分之一加三分之一條;第三次賣出餘下的四分之一加四分之一條;第四次賣出餘下的五分之一加五分之一條;最後賣出餘下的11條。問原來的魚缸中共有幾條金魚?

*問題分析與演算法設計
題目中所有的魚是分五次出售的,每次賣出的策略相同;第j次賣剩下的(j+1)分之一再加1/(j+1)條。第五次將第四次餘下的11條全賣了。
假定第j次魚的總數為X,則第j次留下:
x-(x+1)/(j+1)
當第四次出售完畢時,應該剩下11條。若X滿足上述要求,則X就是題目的解。
應當注意的是:"(x+1)/(j+1)"應滿足整除條件。試探X的初值可以從23開始,試探的步長為2,因為X的值一定為奇數。

*程式說明與註釋
#include<stdio.h>
int main()
{
int i,j,n=0,x; /*n為標誌變數*/
for(i=23;n==0;i+=2) /*控制試探的步長和過程*/
{
for(j=1,x=i;j<=4&&x>=11;j++) /*完成出售四次的操作*/
if((x+1)%(j+1)==0) /*若滿足整除條件則進行實際的出售操作*/
x-=(x+1)/(j+1);
else {x=0;break;} /*否則停止計算過程*/
if(j==5&&x==11) /*若第四次餘下11條則滿足題意*/
{
printf("There are %d fishes at first.\n",i); /*輸出結果*/
n=1; /*控制退出試探過程*/
}
}
}

*執行結果
There are 59 fishes at first.

*思考題
日本著名數學遊戲專家中村義作教授提出這樣一個問題:父親將2520個桔子分給六個兒子。分完後父親說:“老大將分給你的桔子的1/8給老二;老二拿到後連同原先的桔子分1/7給老三;老三拿到後連同原先的桔子分1/6給老四;老四拿到後連同原先的桔子分1/5給老五;老五拿到後連同原先的桔子分1/4給老六;老六拿到後連同原先的桔子分1/3給老大”。結果大家手中的桔子正好一樣多。問六兄弟原來手中各有多少桔子?

甲、乙、丙三位魚夫出海打魚,他們隨船帶了21只籮筐。當晚返航時,他們發現有七筐裝滿了魚,還有七筐裝了半筐魚,另外七筐則是空的,由於他們沒有秤,只好通過目測認為七個滿筐魚的重量是相等的,7個半筐魚的重量是相等的。在不將魚倒出來的前提下,怎樣將魚和筐平分為三份?

*問題分析與演算法設計
根據題意可以知道:每個人應分得七個籮筐,其中有3.5筐魚。採用一個3*3的陣列a來表示三個人分到的東西。其中每個人對應陣列a的一行,陣列的第0列放分到的魚的整筐數,陣列的第1列放分到的半筐數,陣列的第2列放分到的空筐數。由題目可以推出:
。陣列的每行或每列的元素之和都為7;
。對陣列的行來說,滿筐數加半筐數=3.5;
。每個人所得的滿筐數不能超過3筐;
。每個人都必須至少有1 個半筐,且半筐數一定為奇數
對於找到的某種分魚方案,三個人誰拿哪一份都是相同的,為了避免出現重複的分配方案,可以規定:第二個人的滿筐數等於第一個人的滿筐數;第二個人的半筐數大於等於第一個人的半筐數。

*程式說明與註釋
#include<stdio.h>
int a[3][3],count;
int main()
{
int i,j,k,m,n,flag;
printf("It exists possible distribtion plans:\n");
for(i=0;i<=3;i++) /*試探第一個人滿筐a[0][0]的值,滿筐數不能>3*/
{
a[0][0]=i;
for(j=i;j<=7-i&&j<=3;j++) /*試探第二個人滿筐a[1][0]的值,滿筐數不能>3*/
{
a[1][0]=j;
if((a[2][0]=7-j-a[0][0])>3)continue; /*第三個人滿筐數不能>3*/
if(a[2][0]<a[1][0])break; /*要求後一個人分的滿筐數>=前一個人,以排除重複情況*/
for(k=1;k<=5;k+=2) /*試探半筐a[0][1]的值,半筐數為奇數*/
{
a[0][1]=k;
for(m=1;m<7-k;m+=2) /*試探 半筐a[1][1]的值,半筐數為奇數*/
{
a[1][1]=m;
a[2][1]=7-k-m;
for(flag=1,n=0;flag&&n<3;n++)
/*判斷每個人分到的魚是 3.5筐,flag為滿足題意的標記變數*/
if(a[n][0]+a[n][1]<7&&a[n][0]*2+a[n][1]==7)
a[n][2]=7-a[n][0]-a[n][1]; /*計算應得到的空筐數量*/
else flag=0; /*不符合題意則置標記為0*/
if(flag)
{
printf("No.%d Full basket Semi–basket Empty\n",++count);
for(n=0;n<3;n++)
printf(" fisher %c: %d %d %d\n",
'A'+n,a[n][0],a[n][1],a[n][2]);
}
}
}
}
}
}
* 執行結果
It exists possible distribution plans:
No.1 Full basket Semi–basket Empty
fisher A: 1 5 1
fisher B: 3 1 3
fisher C: 3 1 3
No.2 Full basket Semi–basket Empty
fisher A: 2 3 2
fisher B: 2 3 2
fisher C: 3 1 3

*思考題
晏會上數學家出了一道難題:假定桌子上有三瓶啤酒,癬瓶子中的酒分給幾個人喝,但喝各瓶酒的人數是不一樣的。不過其中有一個人喝了每一瓶中的酒,且加起來剛好是一瓶,請問喝這三瓶酒的各有多少人?
(答案:喝三瓶酒的人數分別是2人、3人和6人)

個位數為6且能被3整除的五位數共有多少?

*題目分析與演算法設計
根據題意可知,滿足條件的五位數的選擇範圍是10006、10016。。。99996。可設基礎數i=1000,通過計算i*10+6即可得到欲選的數(i的變化範圍是1000~999),再判斷該數能否被3整除。

*程式說明與註釋
#include<stdio.h>
int main()
{
long int i;
int count=0; /*count:統計滿足條件的五位數的個數*/
for(i=1000;i<9999;i++)
if(!((i*10+6)%3)) /*判斷所選的數能否被3整除*/
count++; /*若滿足條件則計數*/
printf("count=%d\n",count);
}

*執行結果
count=2999

*思考題
求100到1000之間有多少個其數字之和為5的整數。
(答案:104,113,122,131,140,203,212,221,230,302,311,320,401,410,500)

一個自然數被8除餘1,所得的商被8除也餘1,再將第二次的商被8除後餘7,最後得到一個商為a。又知這個自然數被17除餘4,所得的商被17除餘15,最後得到一個商是a的2倍。求這個自然數。

*問題分析與演算法設計
根據題意,可設最後的商為i(i從0開始取值),用逆推法可以列出關係式:
(((i*8+7)*8)+1)*8+1=((2*i*17)+15)*18+4
再用試探法求出商i的值。

*程式說明與註釋
#include<stdio.h>
int main()
{
int i;
for(i=0;;i++) /*試探商的值*/
if(((i*8+7)*8+1)*8+1==(34*i+15)*17+4)
{ /*逆推判斷所取得的當前i值是否滿足關係式*/
/*若滿足則輸出結果*/
printf("The required number is: %d\n",(34*i+15)*17+4);
break; /*退出迴圈*/
}
}

*執行結果
The required number is:1993

一個自然數的七進製表達式是一個三位數,而這個自然數的九進製表示也是一個三位數,且這兩個三位數的數碼正好相反,求這個三位數。

*問題分析與演算法設計
根據題意可知,七進位制和九進製表示的這全自然數的每一位一定小於7,可設其七進位制數形式為kji(i、j、k的取值分別為1~6),然後設其九進製表示形式為ijk。

*程式說明與註釋
#include<stdio.h>
int main()
{
int i,j,k;
for(i=1;i<7;i++)
for(j=0;j<7;j++)
for(k=1;k<7;k++)
if(i*9*9+j*9+k==i+j*7+k*7*7)
{
printf("The special number with 3 digits is:");
printf("%d%d%d(7)=%d%d%d(9)=%d(10)\n",k,j,i,i,j,k,i*9*9+j*9+k);
}
}

*執行結果
The special number with 3 digits is:503(7)=305(9)=248(10)

位反序數

設N是一個四位數,它的9倍恰好是其反序數,求N。反序數就是將整數的數字倒過來形成的整數。例如:1234的反序數是4321。

*問題分析與演算法設計
可設整數N的千、百、十、個位為i、j、k、l,其取值均為0~9,則滿足關係式:
(i*103+j*102+10*k+l)*9=(l*103+k*102+10*j+i)
的i、j、k、l即構成N。

*程式說明與註釋
#include<stdio.h>
int main()
{
int i;
for(i=1002;i<1111;i++) /*窮舉四位數可能的值*/
if(i%10*1000+i/10%10*100+i/100%10*10+i/1000==i*9)
/*判斷反序數是否是原整數的9倍*/
printf("The number satisfied stats condition is: %d\n",i);
/*若是則輸出*/
}

*執行結果
The number satisfied states condition is:1089

一輛以固定速度行駛的汽車,司機在上午10點看到里程錶上的讀數是一個對稱數(即這個數從左向右讀和從右向左讀是完全一樣的),為95859。兩小時後里程錶上出現了一個新的對稱數。問該車的速度是多少?新的對稱數是多少?

*問題分析與演算法設計
根據題意,設所求對稱數為i,其初值為95589,對其依次遞增取值,將i值的每一位分解後與其對稱位置上的數進行比較,若每個對稱位置上的數皆相等,則可判定i即為所求的對稱數。

*程式說明與註釋
#include<stdio.h>
int main()
{
int t,a[5]; /*陣列a存放分解的數字位*/
long int k,i;
for(i=95860;;i++) /*以95860為初值,迴圈試探*/
{
for(t=0,k=100000;k>=10;t++) /*從高到低分解所取i值的每位數*/
{ /* 字,依次存放於a[0]~a[5]中*/
a[t]=(i%k)/(k/10);
k/=10;
}
if((a[0]==a[4])&&(a[1]==a[3]))
{
printf("The new symmetrical number kelometers is:%d%d%d%d%d\n",
a[0],a[1],a[2],a[3],a[4]);
printf("The velocity of the car is: %.2f\n",(i-95859)/2.0);
break;
}
}
}

*執行結果
The new symmetrical number kelometers is:95959.
The velocity of the car is:50.00

*思考題
將一個數的數碼倒過來所得到的新數叫原數的反序數。如果一個數等於它的反序數,則稱它為對稱數。求不超過1993的最大的二進位制的對稱數。

已知兩個平方三位數abc和xyz,其中a、b、c、x、y、z未必是不同的;而ax、by、cz是三個平方二位數。請程式設計求三位數abc和xyz。

*問題分析與演算法設計
任取兩個平方三位數n和n1,將n從高向低分解為a、b、c,將n1從高到低分解為x、y、z。判斷ax、by、cz是否均為完全平方數。

*程式說明與註釋
#include<stdio.h>
#include<math.h>
void f(int n,float* s);
int main()
{
int i,t;
float a[3],b[3];
print("The possible perfect squares combinations are:\n");
for(i=11;i<=31;++i) //窮舉平方三位數的取值範圍
for(t=11;t<=31;++t)
{
f(i*i,a); //分解平方三位數的各位,每位數字分別存入陣列中
f(t*t,b);
if(sqrt(a[0]*10+b[0]) == (int)sqrt(a[0]*10+b[0])
&& sqrt(a[1]*10+b[1]) == (int)sqrt(a[1]*10+b[1])
&& sqrt(a[2]*10+b[2]) == (int)sqrt(a[2]*10+b[2]) )
{
printf("%d and %d\n,i*i,t*t"); //若三個新的數均是完全平方數,則輸出
}
}
}

/* ———————————————-
分解三位數n的各位數字,將各個數字從高到低依次存入指標s所指向的陣列中
————————————————*/

void f(int n,float* s)
{
int k;
for(k=1000;k>=10;++s)
{
*s = (n%k) /(k/10);
k /=10;
}
}

*執行結果
The possible perfect squares combinations are:
400 and 900
841 and 196

*思考題

求這樣一個三位數,該三位數等於其每位數字的階乘之和。
即 abc = a! + b! + c!
(正確結果:145 = 1! + 4! +5!)

如果一個正整數等於其各個數字的立方和,則稱該數為阿姆斯特朗數(亦稱為自戀性數)。
如 407=43+03+73就是一個阿姆斯特朗數。試程式設計求1000以內的所有阿姆斯特朗數。

*問題分析與演算法設計
可採用窮舉法,依次取1000以內的各數(設為i),將i的各位數字分解後,據阿姆斯特朗數的性質進行計算和判斷。

*程式說明與註釋
#include<stdio.h>
int main()
{
int i,t,k,a[3];
printf("There are follwing Armstrong number smaller than 1000:\n");
for(i=2;i<1000;i++) /*窮舉要判定的數i的取值範圍2~1000*/
{
for(t=0,k=1000;k>=10;t++) /*擷取整數i的各位(從高向低位)*/
{
a[t]=(i%k)/(k/10); /*分別賦於a[0]~a[2}*/
k/=10;
}
if(a[0]*a[0]*a[0]+a[1]*a[1]*a[1]+a[2]*a[2]*a[2]==i)
/*判斷i是否為阿姆斯特朗數*/
printf("%5d",i); /*若滿足條件,則輸出*/

}
printf("\n");
}

*執行結果
There are following Armstrong number smaller than 1000:
153 370 371 407

如果一個數恰好等於它的因子之和,則稱該數為“完全數”。

*問題分析與演算法設計
根據完全數的定義,先計算所選取的整數a(a的取值1~1000)的因子,將各因子累加於m,若m等於a,則可確認a為完全數。

*程式說明與註釋
#include<stdio.h>
int main()
{
int a,i,m;
printf("There are following perfect numbers smaller than 1000:\n");
for(a=1;a<1000;a++) /*迴圈控制選取1~1000中的各數進行判斷*/
{
for(m=0,i=1;i<=a/2;i++) /*計算a的因子,並將各因子之和m=a,則a是完全數輸出*/
if(!(a%i))m+=i;
if(m==a)
printf("%4d ",a);
}
printf("\n");
}

*執行結果
TThere are following perfect numbers smaller than 1000:
6 28 496

如果整數A的全部因子(包括1,不包括A本身)之和等於B;且整數B的全部因子(包括1,不包括B本身)之和等於A,則將整數A和B稱為親密數。求3000以內的全部親密數。

*問題分析與演算法設計
按照親密數定義,要判斷數a是否有親密數,只要計算出a的全部因子的累加和為b,再計算b的全部因子的累加和為n,若n等於a則可判定a和b是親密數。計算數a的各因子的演算法:
用a依次對i(i=1~a/2)進行模運算,若模運算結果等於0,則i為a的一個因子;否則i就不是a的因子。

*程式說明與註釋
#include<stdio.h>
int main()
{
int a,i,b,n;
printf("There are following friendly–numbers pair smaller than3000:\n");
for(a=1;a<3000;a++) /*窮舉1000以內的全部整數*/
{
for(b=0,i=1;i<=a/2;i++) /*計算數a的各因子,各因子之和存放於b*/
if(!(a%i))b+=i; /*計算b的各因子,各因子之和存於n*/
for(n=0,i=1;i<=b/2;i++)
if(!(b%i))n+=i;
if(n==a&&a<b)
printf("%4d..%4d ",a,b); /*若n=a,則a和b是一對親密數,輸出*/
}
}

*執行結果
There are following friendly–numbers pair smaller than 3000:
220.. 284 1184.. 1210 2620.. 2924

自守數是指一個數的平方的尾數等於該數自身的自然數。例如:
252=625 762=5776 93762=87909376
請求出200000以內的自守數

*問題分析與演算法設計
若採用“求出一個數的平方後再擷取最後相應位數”的方法顯然是不可取的,因為計算機無法表示過大的整數。
分析手工方式下整數平方(乘法)的計算過程,以376為例:
376 被乘數
X 376 乘數
———-
2256 第一個部分積=被乘數*乘數的倒數第一位
2632 第二個部分積=被乘數*乘數的倒數第二位
1128 第三個部分積=被乘數*乘數的倒數第三位
———-
141376 積
本問題所關心的是積的最後三位。分析產生積的後三位的過程,可以看出,在每一次的部分積中,並不是它的每一位都會對積的後三位產生影響。總結規律可以得到:在三位數乘法中,對積的後三位產生影響的部分積分別為:
第一個部分積中:被乘數最後三位*乘數的倒數第一位
第二個部分積中:被乘數最後二位*乘數的倒數第二位
第三個部分積中:被乘數最後一位*乘數的倒數第三位
將以上的部分積的後三位求和後擷取後三位就是三位數乘積的後三位。這樣的規律可以推廣到同樣問題的不同位數乘積。
按照手工計算的過程可以設計演算法編寫程式。

*程式說明與註釋
#include<stdio.h>
int main()
{
long mul,number,k,ll,kk;
printf("It exists following automorphic nmbers small than200000:\n");
for(number=0;number<200000;number++)
{
for(mul=number,k=1;(mul/=10)>0;k*=10);
/*由number的位數確定擷取數字進行乘法時的係數k*/
kk=k*10; /*kk為擷取部分積時的係數*/
mul=0; /*積的最後n位*/
ll=10; /*ll為擷取乘數相應位時的係數*/
while(k>0)
{
mul=(mul+(number%(k*10))*(number%ll-number%(ll/10)))%kk;
/*(部分積+擷取被乘數的後N位*擷取乘數的第M位),%kk再擷取部分積*/
k/=10; /*k為擷取被乘數時的係數*/
ll*=10;
}
if(number==mul) /*判斷若為自守數則輸出*/
printf("%ld ",number);
}
}

*執行結果
It exsts following automorphic numbners smaller than 200000:
0 1 5 6 25 76 376 625 9376 90625 109376

列印所有不超過n(取n<256) 的其平方具有對稱性質的數(也稱迴文數)。

*問題分析與演算法設計
對於要判斷的數n,計算出其平方後(存於a),將a的每一位進行分解,再按a的從低到高的順序將其恢復成一個數k(如n=13,則a=169且k=961),若a等於k則可判定n為回亠數。

*程式說明與註釋

原程式好像有錯,而且比較費解,現基於原程式修改如下(如果讀者還發現錯誤請提出):

#include<stdio.h>
int main(void)
{
int m[16],n,i,t,count=0;
long unsigned a,k;
printf("No. number it's square(palindrome)\n");
for(n=1;n<256;n++) /*窮舉n的取值範圍*/
{
k=0;t=1;a=n*n; /*計算n的平方*/

for(i=0;a!=0;i++) /*從低到高分解數a的每一位存於陣列m[0]~m[16]*/
{
m[i]=a%10;//這個是取得a的個位,整個迴圈合起來就可以取得各個位
a/=10;
}

int j=0;
for(i–;j<i;j++,i–)//因為n的平方的各個位都存在陣列中了,下面判斷是不是對稱
if(m[j]!=m[i])break;//只要有一位不是對稱,那就說明不是對稱,就可以退出了

//所有的位都對稱就說明是對稱了,這樣就可以打印出結果了
if(j>=i)printf("%2d%10d%10d\n",++count,n,n*n);

}

return 0;
}

*執行結果
No. number it's square(palindrome)
1 1 1
2 2 4
3 3 9
4 11 121
5 22 484
6 26 676
7 101 10201
8 111 12321
9 121 14641
10 202 40804
11 212 44944

//下面程式是原來的,有錯,而且費解
#include<stdio.h>
int main(void)
{
int m[16],n,i,t,count=0;
long unsigned a,k;
printf("No. number it's square(palindrome)\n");
for(n=1;n<256;n++) /*窮舉n的取值範圍*/
{
k=0;t=1;a=n*n; /*計算n的平方*/

for(i=1;a!=0;i++) /*從低到高分解數a的每一位存於陣列m[1]~m[16]*/
{
m[i]=a%10;//安安注:這個是取得a的個位,整個迴圈合起來就可以取得各個位,並存於陣列中,為了是下面判斷是不是對稱
a/=10;
}

for(;i>1;i–)
{
k+=m[i-1]*t;
t*=10;
}
if(k==n*n)
printf("%2d%10d%10d\n",++count,n,n*n);
}

return 0;
}

*執行結果
No. number it's square(palindrome)
1 1 1
2 2 4
3 3 9
4 11 121
5 22 484
6 26 676
7 101 10201
8 111 12321
9 121 14641

3025這個數具有一種獨特的性質:將它平分為二段,即30和25,使之相加後求平方,即(30+25)2,恰好等於3025本身。請求出具有這樣性質的全部四位數。

*問題分析與演算法設計
具有這種性質的四位數沒有分佈規律,可以採用窮舉法,對所有四位數進行判斷,從而篩選出符合這種性質的四位數。具體演算法實現,可任取一個四位數,將其截為兩部分,前兩位為a,後兩位為b,然後套用公式計算並判斷。

*程式說明與註釋
#include<stdio.h>
int main()
{
int n,a,b;
printf("There are following number with 4 digits satisfiedcondition\n");
for(n=1000;n<10000;n++) /*四位數N的取值範圍1000~9999*/
{
a=n/100; /*擷取N的前兩位數存於a*/
b=n%100; /*擷取N的後兩位存於b*/
if((a+b)*(a+b)==n) /*判斷N是否為符合題目所規定的性質的四位數*/
printf("%d ",n);
}
}

*執行結果
There are following numbers with 4 digits satisfied condition:
2025 3025 9801

求素數表中1~1000之間的所有素數

*問題分析與演算法設計
素數就是僅能衩1和它自身整除的整數。判定一個整數n是否為素數就是要判定整數n能否被除1和它自身之外的任意整數整除,若都不能整除,則n為素數。
程式設計時i可以從2開始,到該整數n的1/2為止,用i依次去除需要判定的整數,只要存在可以整除該數的情況,即可確定要判斷的整數不是素數,否則是素數。

*程式說明與註釋
#include<stdio.h>
int main()
{
int n1,nm,i,j,flag,count=0;
do{
printf("Input START and END=?");
scanf("%d%d",&n1,&nm); /*輸入求素數的範圍*/
}while(!(n1>0&&n1<nm)); /*輸入正確的範圍*/
printf("………..PRIME TABLE(%d–%d)…………\n",n1,nm);
if(n1==1||n1==2) /*處理素數2*/
{
printf("%4d",2);
n1=3;count++;
}
for(i=n1;i<=nm;i++) /*判定指定範圍內的整數是否為素數*/
{
if(!(i%2))continue;
for(flag=1,j=3;flag&&j<i/2;j+=2)
/*判定能否被從3到整數的一半中的某一數所整除*/
if(!(i%j))flag=0; /*若能整除則不是素數*/
if(flag) printf(++count%15?"%4d":"%4d\n",i);
}
}

*思考題
請找出十個最小的連續自然數,它們個個都是合數(非素數)

C/C++語言經典、實用、趣味程式設計程式設計百例精解(4

31.歌德巴赫猜想

驗證:2000以內的正偶數都能夠分解為兩個素數之和(即驗證歌德巴赫猜想對2000以內的正偶數成立)。

*問題分析與演算法設計
為了驗證歌德巴赫猜想對2000以內的正偶數都是成立的,要將整數分解為兩部分,然後判斷出分解出的兩個整數是否均為素數。若是,則滿足題意;否則重新進行分解和判斷。
程式中對判斷是否為素數的演算法進行了改進,對整數判斷“用從2開始到該整數的一半”改為“2開始到該整數的平方根”。原因何在請自行分析。

*程式說明與註釋
#include<stdio.h>
#include<math.h>
int fflag(int n);
int main()
{
int i,n;
for(i=4;i<=2000;i+=2)
{
for(n=2;n<i;n++) /*將偶數i分解為兩個整數*/
if(fflag(n)) /*分別判斷兩個整數是否均為素數*/
if(fflag(i-n))
{
printf("%14d=%d+%d\n",i,n,i-n); /*若均是素數則輸出*/
break;
}
if(n==i) printf("error %d\n",i);
}
}

intfflag(int i) /*判斷是否為素數*/
{
int j;
if(i<=1)return 0;
if(i==2)return 1;
if(!(i%2))return 0; /*if no,return 0*/
for(j=3;j<=(int)(sqrt((double)i)+1);j+=2)
if(!(i%j))return 0;
return 1; /*if yes,return 1*/
}

32.可逆素數

求四位的可逆素數。可逆素數指:一個素數將其各位數字的順序倒過來構成的反序數也是素數。

*問題分析與演算法設計
  本題的重點不是判斷素數的方法,而是求一個整數的反序數。求反序數的方法是從整數的末尾依次擷取最後一位數字,每擷取一次後整數縮小10倍,將擷取的數字作為新的整數的最後一位(新的整數擴大10倍後加上被擷取的數字)。這樣原來的整數的數字從低到高被不斷地擷取,依次作為新的整數從高到低的各位數字。

*程式說明與註釋
#include<stdio.h>
#include<math.h>
int num(int number);
int ok(int number);
int main()
{
int i,count;
printf("There are invertable primes with 4 digits: \n");
for(count=0,i=1001;i<9999;i+=2) //窮舉全部的奇數
{
if(num(i)) //若是可逆素數,則輸出
printf(count%9 ? "%3d:%d" : "%3d:%d\n",++count,i);
}
return 0;
}

intnum(int number)
{
int i,j;
if(!ok(number))return 0; //判斷是否為素數
for(i=number,j=0;i>0;i/=10) //按位將整數倒過來,產生反序數
{
j=j*10 + i%10;
}
if(number<j) //若原數小於反序數
{
if(!ok(i)) //判斷對應的反序數是否為可逆素數
{
return 0;
}
else
{
return 1; //若是可逆數素數,則返回1
}
}
else
{
return 0;
}
getchar();
return 0;
}

intok(int number)
{
int i,j;
if(number%2 ==0) //判斷是否為素數
return 0;

j=sqrt((double)number) +1 ; //取整數的平方根為判斷的上限
for(i=3;i<j;i+=2)
{
if(number %i ==0) //若為素數則返回1,否則返回0
return 0;
}

return1;
}

*思考題

求1000以內的孿生素數。孿生素數是指:若a為素數,且a+2也是素數,則素數a和a+2稱為孿生素數。

33.迴文素數

求不超過1000的迴文素數。

*問題分析與演算法設計
  所謂迴文素數是指,對一個整數n從左向右和從由向左讀其結果值相同且是素數,即稱n為迴文素數。所以本題的重點不是判斷素數的方法,而是求迴文整數。構造迴文數的方法很多,這裡僅介紹一種最簡單的演算法。實現思路是先求出一個整數的迴文數,再判斷是否為素數。
  不超過1000的迴文數包括二位和三位的迴文數,我們採用窮舉法來構造一個整數並求與其對應的反序數,若整數與其反序數相等,則該整數是迴文數。

*程式說明與註釋
#include<stdio.h>

inta(int n)
int main()
{
int i,j,t,k,s;
printf("Following are palindrome primes not greater than 1000:\n");
for(i=0;i<=9;++i) //窮舉第一位
for(j=0;j<=9;++j) //窮舉第二位
for(k=0;k<=9;++k) //窮舉第三位
{
s=i*100 + j*10 + k; //計算組成的整數
t=ik*100 + j*10 + i; //計算對應的反序數
if(i == 0 && j==0) //處理整數的前兩位為0的情況
{
t/100;
}
else if(i ==0) //處理整數的第一位為0的情況
{
t/10;
}
if(s.10 && s==t && a(s)) //若大於10且為迴文素數,則輸出
{
printf("%d\t",s);
}
}
return 0;
}

//判斷引數n是否為素數
int a(int n)
{
int i;
for(i=2;i<(n-1)/2;+=i)
{
if(n%i == 0)
return 0;
}

return1;

}

*執行結果

Followingare palindrome primes not greater than 1000:
11 101 131 151 181 191 313 353
373 383 727 787 797 919 929

*思考題

優化生成迴文數的演算法。

34.要發就發

“1898–要發就發”。請將不超過1993的所有素數從小到大排成第一行,第二行上的每個素數都等於它右肩上的素數之差。程式設計求出:第二行數中是否存在這樣的若干個連續的整數,它們的和恰好是1898?假好存在的話,又有幾種這樣的情況?
第一行:2 3 5 7 11 13 17……19791987 1993
第二行:1 2 2 4 2 4…… 8 6

*問題分析與演算法設計
首先從數學上分析該問題:
假設第一行中的素數為n[1]、n[2]、n[3]….n[i]、…第二行中的差值為m[1]、m[2]、m[3]…m[j]…。其中m[j]為:
m[j]=n[j+1]-n[j]。
則第二行連續N個數的和為:
SUM=m[1]+m[2]+m[3]+…+m[j]
=(n[2]-n[1])+(n[3]-n[2])+(n[4]-n[3])+…+(n[j+1]-n[j])
=n[j+1]-n[1]
由此題目就變成了:在不超過1993的所有素數中是否存在這樣兩個素數,它們的差恰好是1898。若存在,則第二行中必有所需整數序列,其和恰為1898,。
對等價問題的求解是比較簡單的。
由分析可知,在素數序列中不必包含2,因為任意素數與2的差一定為奇數,所以不必考慮。

*程式與程式註釋:
#include<stdio.h>
#include<math.h>
#define NUM 320
int number[NUM]; /*存放不超過1993的全部奇數*/
int fflag(int i);
int main()
{
int i,j,count=0;
printf("there are follwing primes sequences in first row:\n");
for(j=0,i=3;i<=1993;i+=2) /*求出不超過1993的全部奇數*/
if(fflag(i)) number[j++]=i;
for(j–;number[j]>1898;j–) /*從最大的素數開始向1898搜尋*/
{
for(i=0;number[j]-number[i]>1898;i++); /*迴圈查詢滿足條件的素數*/
if(number[j]-num