Nhảy chuyển đến

Si pháp

Tố số si pháp

Dẫn vào

Nếu chúng ta muốn biết nhỏ hơn hoặc bằngCó bao nhiêu cái tố số đâu?

Một cái tự nhiên ý tưởng là đối với nhỏ hơn hoặc bằngMỗi cái số tiến hành một lần số nguyên tố kiểm nghiệm. Loại này bạo lực cách làm hiển nhiên không thể đạt tới tối ưu phức tạp độ.

Ai kéo Tost ni si pháp

Quá trình

Suy xét như vậy một việc: Đối với tùy ý một cái lớn hơnChính số nguyên,Như vậy nóLần chính là hợp số (). Lợi dụng cái này kết luận, chúng ta có thể tránh cho rất nhiều lần không cần thiết kiểm tra đo lường.

Nếu chúng ta từ nhỏ đến lớn suy xét mỗi cái số, sau đó đồng thời đem trước mặt cái này số sở hữu ( so với chính mình đại ) bội số nhớ vì hợp số, như vậy vận hành kết thúc thời điểm không có bị đánh dấu số chính là tố đếm.

Thực hiện

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
vectorint>prime;
boolis_prime[N];

voidEratosthenes(intn){
is_prime[0]=is_prime[1]=false;
for(inti=2;in;++i)is_prime[i]=true;
for(inti=2;in;++i){
if(is_prime[i]){
prime.push_back(i);
if((longlong)i*i>n)continue;
for(intj=i*i;jn;j+=i)
// bởi vì từ 2 đến i - 1 bội số chúng ta phía trước si qua, nơi này trực tiếp từ i
// bội số bắt đầu, đề cao vận hành tốc độ
is_prime[j]=false;// là i bội số đều không phải tố số
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
prime=[]
is_prime=[False]*N


defEratosthenes(n):
is_prime[0]=is_prime[1]=False
foriinrange(2,n+1):
is_prime[i]=True
foriinrange(2,n+1):
ifis_prime[i]:
prime.append(i)
ifi*i>n:
continue
forjinrange(i*i,n+1,i):
is_prime[j]=False

Trở lên vìEratosthenes si pháp( ai kéo Tost ni si pháp, tên gọi tắt ai thị si pháp ), thời gian phức tạp độ là.

Chứng minh

Hiện tại chúng ta liền tới nhìn xem suy luận quá trình:

Nếu mỗi một lần đối số tổ thao tác tiêu phí 1 cái đơn vị thời gian, tắc thời gian phức tạp độ vì:

Trong đóTỏ vẻ đệTiểu nhân tố số,Tỏ vẻTố mấy cái số.Tỏ vẻ tầng thứ nhất for tuần hoàn, trong đó mệt hơn nữa giớiif (prime[i])Tiến vào true chi nhánh số lần;Tỏ vẻ tầng thứ hai for tuần hoàn chấp hành số lần.

Căn cứ Mertens đệ nhị định lý, tồn tại hằng sốKhiến cho:

Cho nênEratosthenes si phápThời gian phức tạp độ vì.Kế tiếp chúng ta chứng minh Mertens đệ nhị định lý nhược hóa phiên bản:

Căn cứ,Cũng biết đệCái tố số lớn nhỏ vì.Vì thế liền có

Đương nhiên, mặt trên cách làm hiệu suất vẫn cứ không đủ hiệu suất cao, ứng dụng phía dưới vài loại phương pháp có thể hơi chút đề cao thuật toán chấp hành hiệu suất.

Si đến căn bậc hai

Hiển nhiên, muốn tìm được thẳng đếnMới thôi sở hữu tố số, chỉ đối không vượt quaTố số tiến hành sàng chọn liền đủ rồi.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
vectorint>prime;
boolis_prime[N];

voidEratosthenes(intn){
is_prime[0]=is_prime[1]=false;
for(inti=2;in;++i)is_prime[i]=true;
// i * i
for(inti=2;i*in;++i){
if(is_prime[i])
for(intj=i*i;jn;j+=i)is_prime[j]=false;
}
for(inti=2;in;++i)
if(is_prime[i])prime.push_back(i);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
prime=[]
is_prime=[False]*N


defEratosthenes(n):
is_prime[0]=is_prime[1]=False
foriinrange(2,n+1):
is_prime[i]=True
# làm i tuần hoàn đến
foriinrange(2,isqrt(n)+1):# `isqrt` là Python 3.8 tân tăng hàm số
ifis_prime[i]:
forjinrange(i*i,n+1,i):
is_prime[j]=False
foriinrange(2,n+1):
ifis_prime[i]:
prime.append(i)

Loại này ưu hoá sẽ không ảnh hưởng tiến dần thời gian phức tạp độ, trên thực tế lặp lại trở lên chứng minh, chúng ta đem được đến,Căn cứ đối số tính chất, chúng nó tiến dần tương đồng, nhưng thao tác số lần sẽ rõ hiện giảm bớt.

Chỉ si số lẻ

Bởi vì trừ 2 bên ngoài số chẵn đều là hợp số, cho nên chúng ta có thể trực tiếp nhảy qua chúng nó, chỉ dùng quan tâm số lẻ liền hảo.

Đầu tiên, làm như vậy có thể làm chúng ta nội tồn nhu cầu giảm phân nửa; tiếp theo, sở cần thao tác ước chừng cũng giảm phân nửa.

Giảm bớt nội tồn chiếm dụng

Chúng ta chú ý tới sàng chọn khi chỉ cầnboolLoại hình số tổ.boolSố tổ một cái nguyên tố giống nhau chiếm dụngByte ( tứcSo đặc ), nhưng là tồn trữ một cái bố ngươi giá trị chỉ cầnCái so đặc liền đủ rồi.

Chúng ta có thể sử dụngVị giải toánTương quan tri thức, đem mỗi cái bố ngươi giá trị áp đến một cái so đặc vị trung, như vậy chúng ta chỉ cần sử dụngSo đặc ( tứcByte ) mà phiByte, có thể lộ rõ giảm bớt nội tồn chiếm dụng. Phương thức này được xưng là “Vị cấp áp súc”.

Đáng giá nhắc tới chính là, tồn tại tự động chấp hành vị cấp áp súc số liệu kết cấu, như C++ trungvector&LTbool>Cùngbitset.

Mặt khác,vector&LTbool>CùngbitsetĐối trình tự có hằng số ưu hoá, thời gian phức tạp độAi thị si ở sử dụngbitsetHoặcvector&LTbool>Ưu hoá sau, tính năng thậm chí vượt qua thời gian phức tạp độÂu kéo si.

Tham kiếnbitset: Cùng ai thị si kết hợp.

Phân khối sàng chọn

Từ ưu hoá “Si đến căn bậc hai” cũng biết, không cần vẫn luôn giữ lại toàn bộis_prime[1...n]Số tổ. Vì tiến hành sàng chọn, chỉ giữ lại đếnTố số liền đủ rồi, tứcprime[1...sqrt(n)].Cũng đem toàn bộ phạm vi phân thành khối, mỗi cái khối phân biệt tiến hành sàng chọn. Như vậy, chúng ta liền không cần đồng thời ở bên trong tồn người trung gian lưu nhiều khối, hơn nữa CPU có thể càng tốt mà xử lý hoãn tồn.

ThiếtLà một cái hằng số, nó quyết định khối lớn nhỏ, như vậy chúng ta liền cóCái khối, mà khối() bao hàm khu gianTrung con số. Chúng ta có thể theo thứ tự xử lý khối, nói cách khác, đối với mỗi cái khối,Chúng ta đem biến lịch sở hữu số nguyên tố ( từĐến) cũng sử dụng chúng nó tiến hành sàng chọn.

Đáng chú ý chính là, chúng ta ở xử lý cái thứ nhất con số khi yêu cầu hơi chút sửa chữa một chút sách lược: Đầu tiên, ứng giữ lạiTrung sở hữu số nguyên tố; đệ nhị, con sốCùngHẳn là đánh dấu vì phi tố số. Ở xử lý cuối cùng một cái khối khi, không nên quên cuối cùng một con sốCũng không nhất định ở vào khối cuối cùng.

Dưới thực hiện sử dụng khối sàng chọn tới tính toán nhỏ hơn hoặc bằngSố nguyên tố số lượng.

Thực hiện
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
intcount_primes(intn){
constexprstaticintS=10000;
vectorint>primes;
intnsqrt=sqrt(n);
vectorchar>is_prime(nsqrt+1,true);
for(inti=2;insqrt;i++){
if(is_prime[i]){
primes.push_back(i);
for(intj=i*i;jnsqrt;j+=i)is_prime[j]=false;
}
}
intresult=0;
vectorchar>block(S);
for(intk=0;k*Sn;k++){
fill(block.begin(),block.end(),true);
intstart=k*S;
for(intp:primes){
intstart_idx=(start+p-1)/p;
intj=max(start_idx,p)*p-start;
for(;jS;j+=p)block[j]=false;
}
if(k==0)block[0]=block[1]=false;
for(inti=0;iS&&start+in;i++){
if(block[i])result++;
}
}
returnresult;
}

Phân khối si pháp tiến dần thời gian phức tạp độ cùng ai thị si pháp là giống nhau ( trừ phi khối phi thường tiểu ), nhưng là sở cần nội tồn đem thu nhỏ lại vì,Hơn nữa có càng tốt hoãn tồn kết quả. Về phương diện khác, đối với mỗi một đôi khối cùng khu gianTrung tố số đều phải tiến hành phép chia, mà đối với nhỏ lại khối tới nói, loại tình huống này muốn không xong đến nhiều. Bởi vậy, ở lựa chọn hằng sốKhi muốn bảo trì cân bằng.

Khối lớn nhỏLấyĐếnChi gian, có thể đạt được tốt nhất tốc độ.

Tuyến tính si pháp

Ai thị si pháp vẫn có ưu hoá không gian, nó sẽ đem một cái hợp số lặp lại nhiều lần đánh dấu. Có biện pháp gì không bỏ bớt vô ý nghĩa bước đi đâu? Đáp án là khẳng định.

Nếu có thể làm mỗi cái hợp số đều chỉ bị đánh dấu một lần, như vậy thời gian phức tạp độ liền có thể hàng đến.

Thực hiện
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
vectorint>pri;
boolnot_prime[N];

voidpre(intn){
for(inti=2;in;++i){
if(!not_prime[i]){
pri.push_back(i);
}
for(intpri_j:pri){
if(i*pri_j>n)break;
not_prime[i*pri_j]=true;
if(i%pri_j==0){
// i % pri_j == 0
// nói cách khác, i phía trước bị pri_j si qua
// bởi vì pri bên trong số nguyên tố là từ nhỏ đến lớn, cho nên i thừa thượng mặt khác số nguyên tố kết quả nhất định sẽ bị
// pri_j bội số si rớt, liền không cần ở chỗ này trước si một lần, cho nên nơi này trực tiếp break
// rớt thì tốt rồi
break;
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
pri=[]
not_prime=[False]*N


defpre(n):
foriinrange(2,n+1):
ifnotnot_prime[i]:
pri.append(i)
forpri_jinpri:
ifi*pri_j>n:
break
not_prime[i*pri_j]=True
ifi%pri_j==0:
"""
i % pri_j == 0
Nói cách khác, i phía trước bị pri_j si qua
Bởi vì pri bên trong số nguyên tố là từ nhỏ đến lớn, cho nên i thừa thượng mặt khác số nguyên tố kết quả nhất định sẽ bị
pri_j bội số si rớt, liền không cần ở chỗ này trước si một lần, cho nên nơi này trực tiếp break
Rớt thì tốt rồi
"""
break

Mặt trên loại nàyTuyến tính si phápCũng xưng làEuler si pháp( Âu kéo si pháp ).

Note

Chú ý tới si pháp cầu tố số đồng thời cũng được đến mỗi cái số nhỏ nhất chất ước số.

Si pháp cầu Âu kéo hàm số

Chú ý tới tại tuyến tính si trung, mỗi một cái hợp số đều là bị nhỏ nhất chất ước số si rớt. Tỷ như thiếtNhỏ nhất chất ước số,,Như vậy tuyến tính si trong quá trìnhThông quaSi rớt.

Quan sát tuyến tính si quá trình, chúng ta còn cần xử lý hai cái bộ phận, phía dưới đốiPhân tình huống thảo luận.

Nếu,Như vậyBao hàmSở hữu chất ước số.

Kia nếuĐâu, lúc nàyCùngLà hơn kém nhau, căn cứ Âu kéo hàm số tính chất, chúng ta có:

Thực hiện

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
vectorint>pri;
boolnot_prime[N];
intphi[N];

voidpre(intn){
phi[1]=1;
for(inti=2;in;i++){
if(!not_prime[i]){
pri.push_back(i);
phi[i]=i-1;
}
for(intpri_j:pri){
if(i*pri_j>n)break;
not_prime[i*pri_j]=true;
if(i%pri_j==0){
phi[i*pri_j]=phi[i]*pri_j;
break;
}
phi[i*pri_j]=phi[i]*phi[pri_j];
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pri=[]
not_prime=[False]*N
phi=[0]*N


defpre(n):
phi[1]=1
foriinrange(2,n+1):
ifnotnot_prime[i]:
pri.append(i)
phi[i]=i-1
forpri_jinpri:
ifi*pri_j>n:
break
not_prime[i*pri_j]=True
ifi%pri_j==0:
phi[i*pri_j]=phi[i]*pri_j
break
phi[i*pri_j]=phi[i]*phi[pri_j]

Si pháp cầu Mobius hàm số

Định nghĩa

Căn cứ Mobius hàm số định nghĩa, thiếtLà một cái hợp số,Nhỏ nhất chất ước số,,Có:

NếuLà số nguyên tố, có.

Thực hiện

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
vectorint>pri;
boolnot_prime[N];
intmu[N];

voidpre(intn){
mu[1]=1;
for(inti=2;in;++i){
if(!not_prime[i]){
mu[i]=-1;
pri.push_back(i);
}
for(intpri_j:pri){
if(i*pri_j>n)break;
not_prime[i*pri_j]=true;
if(i%pri_j==0){
mu[i*pri_j]=0;
break;
}
mu[i*pri_j]=-mu[i];
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pri=[]
not_prime=[False]*N
mu=[0]*N


defpre(n):
mu[1]=1
foriinrange(2,n+1):
ifnotnot_prime[i]:
pri.append(i)
mu[i]=-1
forpri_jinpri:
ifi*pri_j>n:
break
not_prime[i*pri_j]=True
ifi%pri_j==0:
mu[i*pri_j]=0
break
mu[i*pri_j]=-mu[i]

Si pháp cầu số ước lượng cái số

DùngTỏ vẻSố ước lượng cái số,Tỏ vẻNhỏ nhất chất ước số xuất hiện số lần.

Số ước lượng cái số định lý

Định lý: NếuTắc.

Chứng minh: Chúng ta biếtSố ước lượng cóCộngCái, căn cứ phép nhân nguyên lý,Số ước lượng cái số chính là.

Thực hiện

Bởi vìLà tích tính hàm số, cho nên có thể sử dụng tuyến tính si.

Ở chỗ này đơn giản giới thiệu một chút tuyến tính si thực hiện nguyên lý.

  1. ĐươngVì số nguyên tố khi,,Đồng thời thiết,Trong đóNhỏ nhất chất ước số.
  2. ĐươngChất ước số khi,.
  3. ĐươngHơn kém nhau khi,.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
vectorint>pri;
boolnot_prime[N];
intd[N],num[N];

voidpre(intn){
d[1]=1;
for(inti=2;in;++i){
if(!not_prime[i]){
pri.push_back(i);
d[i]=2;
num[i]=1;
}
for(intpri_j:pri){
if(i*pri_j>n)break;
not_prime[i*pri_j]=true;
if(i%pri_j==0){
num[i*pri_j]=num[i]+1;
d[i*pri_j]=d[i]/num[i*pri_j]*(num[i*pri_j]+1);
break;
}
num[i*pri_j]=1;
d[i*pri_j]=d[i]*2;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pri=[]
not_prime=[False]*N
d=[0]*N
num=[0]*N


defpre(n):
d[1]=1
foriinrange(2,n+1):
ifnotnot_prime[i]:
pri.append(i)
d[i]=2
num[i]=1
forpri_jinpri:
ifi*pri_j>n:
break
not_prime[i*pri_j]=True
ifi%pri_j==0:
num[i*pri_j]=num[i]+1
d[i*pri_j]=d[i]//num[i*pri_j]*(num[i*pri_j]+1)
break
num[i*pri_j]=1
d[i*pri_j]=d[i]*2

Si pháp cầu số ước lượng cùng

Tỏ vẻSố ước lượng cùng,Tỏ vẻNhỏ nhất chất ước số.

Thực hiện

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
vectorint>pri;
boolnot_prime[N];
intg[N],f[N];

voidpre(intn){
g[1]=f[1]=1;
for(inti=2;in;++i){
if(!not_prime[i]){
pri.push_back(i);
g[i]=i+1;
f[i]=i+1;
}
for(intpri_j:pri){
if(i*pri_j>n)break;
not_prime[i*pri_j]=true;
if(i%pri_j==0){
g[i*pri_j]=g[i]*pri_j+1;
f[i*pri_j]=f[i]/g[i]*g[i*pri_j];
break;
}
f[i*pri_j]=f[i]*f[pri_j];
g[i*pri_j]=1+pri_j;
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
pri=[]
not_prime=[False]*N
f=[0]*N
g=[0]*N


defpre(n):
g[1]=f[1]=1
foriinrange(2,n+1):
ifnotnot_prime[i]:
pri.append(i)
g[i]=i+1
f[i]=i+1
forpri_jinpri:
ifi*pri_j>n:
break
not_prime[i*pri_j]=True
ifi%pri_j==0:
g[i*pri_j]=g[i]*pri_j+1
f[i*pri_j]=f[i]//g[i]*g[i*pri_j]
break
f[i*pri_j]=f[i]*f[pri_j]
g[i*pri_j]=1+pri_j

Giống nhau tích tính hàm số

Nếu một cáiTích tính hàm sốThỏa mãn: Đối với tùy ý số nguyên tốCùng chính số nguyên,Có thể ởThời gian nội tính toán,Như vậy có thể ởThời gian nội si raGiá trị.

Thiết hợp sốChất ước số phân giải là,Trong đóVì số nguyên tố, chúng ta tại tuyến tính si trung ký lục,NếuBịSi rớt (Là số nguyên tố ), như vậyThỏa mãn như sau đệ đẩy thức:

Nếu,Thuyết minhChính là nào đó số nguyên tố thứ mịch, có thểTính toán;Nếu không,.

Bổn tiết bộ phận nội dung dịch tự bác vănРешето ЭратосфенаCùng với tiếng Anh phiên dịch bảnSieve of Eratosthenes.Trong đó tiếng Nga bản bản quyền hiệp nghị vì Public Domain + Leave a Link; tiếng Anh bản bản quyền hiệp nghị vì CC-BY-SA 4.0.