完美數簡介及演算法分析
阿新 • • 發佈:2019-01-28
完美數簡介
各個小於它的約數(真約數,列出某數的約數,去掉該數本身,剩下的就是它的真約數)的和等於它本身的自然數叫做完全數(Perfect number),又稱完美數或完備數。例如:第一個完全數是6,它有約數1、2、3、6,除去它本身6外,其餘3個數相加,1+2+3=6。第二個完全數是28,它有約數1、2、4、7、14、28,除去它本身28外,其餘5個數相加,1+2+4+7+14=28。第三個完全數是496,有約數1、2、4、8、16、31、62、124、248、496,除去其本身496外,其餘9個數相加,1+2+4+8+16+31+62+124+248=496。後面的完全數還有8128、33550336等等。
解法
如何求小於10000的所有完美數?並將程式寫的有效率?基本上有三個步驟:
求出一定數目的質數表
利用質數表求指定數的因式分解
利用因式分解求所有真因數和,並檢查是否為完美數
步驟一 與 步驟二在之前討論過了,問題在步驟三,如何求真因數和?方法很簡單,要先知道將所有真因數和加上該數本身,會等於該數的兩倍,例如:
2 * 28 = 1 + 2 + 4 + 7 + 14 + 28
等式後面可以化為:
2 * 28 = (20 + 21 + 22) * (70 + 71)
所以只要求出因式分解,就可以利用迴圈求得等式後面的值,將該值除以2就是真因數和了;等式後面第一眼看時可能想到使用等比級數公式來解,不過會使用到次方運算,可以在迴圈走訪因式分解陣列時,同時計算出等式後面的值,這在下面的實作中可以看到。
C/OC程式碼實現
#define N 1000
#define P 10000
int prime(int*); // 求質數表
int factor(int*, int, int*); // 求factor(因式分解)
int fsum(int*, int); // sum ot proper factor(因式求和,判斷是不是完全數)
//主程式(C/OC) int ptable[N+1] = {0}; // 儲存質數表 int fact[N+1] = {0}; // 儲存因式分解結果 int count1, count2, i; count1 = prime(ptable); for(i = 0; i <= P; i++) {//i為被判斷的數 count2 = factor(ptable, i, fact);//將i因式分解,通過質數表ptable,結果放到fact中 if(i == fsum(fact, count2)) printf("Perfect Number: %d\n", i); } printf("\n");
//求質數表,放到pNum中
int prime(int* pNum) {
int i, j;
int prime[N+1];
for(i = 2; i <= N; i++)
prime[i] = 1;
for(i = 2; i*i <= N; i++) {
if(prime[i] == 1) {
for(j = 2*i; j <= N; j++) {
if(j % i == 0)
prime[j] = 0;
}
}
}
for(i = 2, j = 0; i < N; i++) {
if(prime[i] == 1)
pNum[j++] = i;
}
return j;
}
//將i因式分解,通過質數表ptable,結果(都是質數)放到fact中
int factor(int* table, int num, int* frecord) {
int i, k;
for(i = 0, k = 0; table[i] * table[i] <= num;) {
if(num % table[i] == 0) {
frecord[k] = table[i];
k++;
num /= table[i];
}
else
i++;
}
frecord[k] = num;
return k+1;
}
//通過傳入質數表和質數表長度,判斷質數表所對應的數是不是完美數
int fsum(int* farr, int c) {
int i, r, s, q;
i = 0;
r = 1;
s = 1;
q = 1;
while(i < c) {
do {
r *= farr[i];
q += r;//q為真因子相加結果
i++;
} while(i < c-1 && farr[i-1] == farr[i]);//如果因子中有兩個相同質數
s *= q;//某個因子(包括相同的)計算結束,儲存在s中,然後while迴圈判斷下一個質數
r = 1;
q = 1;
}
return s / 2;
}