Last active
December 2, 2021 23:53
-
-
Save mikmart/12d4132e717961846f48c4e76ec6d955 to your computer and use it in GitHub Desktop.
Frequent strong liars among witness numbers (with top 100 results up to 115,471)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
witness | lies | |
---|---|---|
4096 | 246 | |
256 | 171 | |
1296 | 140 | |
729 | 115 | |
16 | 114 | |
6561 | 108 | |
1024 | 98 | |
625 | 96 | |
64 | 90 | |
15625 | 88 | |
81 | 85 | |
1568 | 81 | |
20736 | 79 | |
10000 | 78 | |
11449 | 77 | |
900 | 76 | |
4913 | 76 | |
529 | 75 | |
2116 | 75 | |
6106 | 75 | |
7991 | 75 | |
2304 | 74 | |
2401 | 74 | |
4624 | 73 | |
1451 | 72 | |
6068 | 72 | |
1156 | 71 | |
3446 | 71 | |
7225 | 71 | |
1369 | 70 | |
1444 | 69 | |
1728 | 69 | |
2187 | 69 | |
676 | 68 | |
1207 | 68 | |
1600 | 68 | |
3481 | 68 | |
5808 | 68 | |
7151 | 68 | |
300 | 67 | |
1363 | 67 | |
4924 | 67 | |
14884 | 67 | |
512 | 66 | |
675 | 66 | |
5043 | 66 | |
7396 | 66 | |
8836 | 66 | |
32 | 65 | |
324 | 65 | |
400 | 65 | |
1936 | 65 | |
9299 | 65 | |
11093 | 65 | |
14641 | 65 | |
1177 | 64 | |
4225 | 64 | |
28561 | 64 | |
125 | 63 | |
361 | 63 | |
2888 | 63 | |
3194 | 63 | |
3974 | 63 | |
3449 | 63 | |
5476 | 63 | |
7132 | 63 | |
14258 | 63 | |
19988 | 63 | |
144 | 62 | |
254 | 62 | |
563 | 62 | |
1101 | 62 | |
3364 | 62 | |
4232 | 62 | |
5776 | 62 | |
1322 | 61 | |
1303 | 61 | |
2382 | 61 | |
2601 | 61 | |
3532 | 61 | |
3025 | 61 | |
3721 | 61 | |
12996 | 61 | |
318 | 60 | |
857 | 60 | |
1432 | 60 | |
3844 | 60 | |
4754 | 60 | |
7569 | 60 | |
6859 | 60 | |
8765 | 60 | |
10201 | 60 | |
18769 | 60 | |
32768 | 60 | |
36 | 59 | |
243 | 59 | |
972 | 59 | |
1918 | 59 | |
3656 | 59 | |
4024 | 59 | |
4562 | 59 | |
5660 | 59 | |
7056 | 59 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import csv | |
import logging | |
import time | |
from argparse import ArgumentParser | |
from typing import List, Optional, Tuple | |
from collections import Counter | |
class Suspect: | |
""" An integer accused of being prime. """ | |
__slots__ = ["n", "s", "d"] | |
def __init__(self, n: int): | |
s, d = prepare_suspect(n) | |
self.n = n | |
self.s = s | |
self.d = d | |
def prepare_suspect(n: int) -> Tuple[int, int]: | |
""" Find integers s and d such that n = 2^s * d + 1 and d is odd. """ | |
s = 1 | |
d, r = divmod(n, 2) | |
if not r: | |
raise ValueError("`n` must be odd.") | |
while True: | |
q, r = divmod(d, 2) | |
if r: | |
break | |
else: | |
s += 1 | |
d = q | |
return s, d | |
def is_prime(s: Suspect, witnesses: Optional[List[int]] = None) -> bool: | |
""" Check if a suspect is prime based on a set of witnesses. """ | |
if not witnesses: | |
witnesses = call_star_witnesses(s.n) | |
return all(witness_statement(w, s) == True for w in witnesses) | |
def witness_statement(witness: int, suspect: Suspect) -> bool: | |
""" Do any Miller-Rabin congruence relations hold? """ | |
a = witness | |
n = suspect.n | |
s = suspect.s | |
d = suspect.d | |
x = pow(a, d, n) | |
if x in {1, n - 1}: | |
return True | |
while (s := s - 1): | |
x = pow(x, 2, n) | |
if x == (n - 1): | |
return True | |
return False | |
MAX_SUSPECT = 1_122_004_669_633 | |
def call_star_witnesses(n: int): | |
""" | |
Find sets of witnesses whose agreement is sufficient to declare primality. | |
Presented by Matt Parker on Numberphile: https://youtu.be/_MscGSN5J6o | |
""" | |
if n < 1_373_653: | |
return [2, 3] | |
elif n < 25_326_001: | |
return [2, 3, 5] | |
elif n < MAX_SUSPECT: | |
return [2, 13, 23, 1_662_803] | |
else: | |
raise ValueError(f"Don't know star witnesses for x >= {MAX_SUSPECT}.") | |
if __name__ == "__main__": | |
parser = ArgumentParser() | |
parser.add_argument("n", type=int, nargs="?", default=100) | |
parser.add_argument("--top", type=int, default=5) | |
parser.add_argument("--verbose", action="store_true", default=False) | |
parser.add_argument("--output", type=str, default="") | |
args = parser.parse_args() | |
logging.basicConfig(level=logging.DEBUG if args.verbose else logging.INFO) | |
start = lap_start = time.perf_counter() | |
lies: Counter[int] = Counter() | |
try: | |
# Test all "suspects" up to given `n` | |
for n in range(5, args.n, 2): | |
s = Suspect(n) | |
# Get true verdict from star witnesses | |
star_witnesses = call_star_witnesses(n) | |
truth = is_prime(s, witnesses=star_witnesses) | |
if truth: | |
logging.debug(f"{n} is prime") | |
else: | |
logging.debug(f"{n} is not prime") | |
# Get statements from all potential witnesses and count lies | |
potential_witnesses = range(2, n) | |
for w in potential_witnesses: | |
if witness_statement(w, s) != truth: | |
lies[w] += 1 | |
# Output some progress info every now and again | |
if (n + 1) % 1000 == 0: | |
lap_stop = time.perf_counter() | |
logging.info(f"Done {n + 1}/{args.n} ... ({lap_stop - lap_start:.2f}s)") | |
lap_start = lap_stop | |
except KeyboardInterrupt: | |
pass # Finish reporting statistics before exiting | |
stop = time.perf_counter() | |
logging.info(f"Done ({stop - start:.2f}s total)") | |
logging.info(f"The top {args.top} liars up to {n} are:") | |
logging.info(lies.most_common(args.top)) | |
if args.output: | |
with open(args.output, "w", newline="") as f: | |
w = csv.DictWriter(f, fieldnames=["witness", "lies"]) | |
w.writeheader() | |
for witness, lies in lies.most_common(): | |
w.writerow({ "witness" : witness, "lies" : lies }) | |
logging.info(f"Full results written to '{args.output}'.") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment