首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> C 語言入門

自守數算法 C 語言實現:從數學原理到位運算優化的完整解析

     編輯:C 語言入門

什麼是自守數?

自守數:一個自然數的平方,其尾部數字恰好等於該數本身。

  • 5² = 25
  • 6² = 36
  • 25² = 625
  • 76² = 5776
  • 376² = 141376
  • 9376² = 87909376

規律:自守數以 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 結尾)。

算法演進:從暴力到數學優化

版本 1:暴力枚舉(教學用)

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⁹ 不可行。

版本 2:遞歸構造(數學本質,O(log N) 生成所有)

利用性質:若 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 個)。

版本 3:中國剩餘定理(CRT)直接求解

解同餘組:

  • 情況 A:n ≡ 0 (mod 2ᵏ), n ≡ 1 (mod 5ᵏ) → 以 6 結尾序列
  • 情況 B:n ≡ 1 (mod 2ᵏ), n ≡ 0 (mod 5ᵏ) → 以 5 結尾序列

用擴展歐幾里得求模逆,直接計算 k 位解。代碼略長,適合大數庫(GMP)實現任意精度自守數。

原始代碼分析與重構

輸入代碼使用巨集 `forech_bit_num`、`forech_number`、`automorphic_number` 實現位級部分積累加判斷,屬於手工模擬乘法列豎式的逐位驗證法。邏輯正確但可讀性極差,現代 C 建議用函數+模運算重寫。

完整可運行參考代碼(C99)

#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

總結與拓展

  • 自守數是數論趣味題,實際應用極少,但模運算、遞歸構造、CRT 是核心技能可迁移。
  • 面試常考:判斷自守數、輸出前 N 個、大數實現(字符串/大整數庫)。
  • 擴展:自守數在不同進制下的性質、n 次方自守數(n³ 尾部等於 n)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved