-
-
Notifications
You must be signed in to change notification settings - Fork 50.3k
Expand file tree
/
Copy pathsol1.py
More file actions
158 lines (126 loc) · 3.47 KB
/
sol1.py
File metadata and controls
158 lines (126 loc) · 3.47 KB
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"""
Project Euler Problem 810: https://projecteuler.net/problem=810
We use x ⊕ y for the bitwise XOR of x and y.
Define the XOR-product of x and y, denoted by x ⊗ y,
similar to a long multiplication in base 2,
except the intermediate results are XORed instead of usual integer addition.
For example, 7 ⊗ 3 = 9, or in base 2, 111_2 ⊗ 11_2 = 1001_2:
111
⊗ 11
-------
111
111
-------
1001
An XOR-Prime is an integer n greater than 1 that is not an
XOR-product of two integers greater than 1.
The above example shows that 9 is not an XOR-prime.
Similarly, 5 = 3 ⊗ 3 is not an XOR-prime.
The first few XOR-primes are 2, 3, 7, 11, 13, ... and the 10th XOR-prime is 41.
Find the 5,000,000.th XOR-prime.
References:
http://en.wikipedia.org/wiki/M%C3%B6bius_function
"""
def xor_multiply(a: int, b: int) -> int:
"""
Perform XOR multiplication of two integers, equivalent to polynomial
multiplication in GF(2).
>>> xor_multiply(3, 5) # (011) ⊗ (101)
15
"""
res = 0
while b:
if b & 1:
res ^= a
a <<= 1
b >>= 1
return res
def divisors(n: int) -> set[int]:
"""
Return all divisors of n (excluding 0).
>>> divisors(12)
{1, 2, 3, 4, 6, 12}
"""
s = {1}
for i in range(2, int(n**0.5) + 1):
if n % i == 0:
s.add(i)
s.add(n // i)
s.add(n)
return s
def mobius_table(n: int) -> list[int]:
"""
Generate Möbius function values from 1 to n.
>>> mobius_table(10)[:6]
[0, 1, -1, -1, 0, -1]
"""
mob = [1] * (n + 1)
is_prime = [True] * (n + 1)
mob[0] = 0
for p in range(2, n + 1):
if is_prime[p]:
mob[p] = -1
for j in range(2 * p, n + 1, p):
is_prime[j] = False
mob[j] *= -1
p2 = p * p
if p2 <= n:
for j in range(p2, n + 1, p2):
mob[j] = 0
return mob
def count_irreducibles(d: int) -> int:
"""
Count the number of irreducible polynomials of degree d over GF(2)
using the Möbius function.
>>> count_irreducibles(3)
2
"""
mob = mobius_table(d)
total = 0
for div in divisors(d) | {d}:
total += mob[div] * (1 << (d // div))
return total // d
def find_xor_prime(rank: int) -> int:
"""
Find the Nth XOR prime using a bitarray-based sieve.
>>> find_xor_prime(10)
41
"""
total, degree = 0, 1
while True:
count = count_irreducibles(degree)
if total + count > rank:
break
total += count
degree += 1
limit = 1 << (degree + 1)
sieve = [True] * limit
sieve[0] = sieve[1] = False
for even in range(4, limit, 2):
sieve[even] = False
current = 1
for i in range(3, limit, 2):
if sieve[i]:
current += 1
if current == rank:
return i
j = i
while True:
prod = xor_multiply(i, j)
if prod >= limit:
break
sieve[prod] = False
j += 2
raise ValueError(
"Failed to locate the requested XOR-prime within the computed limit"
)
def solution(limit: int = 5000001) -> int:
"""
Wrapper for Project Euler-style solution function.
>>> solution(10)
41
"""
result = find_xor_prime(limit)
return result
if __name__ == "__main__":
print(f"{solution(5000000) = }")