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

Cマガ電脳クラブ(第001回) 10桁の数の怪

問題

ある10桁の数を考えていただきたい。もちろん、そこには10個の数字が並んでいる。
その数は、「構成する各数字をそれぞれ10乗したものの和」に等しいという。その数はいくつか?

たとえば、以下の式は成立しない。残念ながら右辺が1だけ大きいのだ。
1136483324 = 1の10乗 + 1の10乗 + 3の10乗 + 6の10乗 + 4の10乗 + 8の10乗 + 3の10乗 + 3の10乗 + 2の10乗 + 4の10乗


ソース(力まかせ法)

using System;

class Program
{
    static void Main()
    {
        var sw = System.Diagnostics.Stopwatch.StartNew();

        long Sum10Jyou = 0L;
        long KasanVal = 1;
        for (long I = 1000000000L; I <= 9999999999L; I += KasanVal) {
            if (I % 500000000L == 1L) Console.WriteLine("{0}。{1}をチェック中", sw.Elapsed, I);

            if (I % 10 == 0) {
                Sum10Jyou = DeriveSum10Jyou(I);
            }
            else { //1桁目の差分を加算
                Sum10Jyou -= Get10Jyou(I % 10L - 1L);
                Sum10Jyou += Get10Jyou(I % 10L);
            }

            KasanVal = 1L;
            if (I == Sum10Jyou) Console.WriteLine("Answer={0}", I);
            else if (I < Sum10Jyou) {
                //1桁目が0になるまでスキップ
                KasanVal = 10L - I % 10L;
            }
        }
    }

    static long DeriveSum10Jyou(long pTarget)
    {
        long SavedTarget = pTarget;
        long Sum10Jyou = 0L;
        do {
            long ModVal = pTarget % 10L;
            Sum10Jyou += Get10Jyou(ModVal);
            if (SavedTarget < Sum10Jyou) return Sum10Jyou;
            pTarget /= 10L;
        } while (pTarget > 0L);
        return Sum10Jyou;
    }

    static long Get10Jyou(long pTarget)
    {
        if (pTarget == 1L) return 1L;
        if (pTarget == 2L) return 1024L;
        if (pTarget == 3L) return 59049L;
        if (pTarget == 4L) return 1048576L;
        if (pTarget == 5L) return 9765625L;
        if (pTarget == 6L) return 60466176L;
        if (pTarget == 7L) return 282475249L;
        if (pTarget == 8L) return 1073741824L;
        if (pTarget == 9L) return 3486784401L;
        return 0L;
    }
}


ソース(数字の組み合わせを列挙して、それらの10乗の和を求める方法)

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

class Program
{
    struct JyoutaiDef
    {
        internal int CurrVal;
        internal List<int> ValList;
    }

    static void Main()
    {
        var stk = new Stack<JyoutaiDef>();
        JyoutaiDef WillPush;
        for (int I = 0; I <= 9; I++) {
            WillPush.CurrVal = I;
            WillPush.ValList = new List<int>() { I };
            stk.Push(WillPush);
        }

        while (stk.Count > 0) {
            JyoutaiDef Popped = stk.Pop();

            if (Popped.ValList.Count == 10) {
                long Sum10Jyou = 0;
                foreach (int AnyInt in Popped.ValList)
                    Sum10Jyou += Get10Jyou(AnyInt);
                
                //10桁であること
                if ((1000000000L <= Sum10Jyou && Sum10Jyou <= 9999999999L) == false)
                    continue;

                //数字の使用回数が一致していること
                IEnumerable<int> wkEnum = Sum10Jyou.ToString().ToCharArray().
                    Select(X => int.Parse(X.ToString())).OrderBy(X => X);
                if (wkEnum.SequenceEqual(Popped.ValList.OrderBy(X => X))) {
                    Console.WriteLine("Answer={0}", Sum10Jyou);
                }
                continue;
            }

            for (int I = Popped.CurrVal; I <= 9; I++) {
                WillPush.CurrVal = I;
                WillPush.ValList = new List<int>(Popped.ValList) { I };
                stk.Push(WillPush);
            }
        }
    }

    static long Get10Jyou(int pTarget)
    {
        if (pTarget == 1) return 1L;
        if (pTarget == 2) return 1024L;
        if (pTarget == 3) return 59049L;
        if (pTarget == 4) return 1048576L;
        if (pTarget == 5) return 9765625L;
        if (pTarget == 6) return 60466176L;
        if (pTarget == 7) return 282475249L;
        if (pTarget == 8) return 1073741824L;
        if (pTarget == 9) return 3486784401L;
        return 0L;
    }
}


実行結果

00:00:00.0006210。1000000001をチェック中
00:00:59.8395868。1500000001をチェック中
00:02:00.4806814。2000000001をチェック中
00:03:17.4356782。2500000001をチェック中
00:04:31.0162255。3000000001をチェック中
00:05:57.0148923。3500000001をチェック中
00:07:24.0376388。4000000001をチェック中
00:09:12.7531839。4500000001をチェック中
Answer=4679307774
00:10:58.1672008。5000000001をチェック中
00:13:03.9637828。5500000001をチェック中
00:15:03.1932723。6000000001をチェック中
00:17:14.8784219。6500000001をチェック中
00:19:17.4409099。7000000001をチェック中
00:21:30.5686557。7500000001をチェック中
00:23:40.8811793。8000000001をチェック中
00:25:56.0425442。8500000001をチェック中
00:28:09.4392885。9000000001をチェック中
00:30:18.3545992。9500000001をチェック中


解説

for文の中で、
10で割った剰余が1以上だったら、1桁目の差分を加算することにより
計算量を減らしてます。

数字の組み合わせを列挙して、それらの10乗の和を求める方法だと
劇的に高速化されて1秒もかからず答えが求まります。