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

Cマガ電脳クラブ(第126回) 開き直り数

問題

3435という数は、1桁ずつに分けて、それぞれの数を自分と同じ数でべき乗して足し合わせると、元の数である3435になる。
  3435 = 3の3乗 + 4の4乗 + 3の3乗 + 5の5乗
このような数を“開き直り数”と呼ぶことにする。ちょっと考えれば1(=1の1乗)もこの種の数であることがわかる。
では1以上の整数で、この1と3435以外の開き直り数をすべて見つけていただきたい。
ただし、ここでは0の0乗は0とする。


ソース

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

class Program
{
    struct JyoutaiDef
    {
        internal long CurrNum;
        internal List<long> NumList;
    }

    static void Main()
    {
        var stk = new Stack<JyoutaiDef>();
        JyoutaiDef WillPush;
        for (long I = 0; I <= 9; I++) {
            WillPush.CurrNum = I;
            WillPush.NumList = new List<long>() { I };
            stk.Push(WillPush);
        }
        var HirakiNaoriSuuList = new List<long>();
        while (stk.Count > 0) {
            JyoutaiDef Popped = stk.Pop();

            //開き直り数かを判定
            long wkHirakiNaoriSuu;
            if (IsHirakiNaoriSuu(Popped.NumList, out wkHirakiNaoriSuu)) {
                HirakiNaoriSuuList.Add(wkHirakiNaoriSuu);
            }

            //枝切り
            if (Popped.NumList.Count >= 10) continue;

            for (long I = Popped.CurrNum; I <= 9; I++) {
                WillPush.CurrNum = I;
                WillPush.NumList = new List<long>(Popped.NumList) { I };
                stk.Push(WillPush);
            }
        }
        HirakiNaoriSuuList.RemoveAll(X => X < 1); //1未満は不適
        HirakiNaoriSuuList.Sort();
        HirakiNaoriSuuList.ForEach(X => Console.WriteLine(X));
    }

    //開き直り数かを判定
    static bool IsHirakiNaoriSuu(List<long> pNumList, out long pHirakiNaoriSuu)
    {
        pHirakiNaoriSuu = -1;

        long BekijyouSum = 0;
        pNumList.ForEach(X => BekijyouSum += DeriveBekijyou(X));
        List<long> SumNumList = DeriveNumList(BekijyouSum);

        if (pNumList.Count != SumNumList.Count) return false;

        var tmp1 = pNumList.OrderBy(X => X);
        var tmp2 = SumNumList.OrderBy(X => X);
        if (tmp1.SequenceEqual(tmp2)) {
            pHirakiNaoriSuu = BekijyouSum;
            return true;
        }
        return false;
    }

    //べき乗を求める
    static long DeriveBekijyou(long pNum)
    {
        if (pNum == 0) return 0;
        long WillReturn = 1;
        for (long I = 1; I <= pNum; I++) {
            WillReturn *= pNum;
        }
        return WillReturn;
    }

    //数値から数字のListを求めて返す
    static List<long> DeriveNumList(long pVal)
    {
        var WillReturn = new List<long>();
        long CopiedVal = pVal;
        do {
            long ModVal = CopiedVal % 10;
            WillReturn.Add(ModVal);
            CopiedVal /= 10;
        } while (CopiedVal > 0);
        return WillReturn;
    }
}


実行結果

1
3435
438579088


解説

9の9乗=387420489 であり、
387420489 *  1 =  387420489
387420489 *  2 =  774840978
387420489 *  3 = 1162261467
387420489 *  4 = 1549681956
387420489 *  5 = 1937102445
387420489 *  6 = 2324522934
387420489 *  7 = 2711943423
387420489 *  8 = 3099363912
387420489 *  9 = 3486784401
387420489 * 10 = 3874204890
387420489 * 11 = 4261625379
387420489 * 12 = 4649045868
387420489 * 13 = 5036466357
387420489 * 14 = 5423886846
387420489 * 15 = 5811307335
なので、10桁以下であることが、開き直り数である必要条件です。

0から9の数を重複を許して、10個まで用意して、
それぞれのべき乗の和が、開き直り数かを調べてます。

9H10 = (9+10-1)C10 = 18C8 = 43758
なので1から3874204890までループさせるよりも、計算量が少ないです。

第001回 10桁の数の怪