トップページに戻る
次のC++のサンプルへ
前のC++のサンプルへ
Problem60 任意の2つの素数を繋げても素数な、5つの素数の組
問題
素数3, 7, 109, 673は非凡な性質を持っている.
任意の2つの素数を任意の順で繋げると, また素数になっている.
例えば, 7と109を用いると, 7109と1097の両方が素数である.
これら4つの素数の和は792である.
これは, このような性質をもつ4つの素数の組の和の中で最小である.
任意の2つの素数を繋げたときに別の素数が生成される, 5つの素数の組の和の中で最小のものを求めよ.
ソース
#include <valarray>
#include <vector>
#include <string>
#include <stack>
#include <Windows.h>
#include <stdio.h>
#include <numeric>
#include <algorithm>
#include <functional>
//const int CombinationCnt = 4;
//const int Jyougen = 800;
const int CombinationCnt = 5;
const int Jyougen = 27000;
std::vector<int> SosuuVect;
bool IsSosuu(__int64 pVal);
__int64 DeriveConcatVal(int pLeft, int pRight);
//エラトステネスの篩
void Eratosthenes()
{
std::valarray<bool> IsSosuuArr(true,Jyougen+1);
for (int I = 2; I * I <= (int)IsSosuuArr.size()-1; I++) {
if(IsSosuuArr[I] == false) continue;
for (int J = I * 2; J <= (int)IsSosuuArr.size()-1; J += I) {
IsSosuuArr[J] = false;
}
}
for (int I = 2; I <= (int)IsSosuuArr.size()-1; I++) {
if(IsSosuuArr[I] == false) continue;
SosuuVect.push_back(I);
}
std::sort(SosuuVect.begin(),SosuuVect.end(), std::greater<int>());
}
struct JyoutaiDef
{
std::vector<int> ValVect;
std::string Path;
};
void main()
{
Eratosthenes();
std::stack<JyoutaiDef> stk;
JyoutaiDef WillPush;
for(std::vector<int>::iterator it=SosuuVect.begin();it!=SosuuVect.end();it++){
WillPush.ValVect = std::vector<int>(1,*it);
char wkCharArr[100];wsprintf(wkCharArr,"%d,",*it);
WillPush.Path = wkCharArr;
stk.push(WillPush);
}
std::string AnswerPath;
int AnswerSumMin = Jyougen;
while(stk.empty() == false){
JyoutaiDef Popped = stk.top(); stk.pop();
printf("Path=%s 解候補=%s\n",Popped.Path.c_str(),AnswerPath.c_str());
int wkSum = std::accumulate(Popped.ValVect.begin(),Popped.ValVect.end(),0);
if (Popped.ValVect.size() == CombinationCnt && wkSum < AnswerSumMin) {
AnswerSumMin = wkSum;
AnswerPath = Popped.Path;
continue;
}
int ValCnt = Popped.ValVect.size();
int RestCnt = CombinationCnt - ValCnt;
int LastVal = Popped.ValVect.at(ValCnt - 1);
for(std::vector<int>::iterator it=SosuuVect.begin();it!=SosuuVect.end();it++){
if(! (*it > LastVal && AnswerSumMin > wkSum + *it * RestCnt) ) continue;
bool wkBool = true;
for(int I=0;I<=(int)Popped.ValVect.size()-1;I++){
if(IsSosuu(DeriveConcatVal(Popped.ValVect.at(I),*it)) == false
|| IsSosuu(DeriveConcatVal(*it,Popped.ValVect.at(I))) == false){
wkBool = false;
break;
}
}
if (wkBool == false) continue;
WillPush.ValVect = Popped.ValVect;
WillPush.ValVect.push_back(*it);
WillPush.Path = Popped.Path;
char wkCharArr[100];wsprintf(wkCharArr,"%d,",*it);
WillPush.Path += wkCharArr;
stk.push(WillPush);
}
}
printf("%d以下の素数の組み合わせで検証しました。\n",Jyougen);
int MaxVal=INT_MIN;
for(int I=0;I<=(int)SosuuVect.size()-1;I++)
if(MaxVal < SosuuVect.at(I)) MaxVal = SosuuVect.at(I);
printf("%d以下の素数の最大値は%dです。\n",Jyougen,MaxVal);
printf("解は%sで和は%dです。\n",AnswerPath.c_str(),AnswerSumMin);
}
bool IsSosuu(__int64 pVal)
{
if (pVal <= Jyougen)
return std::find(SosuuVect.begin(),SosuuVect.end(),(int)pVal) != SosuuVect.end();
if (pVal % 2 == 0) return false;
for (int I = 3; I * I <= pVal; I += 2) {
if (pVal % I == 0) return false;
}
return true;
}
__int64 DeriveConcatVal(int pLeft, int pRight)
{
__int64 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;
}
実行結果
省略
Path=26951, 解候補=13,5197,5701,6733,8389,
Path=26953, 解候補=13,5197,5701,6733,8389,
Path=26959, 解候補=13,5197,5701,6733,8389,
Path=26981, 解候補=13,5197,5701,6733,8389,
Path=26987, 解候補=13,5197,5701,6733,8389,
Path=26993, 解候補=13,5197,5701,6733,8389,
27000以下の素数の組み合わせで検証しました。
27000以下の素数の最大値は26993です。
解は13,5197,5701,6733,8389,で和は26033です。
解説
深さ優先探索で早く解候補を求めて、枝切り条件に使用してます。
枝切りでは、
素数の昇順になる組み合わせでチェックしているので
解候補の和を超える素数の組み合わせを枝切りしてます。
素数の和の上限を27000と仮決めした上で、
27000以下の素数の組み合わせで検証して
求まった解の合計が26033で
27000 > 26033 なので、5つの素数の組の和の中で最小のものという題意を満たしてます :-)
反復深化で素数の上限を増やしていくほうが、
解答としては優れているかもしれません :-)