Skip to content

Commit 9245396

Browse files
authored
Replaced sieve with seg sieve (#166)
1 parent 61bc640 commit 9245396

File tree

6 files changed

+83
-51
lines changed

6 files changed

+83
-51
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
*
2+
!*/
3+
!*.*
14
/build/
25
a.out
36
header.tmp
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* Author: Jakob Kogler, chilli, pajenegod
3+
* Date: 2020-04-12
4+
* License: CC0
5+
* Description: Prime sieve for generating all primes smaller than LIM.
6+
* Status: Stress-tested
7+
* Time: LIM=1e9 $\approx$ 1.5s
8+
* Details: Despite its n log log n complexity, segmented sieve is still faster
9+
* than other options, including bitset sieves and linear sieves. This is
10+
* primarily due to its low memory usage, which reduces cache misses. This
11+
* implementation skips even numbers.
12+
*
13+
* Benchmark can be found here: https://ideone.com/e7TbX4
14+
*
15+
* The line `for (int i=idx; i<S+L; idx = (i += p))` is done on purpose for performance reasons.
16+
* Se https://github.com/kth-competitive-programming/kactl/pull/166#discussion_r408354338
17+
*/
18+
#pragma once
19+
20+
const int LIM = 1e6;
21+
bitset<LIM> isPrime;
22+
vi eratosthenes() {
23+
const int S = round(sqrt(LIM)), R = LIM / 2;
24+
vi pr = {2}, sieve(S+1); pr.reserve(int(LIM/log(LIM)*1.1));
25+
vector<pii> cp;
26+
for (int i = 3; i <= S; i += 2) if (!sieve[i]) {
27+
cp.push_back({i, i * i / 2});
28+
for (int j = i * i; j <= S; j += 2 * i) sieve[j] = 1;
29+
}
30+
for (int L = 1; L <= R; L += S) {
31+
array<bool, S> block{};
32+
for (auto &[p, idx] : cp)
33+
for (int i=idx; i < S+L; idx = (i+=p)) block[i-L] = 1;
34+
rep(i,0,min(S, R - L))
35+
if (!block[i]) pr.push_back((L + i) * 2 + 1);
36+
}
37+
for (int i : pr) isPrime[i] = 1;
38+
return pr;
39+
}

content/number-theory/chapter.tex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ \section{Modular arithmetic}
1010
\kactlimport{ModSqrt.h}
1111

1212
\section{Primality}
13-
\kactlimport{eratosthenes.h}
13+
\kactlimport{FastEratosthenes.h}
1414
\kactlimport{MillerRabin.h}
1515
\kactlimport{Factor.h}
1616

Lines changed: 30 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,38 @@
11
#include "../utilities/template.h"
22

3-
struct prime_sieve {
4-
typedef unsigned char uchar;
5-
typedef unsigned int uint;
6-
static const int pregen = 3*5*7*11*13;
7-
uint n, sqrtn;
8-
uchar *isprime;
9-
int *prime, primes; // prime[i] is i:th prime
10-
11-
bool is_prime(int n) { // primality check
12-
if(n%2==0 || n<=2) return n==2;
13-
return isprime[(n-3)>>4] & 1 << ((n-3) >> 1&7);
3+
namespace dynamic {
4+
vi eratosthenes(int LIM) {
5+
const int S = round(sqrt(LIM)), R = LIM / 2;
6+
vi pr({2}), sieve(S + 1); pr.reserve(LIM / (int)log(LIM));
7+
vector<array<int, 2>> cp;
8+
for (int i = 3; i <= S; i += 2) if (!sieve[i]) {
9+
cp.push_back({i, i * i / 2});
10+
for (int j = i * i; j <= S; j += 2 * i) sieve[j] = 1;
1411
}
15-
16-
prime_sieve(int _n) : n(_n), sqrtn((int)ceil(sqrt(1.0*n))) {
17-
int n0 = max(n>>4, (uint)pregen) + 1;
18-
prime = new int[max(2775, (int)(1.12*n/log(1.0*n)))];
19-
prime[0]=2; prime[1]=3; prime[2]=5;
20-
prime[3]=7; prime[4]=11; prime[5]=13;
21-
primes=6;
22-
isprime = new uchar[n0];
23-
memset(isprime, 255, n0);
24-
25-
for(int j=1,p=prime[j];j<6;p=prime[++j])
26-
for(int i=(p*p-3)>>4,s=(p*p-3)>>1&7;
27-
i<=pregen; i+= (s+=p)>>3, s&=7)
28-
isprime[i] &= (uchar)~(1<<s);
29-
for(int d=pregen, b=pregen+1; b<n0; b+=d,d<<=1)
30-
memcpy(isprime+b,isprime+1,(n0<b+d)?n0-b:d);
31-
for(uint p=17,i=0,s=7; p<n; p+=2, i+= ++s>>3, s&=7)
32-
if(isprime[i]&1<<s) {
33-
prime[primes++] = p;
34-
if(p<sqrtn) {
35-
int ii=i, ss=s+(p-1)*p/2;
36-
for(uint pp=p*p; pp<n; pp+=p<<1, ss+=p) {
37-
ii += ss>>3;
38-
ss &=7;
39-
isprime[ii] &= (uchar)~(1<<ss);
40-
} } } } };
12+
for (int L = 1; L <= R; L += S) {
13+
vector<bool> block(S);
14+
// array<bool, S> block{};
15+
for (auto &[p, idx] : cp)
16+
for (int i=idx; i < S+L; idx = (i+=p)) block[i-L] = 1;
17+
rep(i,0,min(S, R - L))
18+
if (!block[i]) pr.push_back((L + i) * 2 + 1);
19+
}
20+
return pr;
21+
}
22+
}
23+
#include "../../content/number-theory/FastEratosthenes.h"
24+
#include "../../content/number-theory/Eratosthenes.h"
4125

4226

43-
#include "../../content/number-theory/eratosthenes.h"
27+
int main() {
28+
vi pr1 = eratosthenesSieve(LIM);
29+
vi pr2 = eratosthenes();
30+
assert(pr1 == pr2);
4431

45-
int main(int argc, char** argv) {
46-
ll s = 0, s2 = 0;
47-
prime_sieve ps(MAX_PR);
48-
rep(i,0,ps.primes) s += ps.prime[i];
49-
vi r = eratosthenesSieve(MAX_PR);
50-
for(auto &x: r) s2 += x;
51-
assert(s==s2);
32+
for (int lim=121; lim<1000; lim++) {
33+
vi pr = eratosthenesSieve(lim);
34+
vi r = dynamic::eratosthenes(lim);
35+
assert(pr == r);
36+
}
5237
cout<<"Tests passed!"<<endl;
5338
}

stress-tests/number-theory/MillerRabin.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
#include "../utilities/template.h"
22

33
#include "../../content/number-theory/MillerRabin.h"
4-
#include "../../content/number-theory/eratosthenes.h"
4+
namespace sieve {
5+
#include "../../content/number-theory/FastEratosthenes.h"
6+
}
57

68
ull A[] = {2, 325, 9375, 28178, 450775, 9780504, 1795265022};
79
int afactors[] = {2, 3, 5, 13, 19, 73, 193, 407521, 299210837};
810

9-
const ull LIM = 3ULL << 61;
11+
const ull MR_LIM = 1ULL << 62;
1012

1113
// Accurate for arbitrary 64-bit numbers
1214
ull int128_mod_mul(ull a, ull b, ull m) { return (ull)((__uint128_t)a * b % m); }
@@ -47,11 +49,14 @@ void rec(ull div, ll num, int ind, int factors) {
4749
}
4850
}
4951

52+
const int MAXPR = 1e6;
5053
int main() {
51-
eratosthenesSieve(MAX_PR);
54+
auto prs = sieve::eratosthenes();
55+
vector<bool> isprime(MAXPR);
56+
for (auto i: prs) isprime[i] = true;
5257
for(auto &a: A) rec(1, a, 0, 0);
5358

54-
rep(n,0,MAX_PR) {
59+
rep(n,0,MAXPR) {
5560
if (isPrime(n) != isprime[n]) {
5661
cout << "fails for " << n << endl;
5762
return 1;
@@ -62,7 +67,7 @@ int main() {
6267
rep(i,0,1000000) {
6368
n ^= (ull)rand();
6469
n *= 1237618231ULL;
65-
if (n < LIM && oldIsPrime(n) != isPrime(n)) {
70+
if (n < MR_LIM && oldIsPrime(n) != isPrime(n)) {
6671
cout << "differs from old for " << n << endl;
6772
cout << "old says " << oldIsPrime(n) << endl;
6873
cout << "new says " << isPrime(n) << endl;

0 commit comments

Comments
 (0)