トップページに戻る
次のC#のサンプルへ
前のC#のサンプルへ
Cマガ電脳クラブ(第011回) 最小の素数?
問題
次の式を見ると、左辺はすべて素数で、素因数分解になっている。
左辺をもっとよく見ると、1〜9の数字を1度ずつすべて使って組み合わせてある。
2 × 7 × 853 × 6491 = 77515522
ここで問題。上の例のように、
「1〜9の9個の数字を1度ずつすべて使って組み合わせて作った素数の積」が最小になる組み合わせを求めてほしい。
例では4個の素数の積になっているが、何個の積でもよい。ちなみに"1"は素数ではない。
ついでにもう1問。
「1〜9の9個」ではなくて、
「0〜9の10個」ではどうなるだろうか。ただし、0は各素数の最上位桁にはできない。
ソース
using System;
using System.Collections.Generic;
using System.Linq;
class Program
{
const int Jyougen = 3000000; //仮の上限
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));
//ゼロを含んだらRemove
Predicate<int> HasZeroInNum = pTarget =>
{
do {
if (pTarget % 10 == 0) return true;
} while ((pTarget /= 10) > 0);
return false;
};
SosuuList.RemoveAll(X => HasZeroInNum(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)
{
for (int I = 1; I <= 9; I++) {
if (pAppearedArr[I] == false) return false;
}
return true;
}
}
実行結果
3000000以下の素数で、
積が3000000以下となる組み合わせを検証します。
3000000以下の素数の最大値は2987651です。
解候補を発見しました。
2*3*5*67*1489=2992890
解説
掛け算でInt型の最大値を超える可能性がある箇所のみで、Int型からLong型にキャストしてます。
0から9での答えは、第031回 小町素因数分解で扱ってます。
(第011回と第031回は、同じ問題であるため)