Project Euler 35

197は巡回素数と呼ばれる. 桁を回転させたときに得られる数 197, 971, 719 が全て素数だからである.

100未満には巡回素数が13個ある: 2, 3, 5, 7, 11, 13, 17, 31, 37, 71, 73, 79, および97である. 100万未満の巡回素数は何個か?

Mathematica

A = {};

For[i = 2, i < 1000000, i++,
  
  tmp = Table[
    FromDigits[RotateLeft[IntegerDigits[i], j]], {j, 0, 
     Length[IntegerDigits[i]]}
    ];
  
  If[MemberQ[PrimeQ[tmp], False],
   {},
   A = Append[A, i]]
]

Length[A]

さて、これで答えは出せます。
IntegerDigitsで各桁をリストに分解、tmpには桁を回転させた数全てが入ります。
下のIf文で素数かどうかを判定します。が、これではさすがに遅すぎます。
For文をTiming関数で測ったところ、42.3秒。もうちょっと早くするように考えてみました。

A = {2};

For[i = 3, i < 1000000, i += 2,
  
  tmp = Table[
    FromDigits[RotateLeft[IntegerDigits[i], j]], {j, 0, 
     Length[IntegerDigits[i]]}
    ];
  
  If[MemberQ[PrimeQ[tmp], False],
   {},
   A = Append[A, i]]
]
 
Length[A]

第2案。2以外は偶数の素数はないんだから、2は最初から除外して、 i+=2 にしてしまえ!というのがこれ。
当然ながら単純に半分、21.7秒になりました。

A = {2};

For[i = 3, i < 1000000, i += 2,
  
  If[MemberQ[OddQ[IntegerDigits[i]], False], Continue[]];
  
  tmp = Table[
    FromDigits[RotateLeft[IntegerDigits[i], j]], {j, 0, 
     Length[IntegerDigits[i]]}
    ];
  
  If[MemberQ[PrimeQ[tmp], False],
   {},
   A = Append[A, i]]
]

Length[A]

第3案。桁を回転したもの全てが素数でなければいけないということは、各桁に1つでも偶数が入っていればその数は巡回素数ではない、ということですから、
まずは各桁が全て奇数かどうかを判定してみました。
計算時間は5.2秒。最初よりはだいぶよくなりました。


しかし、随分とIf文がまだ多すぎて重い気がしますし、そもそもMathematicaで手続き的に書くと圧倒的に速度が遅くなってしまうので、なにかもっと良い方法があるはずです、それこそ0.1秒クラスで終わるような方法が。


C/C++

#include <iostream>
#include <mpir.h>
#include <deque>
#include <cmath>
using namespace std;

deque<bool> p;

/*
maxまでの素数を計算する。
あらかじめ 
deque<bool> p; 
をグローバルで宣言しておいてね
*/
void calc_prime(const int max){
	for(int i=0 ; i<max+1 ; i++){
		p.push_back(true);
	}
	p[0] = false;
	p[1] = false;

	double myEnd = sqrt(double(max));
	for(int i=2 ; i < myEnd ; i++){
		for(int j=2 ; i*j<=max ; j++){
			p[i*j] = false;
		}
	}
}

// nが素数かどうか
bool is_prime(int n){
	return p[n];
}

// nの桁数を返す。
int int_digits(int n){
	int count = 0;
	while(n!=0){
		n /= 10;
		count++;
	}
	return count;
}

// nが巡回素数かどうか
bool is_circular(int n){
	int k = n, tmp;
	int dig = int_digits(n);

	for(int i=0 ; i<dig ; i++){
		tmp = k%10;
		k /= 10;
		k += tmp * (int)powl(10,dig-1);

		if(!is_prime(k)) return false;
	}
	return true;
}

int main(){
	calc_prime(1000000);

	int count = 0;
	for(int i=1 ; i<1000000 ; i++){
		if(is_circular(i)){
			cout << i << endl;
			count++;
		}
	}

	cout << endl << count << endl;

	int end;
	cin >> end;
	return 0;
}

関数がいろいろ増えちゃった。is_circularと素数計算を組み合わせたら絶対もっと早くなるよなぁ・・・どこかの桁に偶数があったらその時点で巡回素数ではないので。