Created
August 28, 2022 23:23
-
-
Save Sonictherocketman/49361941c730b31fe5b822d8bbb1d945 to your computer and use it in GitHub Desktop.
Multiplicative Persistence Calculations in Python
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
#! /usr/bin/env pypy3 | |
# Find a number with a multiplicative persistance larger than 11. | |
from collections import Counter | |
from functools import reduce | |
from multiprocessing import Pool | |
import json | |
import requests | |
import time | |
URL = 'https://api.pushover.net/1/messages.json' | |
API_KEY = 'xxx' | |
USER_KEY = 'xxx' | |
STATE_FILE = 'multiplicative-persistance.json' | |
CHUNK_SIZE = 5000000 | |
POOL_SIZE = 3 | |
MAX_TASKS = 10 | |
multiply_all = lambda x, y: x*y | |
def send_push(title, message): | |
requests.post(URL, data=dict( | |
token=API_KEY, | |
user=USER_KEY, | |
title=title, | |
message=message, | |
)) | |
def check_persist(n, count=0): | |
if n < 10: | |
return count | |
next_n = reduce(multiply_all, (int(i) for i in str(n))) | |
return check_persist(next_n, count+1) | |
def load(): | |
try: | |
with open(STATE_FILE) as f: | |
return json.load(f) | |
except json.decoder.JSONDecodeError: | |
print('Invalid state. Restarting counts.') | |
except FileNotFoundError: | |
print('Statefile not found. Restarting counts.') | |
return {} | |
def save(state): | |
with open(STATE_FILE, 'w+') as f: | |
return json.dump(state, f) | |
def exhaust(values, persistance): | |
victor = None | |
for n in values: | |
n_str = str(n) | |
if '0' in n_str or '1' in n_str: | |
# Don't bother. It has zeros. They're useless. | |
continue | |
if '1' in n_str: | |
# Ones just pad the value. A number has the same persistance | |
# with and without ones. | |
continue | |
if '5' in n_str: | |
# Don't bother. It has zeros later on. | |
# This is first because it's an automatic skip. | |
continue | |
if '2' in n_str and '3' in n_str: | |
# Don't bother. It resolves to a smaller number. | |
continue | |
if '2' in n_str and '4' in n_str: | |
# Don't bother. It resolves to a smaller number. | |
continue | |
sorted_n_str = ''.join(sorted(n_str)) | |
if int(sorted_n_str) < n: | |
# Don't bother. We've already checked it. | |
continue | |
counter = None | |
if '2' in n_str: | |
counter = Counter(n_str) | |
count_2 = counter['2'] | |
if count_2 > 1 and count_2 < 5: | |
# Don't bother. It resolves to a smaller number. | |
# e.g. 2*2 = 4 :: 2234 == 434 | |
continue | |
if '3' in n_str: | |
if counter is None: | |
counter = Counter(n_str) | |
count_3 = counter['3'] | |
if count_3 > 1 and count_3 < 4: | |
# Don't bother. It resolves to a smaller number. | |
# e.g. 3*3 = 9 :: 2334 == 294 | |
continue | |
p = check_persist(n, count=0) | |
if p and p > persistance: | |
victor, persistance = n, p | |
return victor, persistance, n | |
def main(): | |
state = load() | |
n = state.get('n', 0) | |
print(f'Starting from {n}') | |
largest_persistence = state.get('largest_persistence', 0) | |
victor = state.get('victor', 0) | |
with Pool(processes=POOL_SIZE, maxtasksperchild=MAX_TASKS) as pool: | |
while True: | |
t = int(time.time()) | |
print(f'Autosaving, Current n={n}, Victor={victor}, Max P={largest_persistence}, T={t}') | |
save({ | |
**state, | |
'n': n, | |
'victor': victor, | |
'largest_persistence': largest_persistence, | |
}) | |
time.sleep(0.2) | |
chunks = [] | |
start = n | |
end = n + CHUNK_SIZE | |
for i in range(POOL_SIZE): | |
chunks.append(range(start, end)) | |
start += CHUNK_SIZE | |
end += CHUNK_SIZE | |
results = pool.starmap(exhaust, [ | |
(chunk, largest_persistence) | |
for chunk in chunks | |
], chunksize=1) | |
persistance = max([p for _, p, _ in results]) | |
n = max([n for _, _, n in results]) | |
try: | |
n_r = min([n_r for n_r, p, _ in results if p == persistance and n_r]) | |
except ValueError: | |
# Found no new victors | |
n_r = None | |
if persistance and persistance > largest_persistence: | |
print(f'Found New, Current n={n}, Victor={n_r}, Max P={persistance}, T={t}') | |
victor = n_r | |
largest_persistence = persistance | |
save({ | |
**state, | |
'n': n_r, | |
'victor': victor, | |
'largest_persistence': largest_persistence, | |
}) | |
# Don't alert about small ones. | |
if persistance > 8: | |
send_push( | |
'Found new Persistence!', | |
f'The largest n so far: {n_r}. ' | |
f'Persistance: {largest_persistence}' | |
) | |
n = max([n for _, _, n in results]) | |
if __name__ == '__main__': | |
main() |
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
#! /usr/bin/env python3 | |
# Find a number with a multiplicative persistance larger than 11. | |
from collections import Counter | |
from functools import reduce | |
import json | |
import requests | |
import time | |
URL = 'https://api.pushover.net/1/messages.json' | |
API_KEY = 'xxxx' | |
USER_KEY = 'xxx' | |
STATE_FILE = 'multiplicative-persistance.json' | |
PAUSE_AT = 5000000 | |
multiply_all = lambda x, y: x*y | |
def send_push(title, message): | |
requests.post(URL, data=dict( | |
token=API_KEY, | |
user=USER_KEY, | |
title=title, | |
message=message, | |
)) | |
def check_persist(n, count=0): | |
if n < 10: | |
return count | |
next_n = reduce(multiply_all, (int(i) for i in str(n))) | |
return check_persist(next_n, count+1) | |
def load(): | |
try: | |
with open(STATE_FILE) as f: | |
return json.load(f) | |
except json.decoder.JSONDecodeError: | |
print('Invalid state. Restarting counts.') | |
except FileNotFoundError: | |
print('Statefile not found. Restarting counts.') | |
return {} | |
def save(state): | |
with open(STATE_FILE, 'w+') as f: | |
return json.dump(state, f) | |
def main(): | |
state = load() | |
n = state.get('n', 0) | |
print(f'Starting from {n}') | |
largest_persistence = state.get('largest_persistence', 0) | |
victor = state.get('victor', 0) | |
i = 0 | |
while True: | |
i += 1 | |
if i % PAUSE_AT == 0: | |
print(f'Autosaving, Current n={n}, Victor={victor}, Max P={largest_persistence}') | |
save({ | |
**state, | |
'n': n, | |
'victor': victor, | |
'largest_persistence': largest_persistence, | |
}) | |
time.sleep(0.1) | |
n_str = str(n) | |
if '0' in n_str: | |
# We don't need to bother. Just replace it and move on. | |
n_str = n_str.replace('0', '1') | |
n = int(n_str) | |
if '1' in n_str: | |
# Ones just pad the value. A number has the same persistance | |
# with and without ones. | |
n_str = n_str.replace('1', '2') | |
n = int(n_str) | |
sorted_n_str = ''.join(sorted(n_str)) | |
if int(sorted_n_str) < n: | |
# Don't bother. We've already checked it. | |
n += 1 | |
continue | |
counter = Counter(n_str) | |
if counter['2'] > 1 and counter['2'] < 5: | |
# Don't bother. It resolves to a smaller number. | |
# e.g. 2*2 = 4 :: 2234 == 434 | |
n += 1 | |
continue | |
if counter['3'] > 1 and counter['3'] < 4: | |
# Don't bother. It resolves to a smaller number. | |
# e.g. 3*3 = 9 :: 2334 == 294 | |
n += 1 | |
continue | |
persistance = check_persist(n, count=0) | |
if persistance and persistance > largest_persistence: | |
print(f'Found New, Current n={n}, Victor={n}, Max P={persistance}') | |
victor = n | |
largest_persistence = persistance | |
save({ | |
**state, | |
'n': n, | |
'victor': victor, | |
'largest_persistence': largest_persistence, | |
}) | |
# Don't alert about small ones. | |
if persistance > 8: | |
send_push( | |
'Found new Persistence!', | |
f'The largest n so far: {n}. ' | |
f'Persistance: {largest_persistence}' | |
) | |
n += 1 | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment