素数ふるい法(オングストロームふるい、線形ふるいの詳しい説明)

素数を見つける最も簡単な方法は、もちろん 1 つずつたどって、順番に素数であるかどうかを判断することです。問題の核心は素数の判断に戻りますが、数値が素数であるかどうかをどのように判断するのでしょうか?

素数の性質は 1 つだけです。つまり、因数は 1 とそれ自体の 2 つだけであり、この性質を使って素数を判断することしかできません。したがって、n が素数かどうかを判断したい場合は、2 から n-1 までたどることができると想像できます。n-1 の数値のいずれも n を割り切れない場合、n は素数です。C言語の演習で出てきたと記憶していますが、一言で言えば非常にシンプルで、最も単純なアルゴリズムと言えます。

オングストロームふるい法

今回ご紹介するエラトステネスのアルゴリズムは、彼が素数を選別するために発明した手法で、便宜上、一般的にはエシェリヒアの篩、あるいは篩と呼ばれていますエシェリヒアふるい法の考え方は非常にシンプルです。つまり、スクリーニングされた素数を使用して、それで割り切れるすべての数値をフィルタリングすることです。この素数は自然数を濾す篩のようなもので、篩にかけて残った数は当然前の素数では割り切れない数であり、素数の定義によればこの残った数も素数となります。

たとえば、100 以内のすべての素数をフィルターで除外したい場合、2 が最小の素数であることがわかっているので、2 を使用して最初にすべての偶数をフィルターで除外できます。次に、3 に戻ります。3 は、2 でふるいにかけた後に残った最初の数値であり、素数でもあります。次に、3 を使用して、3 で割り切れるすべての数値をふるいにかけます。スクリーニング後、後方へのトラバースを続けます。最初に遭遇する数字は 7 なので、7 も素数です。トラバースが終了するまで上記のプロセスを繰り返します。最後に、100 までのすべての素数が得られます。

それでもよく理解できない場合は、プロセス全体を非常に明確に再現した次のアニメーションを見てください。

時間計算量は O(nlognlongn)

#include<iostream>
using namespace std;
const int N = 1e7;
int prime[N + 1];
bool visit[N + 1];
//素数筛
//埃式筛
int E_sieve(int n) {
	int k = 0;
	for (int i = 0; i < n; i++) visit[i] = false;
	// 1.普通
	/* for (int i = 2; i <= n; i++) {
		if (!visit[i]) {
			prime[k++] = i;
			for (int j = i * 2; j <= n; j+=i) { // i的倍数筛选调
				visit[j] = true;
			}
		}
	}*/
	//2.优化 有些数可能会被多筛几遍 这样会影响时间复杂度
	for (int i = 2; i * i <= n; i++)
		if (!visit[i])
			for (int j = i * i; j <= n; j += i) visit[j] = true;
	int k = 0;
	for (int i = 2; i <= n; i++) { // i * i 前面的合数被更小的素数划掉了
		if (!visit[i]) prime[k++] = i;
	}
	return k; // 1~n中的素数
}


int main() {
	return 0;
}

オイラーふるい

オイラーふるい法は線形ふるい法とも呼ばれ、1~nの素数をO(n)線形時間でふるい分けることができます。

たとえば、合成数 6 は、素数 2 と 3 で取り消し線を引くことができます。このとき、最小の素数を明確に取り消す必要があるだけです。

#include<iostream>
using namespace std;
const int N = 1e7;
int prime[N + 1];
bool visit[N + 1];
//素数筛
//埃式筛
int E_sieve(int n) {
	int k = 0;
	for (int i = 0; i < n; i++) visit[i] = false;
	// 1.普通
	/* for (int i = 2; i <= n; i++) {
		if (!visit[i]) {
			prime[k++] = i;
			for (int j = i * 2; j <= n; j+=i) { // i的倍数筛选调
				visit[j] = true;
			}
		}
	}*/
	//2.优化 有些数可能会被多筛几遍 这样会影响时间复杂度
	for (int i = 2; i * i <= n; i++)
		if (!visit[i])
			for (int j = i * i; j <= n; j += i) visit[j] = true;
	int k = 0;
	for (int i = 2; i <= n; i++) { // i * i 前面的合数被更小的素数划掉了
		if (!visit[i]) prime[k++] = i;
	}
	return k; // 1~n中的素数
}
bool vis[N + 1];
int euler_sieve(int n) {
	int cnt = 0;
	memset(vis, 0, sizeof(vis));
	memset(prime, 0, sizeof(prime));

	for (int i = 2; i <= n; i++) {
		if (!vis[i]) prime[cnt++] = i;
		for (int j = 0; j < cnt; j++) {
			if (i * prime[j] > n) break; //越界的不需要了
			vis[i * prime[j]] = 1;//prime中全部的素数相乘一定式合数
			if (i % prime[j] == 0) break; //  每个合数只被最小质因子划掉 
		}
	}
	return cnt;
}
int main() {
	return 0;
}

おすすめ

転載: blog.csdn.net/zhi6fui/article/details/128633372