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

Problem60 任意の2つの素数を繋げても素数な、5つの素数の組

問題

素数3, 7, 109, 673は非凡な性質を持っている.
任意の2つの素数を任意の順で繋げると, また素数になっている.

例えば, 7と109を用いると, 7109と1097の両方が素数である.
これら4つの素数の和は792である.
これは, このような性質をもつ4つの素数の組の和の中で最小である.

任意の2つの素数を繋げたときに別の素数が生成される, 5つの素数の組の和の中で最小のものを求めよ.


ソース

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

class Program
{
    //const int CombinationCnt = 4;
    //const int Jyougen = 800;
    const int CombinationCnt = 5;
    const int Jyougen = 27000;

    static int[] SosuuArr = new int[Jyougen + 1];

    //エラトステネスの篩
    static void Eratosthenes()
    {
        for (int I = 2; I <= SosuuArr.GetUpperBound(0); I++) {
            SosuuArr[I] = I;
        }
        for (int I = 2; I * I <= SosuuArr.GetUpperBound(0); I++) {
            if (I != 2 && I % 2 == 0) continue;

            if (SosuuArr[I] != 0) {
                for (int J = I * 2; J <= SosuuArr.GetUpperBound(0); J += I) {
                    SosuuArr[J] = 0;
                }
            }
        }
        SosuuArr = SosuuArr.Where(X => X != 0).OrderByDescending(X => X).ToArray();
    }

    struct JyoutaiDef
    {
        internal List<int> ValList;
        internal string Path;
    }

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

        Eratosthenes();

        var Stk = new Stack<JyoutaiDef>();
        JyoutaiDef WillPush;
        foreach (int Each in SosuuArr) {
            WillPush.ValList = new List<int> { Each };
            WillPush.Path = Each.ToString() + ",";
            Stk.Push(WillPush);
        }

        string AnswerPath = "";
        int AnswerSumMin = Jyougen;

        while (Stk.Count > 0) {
            JyoutaiDef Popped = Stk.Pop();
            Console.WriteLine("{0} Path={1} 解候補={2}", sw.Elapsed, Popped.Path, AnswerPath);

            int wkSum = Popped.ValList.Sum();

            if (Popped.ValList.Count == CombinationCnt && wkSum < AnswerSumMin) {
                AnswerSumMin = wkSum;
                AnswerPath = Popped.Path;
                continue;
            }

            int ValCnt = Popped.ValList.Count;
            int RestCnt = CombinationCnt - ValCnt;

            int LastVal = Popped.ValList[ValCnt - 1];
            foreach (int Each in SosuuArr.Where(X => X > LastVal
                                             && AnswerSumMin > wkSum + X * RestCnt)) {
                bool wkBool;
                wkBool = Popped.ValList.TrueForAll(X => IsSosuu(DeriveConcatVal(X, Each)));
                if (wkBool == false) continue;
                wkBool = Popped.ValList.TrueForAll(X => IsSosuu(DeriveConcatVal(Each, X)));
                if (wkBool == false) continue;

                WillPush.ValList = new List<int>(Popped.ValList);
                WillPush.ValList.Add(Each);
                WillPush.Path = Popped.Path + Each.ToString() + ",";
                Stk.Push(WillPush);
            }
        }
        Console.WriteLine("{0}以下の素数の組み合わせで検証しました。", Jyougen);
        Console.WriteLine("解は{0}で和は{1}です。", AnswerPath, AnswerSumMin);
    }

    static bool IsSosuu(long pVal)
    {
        if (pVal <= Jyougen) return SosuuArr.Contains((int)pVal);

        if (pVal % 2 == 0) return false;
        for (int I = 3; I * I <= pVal; I += 2) {
            if (pVal % I == 0) return false;
        }
        return true;
    }

    static long DeriveConcatVal(int pLeft, int pRight)
    {
        //return long.Parse(pLeft.ToString() + pRight.ToString()); のちょっとだけ速度改善
        long WillReturn = pLeft;
        if (pRight < 10) WillReturn *= 10; //1桁の場合
        else if (pRight < 100) WillReturn *= 100; //2桁の場合
        else if (pRight < 1000) WillReturn *= 1000; //3桁の場合
        else if (pRight < 10000) WillReturn *= 10000; //4桁の場合
        else if (pRight < 100000) WillReturn *= 100000; //5桁の場合

        WillReturn += pRight;
        return WillReturn;
    }
}


実行結果

省略
00:00:12.7278866 Path=26953, 解候補=13,5197,5701,6733,8389,
00:00:12.7279056 Path=26959, 解候補=13,5197,5701,6733,8389,
00:00:12.7279282 Path=26981, 解候補=13,5197,5701,6733,8389,
00:00:12.7279623 Path=26987, 解候補=13,5197,5701,6733,8389,
00:00:12.7279847 Path=26993, 解候補=13,5197,5701,6733,8389,
27000以下の素数の組み合わせで検証しました。
解は13,5197,5701,6733,8389,で和は26033です。


解説

深さ優先探索で早く解候補を求めて、枝切り条件に使用してます。

枝切りでは、
素数の昇順になる組み合わせでチェックしているので
解候補の和を超える素数の組み合わせを枝切りしてます。

素数の和の上限を27000と仮決めした上で、
27000以下の素数の組み合わせで検証して
求まった解の合計が26033で
27000 > 26033 なので、5つの素数の組の和の中で最小のものという題意を満たしてます :-)
反復深化で素数の上限を増やしていくほうが、
解答としては優れているかもしれません :-)