-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFactorSieve.h
143 lines (118 loc) · 4.69 KB
/
FactorSieve.h
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
#pragma once
#include <algorithm>
#include <cstddef>
#include <concepts>
#include <cstdint>
#include <iostream>
#include <vector>
#include "Exponent.h"
#include "PrimePower.h"
// A Eratosthenes-type sieve for computing the least prime factor for a range of positive integers.
template<std::unsigned_integral T>
class FactorSieve
{
private:
// The underlying lookup table.
std::vector<T> sieve;
// The exclusive upper bound on the lookup table.
T limit;
public:
// Constructs a FactorSieve over [0, 'limit') and optionally outputs progress to 'clog'.
FactorSieve (T limit, bool verbose = false)
: limit (limit)
{
// Fill the sieve with the integers in [0, 'limit').
for (T i = 0; i < limit; ++i)
sieve.emplace_back (i);
T prime = 2;
if (verbose)
while (prime * prime < limit)
{
std::clog << "Marking multiples of " << prime << "\n";
// For each 'multiple' of 'prime', if a smaller prime factor of 'multiple' has not been discovered,
// mark 'prime' as the smallest prime factor of 'multiple'.
for (T multiple = prime * prime; multiple < limit; multiple += prime)
sieve[multiple] = std::min (sieve[multiple], prime);
// Find the next prime to continue the sieve operation.
for (T i = prime + 1; ; ++i)
if (sieve[i] == i)
{
prime = i;
break;
}
}
else
// For each 'multiple' of 'prime', if a smaller prime factor of 'multiple' has not been discovered,
// mark 'prime' as the smallest prime factor of 'multiple'.
while (prime * prime < limit)
{
for (T multiple = prime * prime; multiple < limit; multiple += prime)
sieve[multiple] = std::min (sieve[multiple], prime);
// Find the next prime to continue the sieve operation.
for (T i = prime + 1; ; ++i)
if (sieve[i] == i)
{
prime = i;
break;
}
}
}
// Returns the least prime factor of 'n', if 'n' is in [0, 'limit').
// Out of range arguments result in undefined behaviour.
T LeastPrimeFactor (T n) const
{
return sieve[n];
}
// Returns the prime factorization of 'n', if 'n' is in [0, 'limit').
// Out of range arguments result in undefined behaviour.
std::vector<PrimePower<T, std::uint32_t>> PrimeFactors (T n) const
{
std::vector<PrimePower<T, std::uint32_t>> primeFactors;
// Repeatedly divide 'n' by the smallest prime dividing 'n',
// then look up the new smallest prime dividing the result.
T prime = sieve[n];
primeFactors.emplace_back (prime, 1);
n /= prime;
while (n != 1)
{
prime = sieve[n];
// Check if the prime has already occurred (divides the original 'n' with multiplicity).
if (prime == primeFactors[primeFactors.size () - 1].prime)
++(primeFactors[primeFactors.size () - 1].power);
else
primeFactors.emplace_back (prime, 1);
n /= prime;
}
return primeFactors;
}
// Returns the factors of 'n' in increasing order, if 'n' is in [0, 'limit').
// Out of range arguments result in undefined behaviour.
std::vector<T> Factors (T n) const
{
std::vector<T> factors;
auto primeFactors = PrimeFactors (n);
std::size_t addressSize = primeFactors.size ();
std::vector<std::size_t> address (addressSize, 0);
std::size_t numberFactors = 1;
// Standard product form of divisor counting function.
for (std::size_t i = 0; i < addressSize; ++i)
numberFactors *= (primeFactors[i].power + 1);
// Iterate through all possible exponent tuples on the primes dividing 'n'.
for (std::size_t i = 0; i < numberFactors; ++i)
{
T factor = 1;
for (std::size_t j = 0; j < addressSize; ++j)
factor *= IntegerPow (std::uint64_t (primeFactors[j].prime), address[j]);
factors.emplace_back (factor);
// Move to the next exponent tuple in lex order.
for (std::size_t j = 0; j < addressSize; ++j)
{
address[j] = (address[j] + 1) % (primeFactors[j].power + 1);
if (address[j] > 0)
break;
}
}
std::sort (factors.begin (), factors.end ());
return factors;
}
};