【C語言基礎】->自冪數優化->這個算法快得像一道閃電

Ⅰ 自冪數的定義

自冪數是指一個 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語言】->哥德巴赫猜想驗證->篩選法->算法極限優化之你不可能比我快

以上即爲我對查找自冪數的算法優化,大家可以再多加嘗試,找到更好的方法來優化。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章