トップページに戻る    次のC#のサンプルへ    前のC#のサンプルへ

Problem74 桁の階乗による連鎖

問題

145は各桁の階乗の和が145と自分自身に一致することで有名である.

1! + 4! + 5! = 1 + 24 + 120 = 145

169の性質はあまり知られていない.
これは169に戻る数の中で最長の列を成す.
このように他の数を経て自分自身に戻るループは3つしか存在しない.

169 → 363601 → 1454 → 169
871 →  45361 →  871
872 →  45362 →  872

どのような数からスタートしてもループに入ることが示せる.

例を見てみよう.

 69 → 363600 → 1454 →   169 → 363601 → 1454
 78 →  45360 →  871 → 45361 →    871
540 →    145 →  145

69から始めた場合, 列は5つの循環しない項を持つ.
また100万未満の数から始めた場合、最長の循環しない項は60個であることが知られている.

100万未満の数から開始する列の中で, 60個の循環しない項を持つものはいくつあるか?


ソース

using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        //foreach (int EachInt in new int[] { 145, 169, 871, 872, 69, 78, 540 }) {
        //    Console.WriteLine("{0}の循環しない項は{1}", EachInt, DeriveKousuu(EachInt));
        //    Console.WriteLine();
        //}

        var sw = System.Diagnostics.Stopwatch.StartNew();
        int AnswerCnt = 0;
        for (int I = 1; I <= 999999; I++) {
            if (I % 50000 == 0) {
                Console.WriteLine("{0}をチェック中。AnswerCnt={1}。経過時間={2}", I, AnswerCnt, sw.Elapsed);
            }
            if (DeriveKousuu(I) == 60) AnswerCnt++;
        }
        Console.WriteLine("Answer={0}", AnswerCnt);
    }

    static int DeriveKousuu(int pTarget)
    {
        var ValList = new List<int>();
        ValList.Add(pTarget);

        int CurrVal = pTarget;
        //Console.Write("{0}", CurrVal);

        while (true) {
            int NextVal = 0;
            foreach (char EachChar in CurrVal.ToString()) {
                NextVal += CalcKaijyo(EachChar - '0');
            }
            //Console.Write("→{0}", NextVal);

            if (ValList.Contains(NextVal)) {
                //Console.WriteLine();
                return ValList.Count;
            }
            else ValList.Add(NextVal);

            CurrVal = NextVal;
        }
    }

    static Dictionary<int, int> mKaijyoDict = new Dictionary<int, int>();
    static int CalcKaijyo(int pN)
    {
        if (mKaijyoDict.ContainsKey(pN))
            return mKaijyoDict[pN];

        int WillReturn = 1;
        for (int I = 2; I <= pN; I++) {
            WillReturn *= I;
        }
        return mKaijyoDict[pN] = WillReturn;
    }
}


実行結果

省略
700000をチェック中。AnswerCnt=282。経過時間=00:00:59.8240080
750000をチェック中。AnswerCnt=330。経過時間=00:01:03.9670251
800000をチェック中。AnswerCnt=342。経過時間=00:01:08.2473174
850000をチェック中。AnswerCnt=342。経過時間=00:01:12.5488490
900000をチェック中。AnswerCnt=342。経過時間=00:01:16.9981156
950000をチェック中。AnswerCnt=390。経過時間=00:01:21.5248887
Answer=402


解説

配列の初期化を使った下記の記述で事前検証してます。

foreach (int EachInt in new int[] { 145, 169, 871, 872, 69, 78, 540 }) {
    Console.WriteLine("{0}の循環しない項は{1}", EachInt, DeriveKousuu(EachInt));
    Console.WriteLine();
}