Skip to content

Commit 531e95e

Browse files
Solve PE 808: Reversible Prime Squares
1 parent b47b172 commit 531e95e

File tree

2 files changed

+176
-7
lines changed

2 files changed

+176
-7
lines changed

README.md

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@
99

1010
## Solutions
1111

12-
| number | challenge | links |
13-
| --- | --- | --- |
14-
| 88 | Product Sum Numbers | ([src](project_euler/test_pe88_product_sum_numbers.py)) ([web](https://projecteuler.net/problem=88)) |
15-
| 700 | Eulercoin | ([src](project_euler/test_pe700_eulercoin.py)) ([web](https://projecteuler.net/problem=700)) |
16-
| 800 | Hybrid Integers | ([src](project_euler/test_pe800_hybrid_integers.py)) ([web](https://projecteuler.net/problem=800)) |
17-
| 816 | Shortest Distance Among Points | ([src](project_euler/test_pe816_shortest_distance_among_points.py)) ([web](https://projecteuler.net/problem=816)) |
18-
| 932 | 2025 | ([src](project_euler/test_pe932_2025.py)) ([web](https://projecteuler.net/problem=932)) |
12+
|number|challenge|links|
13+
|-|-|-|
14+
|88|Product Sum Numbers|([src](project_euler/test_pe88_product_sum_numbers.py)) ([web](https://projecteuler.net/problem=88))|
15+
|700|Eulercoin|([src](project_euler/test_pe700_eulercoin.py)) ([web](https://projecteuler.net/problem=700))|
16+
|800|Hybrid Integers|([src](project_euler/test_pe800_hybrid_integers.py)) ([web](https://projecteuler.net/problem=800))|
17+
|808|Reversible Prime Squares|([src](project_euler/test_pe808_reversible_prime_squares.py)) ([web](https://projecteuler.net/problem=808))|
18+
|816|Shortest Distance Among Points|([src](project_euler/test_pe816_shortest_distance_among_points.py)) ([web](https://projecteuler.net/problem=816))|
19+
|932|2025|([src](project_euler/test_pe932_2025.py)) ([web](https://projecteuler.net/problem=932))|
1920

2021
## How To
2122

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
# <https://projecteuler.net/problem=808>
2+
# <p>
3+
# Both $169$ and $961$ are the square of a prime. $169$ is the reverse of $961$.
4+
# </p>
5+
# <p>
6+
# We call a number a <dfn>reversible prime square</dfn> if:</p>
7+
# <ol>
8+
# <li>It is not a palindrome, and</li>
9+
# <li>It is the square of a prime, and</li>
10+
# <li>Its reverse is also the square of a prime.</li>
11+
# </ol>
12+
# <p>
13+
# $169$ and $961$ are not palindromes, so both are reversible prime squares.
14+
# </p>
15+
# <p>
16+
# Find the sum of the first $50$ reversible prime squares.
17+
# </p>
18+
19+
import pytest
20+
21+
# Source - https://stackoverflow.com/a/3035188
22+
# Posted by Robert William Hanks, modified by community. See post 'Timeline' for change history
23+
# Retrieved 2025-11-29, License - CC BY-SA 4.0
24+
import numpy
25+
def primes_from_2_to(n):
26+
""" Input n>=6, Returns a array of primes, 2 <= p < n """
27+
sieve = numpy.ones(n//3 + (n%6==2), dtype=bool)
28+
for i in range(1,int(n**0.5)//3+1):
29+
if sieve[i]:
30+
k=3*i+1|1
31+
sieve[ k*k//3 ::2*k] = False
32+
sieve[k*(k-2*(i&1)+4)//3::2*k] = False
33+
return numpy.r_[2,3,((3*numpy.nonzero(sieve)[0][1:]+1)|1)]
34+
35+
def test_gets_primes():
36+
primes = primes_from_2_to(10)
37+
assert 1 not in primes
38+
assert 2 in primes
39+
assert 3 in primes
40+
assert 4 not in primes
41+
assert 5 in primes
42+
assert 6 not in primes
43+
assert 7 in primes
44+
assert 8 not in primes
45+
assert 9 not in primes
46+
assert 10 not in primes
47+
48+
def test_respects_the_limit():
49+
assert 11 not in primes_from_2_to(10)
50+
51+
# Source - https://stackoverflow.com/a/24953539
52+
# Posted by Alberto, modified by community. See post 'Timeline' for change history
53+
# Retrieved 2025-11-29, License - CC BY-SA 3.0
54+
def reverse_number(n):
55+
r = 0
56+
while n > 0:
57+
r *= 10
58+
r += n % 10
59+
n //= 10
60+
return r
61+
62+
def test_reverses_numbers():
63+
assert 0 == reverse_number(0)
64+
assert 1 == reverse_number(1)
65+
assert 12 == reverse_number(21)
66+
assert 123 == reverse_number(321)
67+
68+
69+
from random import randrange
70+
71+
# Source - https://rosettacode.org/wiki/Miller%E2%80%93Rabin_primality_test#Python
72+
# Will give correct answers for n less than 341550071728321 and then reverting to the probabilistic form
73+
def is_probably_prime(n, _precision_for_huge_n=16):
74+
if n in _known_primes:
75+
return True
76+
if any((n % p) == 0 for p in _known_primes) or n in (0, 1):
77+
return False
78+
d, s = n - 1, 0
79+
while not d % 2:
80+
d, s = d >> 1, s + 1
81+
# Returns exact according to http://primes.utm.edu/prove/prove2_3.html
82+
if n < 1373653:
83+
return not any(_try_composite(a, d, n, s) for a in (2, 3))
84+
if n < 25326001:
85+
return not any(_try_composite(a, d, n, s) for a in (2, 3, 5))
86+
if n < 118670087467:
87+
if n == 3215031751:
88+
return False
89+
return not any(_try_composite(a, d, n, s) for a in (2, 3, 5, 7))
90+
if n < 2152302898747:
91+
return not any(_try_composite(a, d, n, s) for a in (2, 3, 5, 7, 11))
92+
if n < 3474749660383:
93+
return not any(_try_composite(a, d, n, s) for a in (2, 3, 5, 7, 11, 13))
94+
if n < 341550071728321:
95+
return not any(_try_composite(a, d, n, s) for a in (2, 3, 5, 7, 11, 13, 17))
96+
# otherwise
97+
return not any(_try_composite(a, d, n, s)
98+
for a in _known_primes[:_precision_for_huge_n])
99+
100+
def _try_composite(a, d, n, s):
101+
if pow(a, d, n) == 1:
102+
return False
103+
for i in range(s):
104+
if pow(a, 2**i * d, n) == n-1:
105+
return False
106+
return True # n is definitely composite
107+
108+
_known_primes = [2, 3]
109+
_known_primes += [x for x in range(5, 1000, 2) if is_probably_prime(x)]
110+
111+
def test_finds_primes():
112+
assert is_probably_prime(2)
113+
assert is_probably_prime(23)
114+
assert is_probably_prime(239)
115+
assert is_probably_prime(2399)
116+
117+
def test_finds_composites():
118+
assert not is_probably_prime(1)
119+
assert not is_probably_prime(12)
120+
assert not is_probably_prime(124)
121+
assert not is_probably_prime(1248)
122+
123+
def is_integer(n):
124+
return n % 1 == 0
125+
126+
def test_finds_integers():
127+
assert is_integer(0)
128+
assert is_integer(1)
129+
assert is_integer(2)
130+
assert is_integer(3)
131+
132+
def test_finds_non_integers():
133+
assert not is_integer(0.1)
134+
assert not is_integer(1.01)
135+
assert not is_integer(11.001)
136+
assert not is_integer(111.00000000001)
137+
138+
from math import isqrt, sqrt
139+
140+
def reversible_prime_squares_less_than(n):
141+
reversible_prime_squares = []
142+
max = isqrt(n) + 1
143+
for p in primes_from_2_to(max):
144+
square = p * p
145+
reverse = reverse_number(square)
146+
if square == reverse:
147+
continue
148+
root = sqrt(reverse)
149+
if is_integer(root) and is_probably_prime(int(root)):
150+
reversible_prime_squares.append(square)
151+
return reversible_prime_squares
152+
153+
def test_finds_reversible_prime_squares():
154+
squares = reversible_prime_squares_less_than(1000)
155+
assert 169 in squares
156+
assert 961 in squares
157+
158+
def test_does_not_include_palindromes():
159+
assert 121 not in reversible_prime_squares_less_than(130)
160+
161+
@pytest.mark.skip(reason="lots of seconds to solve")
162+
def test_sum_first_50_reversible_prime_squares():
163+
# assert 2 == len(reversible_prime_squares_less_than(1000))
164+
# assert 4 == len(reversible_prime_squares_less_than(10**6))
165+
# assert 12 == len(reversible_prime_squares_less_than(10**9))
166+
# assert 32 == len(reversible_prime_squares_less_than(10**14))
167+
# assert 50 == len(reversible_prime_squares_less_than(10**16))
168+
assert 3807504276997394 == sum(reversible_prime_squares_less_than(10**16))

0 commit comments

Comments
 (0)