自守數:一個自然數的平方,其尾部數字恰好等於該數本身。
規律:自守數以 5、6 結尾,且呈遞歸增長模式(n 位自守數可推導 n+1 位)。
設 n 為 k 位數,則 n 為自守數 ⇔ n² ≡ n (mod 10ᵏ) ⇔ n(n-1) ≡ 0 (mod 10ᵏ)。
即 10ᵏ 整除 n(n-1)。因 n、n-1 互質,故 2ᵏ 整除其中一個,5ᵏ 整除另一個。這解釋了為何自守數成對出現(以 5/6 結尾)。
bool is_automorphic(long n) {
long sq = n * n;
long mod = 1;
while (mod <= n) mod *= 10; // 10^k
return sq % mod == n;
}
void print_automorphic_brute(long limit) {
for (long i = 0; i < limit; ++i)
if (is_automorphic(i)) printf("%ld ", i);
}
時間複雜度 O(N log N),N=10⁶ 尚可,N=10⁹ 不可行。
利用性質:若 x 是 k 位自守數,則存在唯一 d∈{0..9} 使 y = d×10ᵏ + x 為 k+1 位自守數。
void gen_automorphic_recursive(long limit) {
long bases[] = {5, 6}; // 1 位種子
for (int b_idx = 0; b_idx < 2; ++b_idx) {
long x = bases[b_idx];
long mod = 10;
while (x < limit) {
printf("%ld ", x);
// 尋找下一位 d
for (int d = 0; d <= 9; ++d) {
long y = d * mod + x;
if ((y * y) % (mod * 10) == y) {
x = y;
mod *= 10;
break;
}
}
}
}
}
可瞬間生成 64 位範圍內所有自守數(僅 2×19 個)。
解同餘組:
用擴展歐幾里得求模逆,直接計算 k 位解。代碼略長,適合大數庫(GMP)實現任意精度自守數。
輸入代碼使用巨集 `forech_bit_num`、`forech_number`、`automorphic_number` 實現位級部分積累加判斷,屬於手工模擬乘法列豎式的逐位驗證法。邏輯正確但可讀性極差,現代 C 建議用函數+模運算重寫。
#include <stdio.h>
#include <stdint.h>
// 判斷 n 是否為自守數
bool is_automorphic(uint64_t n) {
if (n == 0 || n == 1) return true;
uint64_t mod = 10;
while (mod <= n) mod *= 10;
return (n * n) % mod == n;
}
// 遞歸生成所有 < limit 的自守數
void gen_automorphic(uint64_t limit) {
uint64_t seeds[] = {5, 6};
for (int i = 0; i < 2; ++i) {
uint64_t x = seeds[i];
uint64_t mod = 10;
while (x < limit) {
printf("%llu ", x);
// 試探下一位
for (int d = 0; d <= 9; ++d) {
uint64_t y = d * mod + x;
uint64_t next_mod = mod * 10;
if ((y * y) % next_mod == y) {
x = y;
mod = next_mod;
break;
}
}
}
}
puts("");
}
int main(void) {
printf("暴力法 < 10000: ");
for (uint64_t i = 0; i < 10000; ++i)
if (is_automorphic(i)) printf("%llu ", i);
puts("\n遞歸法 < 10^10: ");
gen_automorphic(10000000000ULL);
return 0;
}
暴力法 < 10000: 0 1 5 6 25 76 376 625 9376
遞歸法 < 10^10: 5 25 625 90625 890625 2890625 12890625 212890625 8212890625 6 76 376 9376 109376 7109376 87109376 787109376 1787109376