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

Cマガ電脳クラブ(第031回) 小町素因数分解

問題

34504345 = 5 x 29 x 47 x 61 x 83
上の式は素因数分解である。ところが、この右辺には1〜9がすべて1字ずつ登場している。
ではこのように1〜9のすべての数字が1回ずつ登場するような素因数分解のうち、左辺の最小数を求めてください。

なお、ここにあげた34504345は最小ではない。
さらに、0〜9の10種の数字を含む場合はどうだろうか。
もちろん、0は各素数の左端にはこない。


ソース

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

class Program
{
    const int Jyougen = 16000000; //仮の上限
    static List<int> SosuuList;

    //エラトステネスの篩
    static void Eratosthenes()
    {
        var IsSosuuArr = new System.Collections.BitArray(Jyougen + 1);

        for (int I = 2; I <= IsSosuuArr.Count - 1; I++) {
            IsSosuuArr[I] = true;
        }
        for (int I = 2; I * I <= IsSosuuArr.Count - 1; I++) {
            if (IsSosuuArr[I] == false) continue;
            for (int J = I * 2; J <= IsSosuuArr.Count - 1; J += I) {
                IsSosuuArr[J] = false;
            }
        }
        SosuuList = new List<int>();
        for (int I = 2; I <= IsSosuuArr.Count - 1; I++) {
            if (IsSosuuArr[I]) SosuuList.Add(I);
        }
    }

    struct JyoutaiDef
    {
        internal int CurrP;
        internal int ProdVal;
        internal bool[] AppearedArr;
        internal string Path;
    }

    static void Main()
    {
        Eratosthenes();

        //同じ数字が2つ以上あったらRemove
        Predicate<int> HasDuplicateInNum = pTarget =>
        {
            int[] AppearCntArr = new int[10 + 1];
            do {
                int wkInt = pTarget % 10;
                if (++AppearCntArr[wkInt] == 2) return true;
            } while ((pTarget /= 10) > 0);
            return false;
        };
        SosuuList.RemoveAll(X => HasDuplicateInNum(X));

        Console.WriteLine("{0}以下の素数で、", Jyougen);
        Console.WriteLine("積が{0}以下となる組み合わせを検証します。", Jyougen);
        Console.WriteLine("{0}以下の素数の最大値は{1}です。", Jyougen, SosuuList.Max());
        Console.WriteLine();

        var stk = new Stack<JyoutaiDef>();
        JyoutaiDef WillPush;

        for (int P = 0; P <= SosuuList.Count - 1; P++) {
            WillPush.CurrP = P;
            WillPush.ProdVal = SosuuList[P];
            WillPush.AppearedArr = new bool[10];
            FillAppearedArr(SosuuList[P], WillPush.AppearedArr);
            WillPush.Path = SosuuList[P].ToString();
            stk.Push(WillPush);
        }

        int AnswerVal = Jyougen;

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

            if (AnswerVal < Popped.ProdVal) continue;

            if (HasAllNumber(Popped.AppearedArr)) {
                Console.WriteLine("解候補を発見しました。");
                Console.WriteLine("{0}={1}", Popped.Path, Popped.ProdVal);
                AnswerVal = Popped.ProdVal;
                continue;
            }

            for (int P = Popped.CurrP + 1; P <= SosuuList.Count - 1; P++) {
                WillPush.CurrP = P;

                long wkLongProd = (long)Popped.ProdVal * SosuuList[P];
                if (AnswerVal < wkLongProd) break;
                WillPush.ProdVal = (int)wkLongProd;

                WillPush.AppearedArr = (bool[])Popped.AppearedArr.Clone();
                if (FillAppearedArr(SosuuList[P], WillPush.AppearedArr) == false)
                    continue;
                WillPush.Path = Popped.Path + "*" + SosuuList[P].ToString();
                stk.Push(WillPush);
            }
        }
    }

    //登場した数字を添字として配列値にTrueをセット
    //配列値が既にTrue(数字が登場済)であれば、FalseをReturn
    static bool FillAppearedArr(int pTarget, bool[] pAppearedArr)
    {
        do {
            int wkInt = pTarget % 10;
            if (pAppearedArr[wkInt]) return false;
            pAppearedArr[wkInt] = true;
        } while ((pTarget /= 10) > 0);
        return true;
    }

    //配列が、数字を全て含むかを判定
    static bool HasAllNumber(bool[] pAppearedArr)
    {
        return Array.TrueForAll(pAppearedArr, X => X);
    }
}


実行結果

16000000以下の素数で、
積が16000000以下となる組み合わせを検証します。
16000000以下の素数の最大値は15987403です。

解候補を発見しました。
2*3*5*487*1069=15618090


解説

掛け算でInt型の最大値を超える可能性がある箇所のみで、Int型からLong型にキャストしてます。

1から9での答えは、第011回 最小の素数?で扱ってます。
(第011回と第031回は、同じ問題であるため)