Ⅰ 自冪數的定義
自冪數是指一個 n 位數,它的每個位上的數字的 n 次冪之和等於它本身。(例如:當n爲3時,有1^3 + 5^3 + 3^3 = 153,153即是n爲3時的一個自冪數)
自冪數包括:獨身數、水仙花數、四葉玫瑰數、五角星數、六合數、北斗七星數、八仙數、九九重陽數、十全十美數
n爲1時,自冪數稱爲獨身數。顯然,0,1,2,3,4,5,6,7,8,9都是自冪數。
n爲2時,沒有自冪數。
n爲3時,自冪數稱爲水仙花數,有4個:153,370,371,407;
n爲4時,自冪數稱爲四葉玫瑰數,共有3個:1634,8208,9474;
n爲5時,自冪數稱爲五角星數,共有3個:54748,92727,93084;
n爲6時,自冪數稱爲六合數, 只有1個:548834;
n爲7時,自冪數稱爲北斗七星數, 共有4個:1741725,4210818,9800817,9926315;
n爲8時,自冪數稱爲八仙數, 共有3個:24678050,24678051,88593477;
n爲9時,自冪數稱爲九九重陽數,共有4個:146511208,472335975,534494836,912985153;
n爲10時,自冪數稱爲十全十美數,只有1個:4679307774。
Ⅱ 需求分析
該程序的目的是輸出一定範圍內的所有自冪數,要判斷一個數是不是自冪數,我們需要得到它的位數,然後根據每位的位數次方之和來判斷該數是不是自冪數。
得到位數以及次方和,便是這個程序的主要步驟,因此,優化該程序也應從這兩部的效率最高的方法考慮。
在我的哥德巴赫猜想驗證的博客中,我給出了一個代碼從最初級到極限的算法優化全部步驟,感興趣的同學可以移步去看那篇文章,在此我不再贅述,直接考慮自冪數算法的極限情況。
【C語言】->哥德巴赫猜想驗證->篩選法->算法極限優化之你不可能比我快
Ⅲ 算法優化
a. 得到當前數字的位數
關於這一步,慣常的想法是通過不斷 /10 得到位數,事實上最快的做法是用 if 條件語句直接判斷。由於int類型最大數是42億左右,十全十美數已經超過了這個範圍,所以我們只考慮十位以下的數。
int getBits(int number) {
if (number >= 100000000 && number < 1000000000) {
return 9;
}
if (number >= 10000000 && number < 100000000) {
return 8;
}
if (number >= 1000000 && number < 10000000) {
return 7;
}
if (number >= 100000 && number < 1000000) {
return 6;
}
if (number >= 10000 && number < 100000) {
return 5;
}
if (number >= 1000 && number < 10000) {
return 4;
}
if (number >= 100 && number < 1000) {
return 3;
}
if (number >= 10 && number < 100) {
return 2;
}
if (number >= 0 && number < 10) {
return 1;
}
}
這裏還有個需要注意的地方,就是要從最大位數開始判斷,因爲往後數字越大最後判斷自冪數的時間就越長,所以把最大的數放在靠上面的語句,這樣可以更快的得到更大數的位數,即使只是省了幾行語句的時間。
b. 判斷自冪數
要知道一個數是不是自冪數,我們需要將每一位的數字的位數次方加起來,然後和本身做比較。這就牽扯到每次判斷可能都要調用pow()函數,但是這樣是很花時間的。
在做哥德巴赫猜想驗證時,我們先生成了一個質數池,每次判斷一個數是不是質數,直接在質數池中取就知道了。同樣的思路,我們判斷自冪數時,不用當場算它的冪級數,而是直接取。這就用到了二維數組。
因爲每個數字的每一位上只可能是0 到 9 這十個數字,且數字的位數也是0 到 9位,所以其冪指數也是隻可能是 0 到 9 這十個數字。所以我們先算完,然後存入二維數組中,到時候直接調取就好了。
const int array[10][10] = {
/*0*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/*1*/ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
/*2*/ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81,
/*3*/ 0, 1, 8, 27, 16*4, 125, 36*6, 49*7, 64*8, 81*9,
/*4*/ 0, 1, 16, 81, 16*4*4, 125*5, 36*6*6, 49*7*7, 64*8*8, 81*9*9,
/*5*/ 0, 1, 32, 243, 16*4*4*4, 125*5*5, 36*6*6*6, 49*7*7*7, 64*8*8*8, 81*9*9*9,
/*6*/ 0, 1, 64, 729, 16*4*4*4*4, 125*5*5*5, 36*6*6*6*6, 49*7*7*7*7, 64*8*8*8*8, 81*9*9*9*9,
/*7*/ 0, 1, 128, 729*3, 16*4*4*4*4*4, 125*5*5*5*5, 36*6*6*6*6*6, 49*7*7*7*7*7, 64*8*8*8*8*8, 81*9*9*9*9*9,
/*8*/ 0, 1, 256, 729*3*3, 16*4*4*4*4*4*4, 125*5*5*5*5*5, 36*6*6*6*6*6*6, 49*7*7*7*7*7*7, 64*8*8*8*8*8*8, 81*9*9*9*9*9*9,
/*9*/ 0, 1, 512, 729*3*3*3, 16*4*4*4*4*4*4*4, 125*5*5*5*5*5*5, 36*6*6*6*6*6*6*6, 49*7*7*7*7*7*7*7, 64*8*8*8*8*8*8*8, 81*9*9*9*9*9*9*9,
};
我用const定義了一個二維數組,所以不需要每一個值我都計算出來,這些數會在預編譯的過程中由C語言編譯器直接計算完成,不會佔用程序運行的時間。
其行爲冪指數,列爲對應的位。
所以判斷自冪數的代碼如下👇
boolean isSelfPower(int number, int bits) {
int i;
int bit;
int sum = 0;
int num = number;
for (; sum <= number && num; num /= 10) {
bit = num % 10;
sum += power(bits, bit);
}
return sum == number;
}
int power(int bits, int number) {
return array[bits][number];
}
那麼這兩個關鍵步驟已經優化完成了。
Ⅳ 完整代碼
#include <stdio.h>
#include <time.h>
const int array[10][10] = {
/*0*/ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
/*1*/ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
/*2*/ 0, 1, 4, 9, 16, 25, 36, 49, 64, 81,
/*3*/ 0, 1, 8, 27, 16*4, 125, 36*6, 49*7, 64*8, 81*9,
/*4*/ 0, 1, 16, 81, 16*4*4, 125*5, 36*6*6, 49*7*7, 64*8*8, 81*9*9,
/*5*/ 0, 1, 32, 243, 16*4*4*4, 125*5*5, 36*6*6*6, 49*7*7*7, 64*8*8*8, 81*9*9*9,
/*6*/ 0, 1, 64, 729, 16*4*4*4*4, 125*5*5*5, 36*6*6*6*6, 49*7*7*7*7, 64*8*8*8*8, 81*9*9*9*9,
/*7*/ 0, 1, 128, 729*3, 16*4*4*4*4*4, 125*5*5*5*5, 36*6*6*6*6*6, 49*7*7*7*7*7, 64*8*8*8*8*8, 81*9*9*9*9*9,
/*8*/ 0, 1, 256, 729*3*3, 16*4*4*4*4*4*4, 125*5*5*5*5*5, 36*6*6*6*6*6*6, 49*7*7*7*7*7*7, 64*8*8*8*8*8*8, 81*9*9*9*9*9*9,
/*9*/ 0, 1, 512, 729*3*3*3, 16*4*4*4*4*4*4*4, 125*5*5*5*5*5*5, 36*6*6*6*6*6*6*6, 49*7*7*7*7*7*7*7, 64*8*8*8*8*8*8*8, 81*9*9*9*9*9*9*9,
};
typedef unsigned char boolean;
int getBits(int number);
int power(int bits, int number);
boolean isSelfPower(int number, int bits);
boolean isSelfPower(int number, int bits) {
int i;
int bit;
int sum = 0;
int num = number;
for (; sum <= number && num; num /= 10) {
bit = num % 10;
sum += power(bits, bit);
}
return sum == number;
}
int power(int bits, int number) {
return array[bits][number];
}
int getBits(int number) {
if (number >= 100000000 && number < 1000000000) {
return 9;
}
if (number >= 10000000 && number < 100000000) {
return 8;
}
if (number >= 1000000 && number < 10000000) {
return 7;
}
if (number >= 100000 && number < 1000000) {
return 6;
}
if (number >= 10000 && number < 100000) {
return 5;
}
if (number >= 1000 && number < 10000) {
return 4;
}
if (number >= 100 && number < 1000) {
return 3;
}
if (number >= 10 && number < 100) {
return 2;
}
if (number >= 0 && number < 10) {
return 1;
}
}
int main() {
int i;
int bits;
int ceiling;
long startTime;
long endTime;
printf("請輸入最大數:");
scanf("%d", &ceiling);
startTime = clock();
for (i = 1; i < ceiling; i++) {
bits = getBits(i);
if (isSelfPower(i, bits)) {
printf("%d\n", i);
}
}
endTime = clock();
endTime -= startTime;
printf("消耗時間:%d.%03ds\n", endTime / 1000, endTime % 1000);
return 0;
}
運行結果如下:
n爲6時👇
只消耗了0.048秒。
n爲8時👇
消耗時間6.022秒。
到了最大位,n爲9時👇
消耗了一分多,前面其實都很快,找到最後一個數花費了很多時間。
所以快的像一道閃電是騙你們的,不過已經很快了,畢竟不是個簡單的事。
下面這個纔是快得像閃電一樣,並且已經優化到了極致。感興趣的同學可以移步此處。
【C語言】->哥德巴赫猜想驗證->篩選法->算法極限優化之你不可能比我快
以上即爲我對查找自冪數的算法優化,大家可以再多加嘗試,找到更好的方法來優化。