Анхны тоо
Анхы тоо гэдэг нь яг 2 ширхэг ялгаатай хуваагчтай натурал тоог хэлдэг. Жишээ нь 2, 3, 5, 7 гэх мэт үргэлжлээд явна.
N гэсэн тоо өгөгдөхөд тухайн тоог анхны тоо эсэхийг хэрхэн мэдэх вэ? Мэдээж бид [1, N]
завсарт гүйгээд N тоог маань хэд нь хувааж байгаа вэ гэдгийг тоолоход болно.
Үүнээс хурднаар шалгаж чадах уу?
Анхны тоо зөвхөн 2 хуваагчтай гэхээр 1 болон өөртөө л хуваагддаг гэсэн үг ба N тоог [2, N-1]
завсарт ямар ч тоо хуваадаггүй бол анхны тоо мөн.
Бүр түүнчлэн N
тоо нь 2-с \( \sqrt N \) хүртэл бүх тоонд хуваагддаггүй гэвэл анхны тоо мөн байна.
Учир нь \( \sqrt N \)-с их ямар нэг хуваагч байдаг гээд X
гэвэл N/X
нь \(\sqrt N \)-с бага байна. Гэхвдээ бид тухайн завсарт шалгасан тул боломжгүй.
Үүнээс бид тухайн тоог анхны тоо эсэхийг \( \sqrt N \)-р шалгаж чадахнээ.
Ахны тоонуудыг байгуулах
1-с N хүртэлх бүх анхны тоог байгуулцгаая. Үүнийг ойролцоогоор \( N*\sqrt (N) \) үйлдлээр хийж чадна. Гэхдээ мэдээж үүнээс хурднаар хэрхэн хийх вэ?
Бидэнд тухайн тоо анхны тоо эсэхийг тэмдэглэх Prime
гэсэн жагсаалт байгаа гэж үзье. Тэгвэл Prime[1] = false, Prime[2] true
байна.
2-г хамгийн эхний анхны тоо гэдгийг мэднэ. Тэгвэл 2-д хуваагддаг N хүртэлх бүх тоонууд анхны тоо болж чадахгүй тиймээс энэхүү тоонуудыг анхны тоо биш гэж тэмдэглэе.
Дараагийн тоо нь 3 ба энэ нь өмнөх бүх анхны тоондоо хуваагдаагүй тул энэ тоо нь анхны тоо. Тэгвэл 3-д хуваагддаг дараагийн бүх тоонууд анхны тоо биш.
Дараагийн тоо нь 4 ба энэ нь 2-д хуваагддаг тул анхны тоо биш гэж тэмдэглэгдсэн. Дараагийн тоо нь 5 ба энэ нь анхы тоо биш гэж тэмдэглэгдээгүй ба өмнөх бүх анхны тоондоо хуваагдаагүй тул анхны тоо.
Тэгвэл 5-д хуваагддаг дараачийн тоонууд нь анхны тоо биш болох юм.
Энэ мэтчилэн бодвол хугацааны үнэлгээ нь $$ \begin{aligned} \sum\nolimits_{p\leq N} N/p \approx n \ln \ln n \end{aligned} $$
байдаг бөгөөд үүнийг Sieve of Eratosthenes гэж нэрлэдэг. Доор кодыг оруулав
#include <bits/stdc++.h>
using namespace std;
int n;
bool Prime[2000000]; // Prime[i] нь i гэсэн тоо анхны тоо бол 0 үгүй бол 1
int main() {
cin >> n;
Prime[1] = 1;
for(int i = 2; i*i <= n; i++) {
if(Prime[i]) continue; // i тоо нь анхны тоо биш тул алгасна.
// i-д хуваагддаг дараачийн бүх тоонууд нь анхны тоо биш тул тэмдэглэнэ.
for(int j = i+i; j <= n; j += i) {
Prime[j] = 1;
}
}
for(int i = 1; i <= n; i++) {
if(!Prime[i]) { // анхны тоо
cout << i << " ";
}
}
cout << endl;
return 0;
}