素数

From Usipedia
Jump to: navigation, search

Contents

はじめに

学校で出された課題がきっかけになって,色々と調べた事をまとめてみました.
このページの内容をレポートなどに書く際は,参考文献としてここへのリンクを貼ってくれたら喜びます.

役に立つ原理

  • 素数の集合をP,A={2, 3, 5, 6k+1, 6k+5 (kは正整数)}とするとP⊂A
  • 集合Aで一番小さい素数でない数は25(=6*4+1)

エラトステネスのふるいを用いたPrimeクラス

概要

標準ライブラリしか使ってない,エラトステネスのふるいで実装したシングルトンなPrimeクラスです.
unsigned intの範囲内の自然数なら超高速で素数判定します.
疑素数やカーマイケル数でも全く問題ありません.

お役立ちな原理

  • 自然数nをふるいにかける際は2から(int)sqrt((float)n)の間にある素数で割って一度も割り切れなかったらnは素数

ささやかな工夫

  • 自然数nが素数かどうか記録した素数テーブルをstd::bitsetで実装
    • nビット目は素数なら1,素数でないなら0が登録されます
  • インスタンスが生成された時点でn=65535までの素数テーブルを作ります
  • これで4294836224くらいまでの数なら一瞬で素数判定できますね!

なんでstd::bitset使ったの?

正直,bool型の配列でも全くかまいません.
けど,std::bitsetの方が各演算子や any(), count(), to_stringなどが使えて便利です.

ソースコード

Prime.h

// Author : usi3
// http://usi3.com/
 
#include <bitset>
#include <cmath>
using namespace std;
 
#define N 65535
 
class Prime{
private:
	Prime(void){}
	Prime(Prime*);
	static Prime *pInstance;
	// nビット目にnが素数かどうかが記録される2進数のテーブル
	bitset<N> table;
 
public:
	static Prime* getInstance(void){
		if(!pInstance){
			// インスタンス生成
			pInstance = new Prime;
			pInstance->init();
		}
		return pInstance;
	}
 
	void init(){
		// 集合Aを素数候補としてとりあえず登録
		table.set(2);
		table.set(3);
		table.set(5);
		for(int i=1; i<(N-1-(N-1)%6)/6; i++){
			table.set(6*i+1);
		}
		for(int i=1; i<(N-5-(N-5)%6)/6; i++){
			table.set(6*i+5);
		}
		// 候補のみをエラトステネスのふるいにかける
		for(int i=25; i<N; i++){
			if(table.at(i)){
				if(!eratos(i)) table.reset(i);
			}
		}
	}
 
	// エラトステネスのふるい
	bool eratos(unsigned int p){
		int r = sqrt((float)p);
		for(int i=2; i<=r; i++){
			if(table.at(i)){
				if(p%i == 0) return false;
			}
		}
		return true;
	}
 
	// 整形して素数一覧を出力
	void view(unsigned int n){
		for(int i=0, c=0; i<n; i++){
			if(is(i)){
				printf("%5u ",i);
				if((++c)%10 == 0) cout << endl;
			}
		}
		cout << endl;
	}
 
	// 素数判定
	bool is(unsigned int n){
		if(n < N){
			return table.at(n);
		}else if(n < N*N){
			return eratos(n);
		}
	}
 
};
 
Prime* Prime::pInstance = 0;
}
}


使用例

main.cpp

#include <iostream>
#include "Prime.h"
using namespace std;
 
int main(void){
	Prime* p = Prime::getInstance();
 
	// 100以下の素数を整形して表示
	p->view(100);
 
	// 疑素数
	cout << p->is(1905) << endl;
	cout << p->is(8911) << endl;
 
	// カーマイケル数
	cout << p->is(561) << endl;
	cout << p->is(6601) << endl;
 
	// unsigned int 範囲内の大きな素数
	cout << p->is(1024760591) << endl;
	cout << p->is(1046028437) << endl;
	cout << p->is(2330842469) << endl;
	cout << p->is(2988897073) << endl;
	cout << p->is(3118688711) << endl;
	cout << p->is(3938292019) << endl;
	cout << p->is(4148217329) << endl;
 
	return 0;
}

おわりに

  • エラトステネスのふるいじゃ300桁の自然数を素数判定しようと思ったら全く歯が立ちません
    • 疑素数とかでも問題がないMiller-Rabinテストがおすすめです!
Namespaces
Variants
Views
Actions
Categories