Last active
December 11, 2024 22:45
-
-
Save mml/ea8c4f64cea4552f95c3653c7dec5152 to your computer and use it in GitHub Desktop.
Fast even digits
This file contains hidden or 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 sys | |
import timeit | |
def high_enough(x, lo): | |
return x < 10*lo | |
def even_digits(x): | |
(lo, divisor) = (10, 10) | |
while not high_enough(x, lo): | |
lo *= 100 | |
divisor *= 10 | |
if lo <= x: | |
left = x // divisor | |
right = x - (left * divisor) | |
return (left, right) | |
return False | |
if __name__ == '__main__': | |
def ranges(): | |
return [range(15), | |
range(100, 1_015), | |
range(10_000, 100_015), | |
range(1_000_000, 10_000_015), | |
range(sys.maxsize-10_000_000, sys.maxsize)] | |
def interesting_number(): | |
for r in ranges(): | |
for i in r: | |
yield i | |
checked = False | |
numgen = False | |
def setup(): | |
global checked | |
global numgen | |
checked = 0 | |
numgen = interesting_number() | |
def checker(): | |
global checked | |
checked += 1 | |
return even_digits(next(numgen)) | |
t = timeit.Timer(setup=setup,stmt=checker) | |
n = sum([len(r) for r in ranges()]) | |
secs = t.timeit(n) | |
print(f'Checked {n} numbers in {secs}s ({1e9*secs/n:.0f}ns ave)') | |
# % python even-digits.py | |
# Checked 19090960 numbers in 15.985163406003267s (837ns ave) |
This file contains hidden or 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 sys | |
import timeit | |
import math | |
import itertools | |
# Faster for large integers, but requires a very precise log function. | |
def log_even_digits(x): | |
if x < 1: | |
return False | |
logx = math.log(x, 100) | |
if logx%1 < 0.5: | |
return False | |
divisor = 10 ** math.ceil(logx) | |
left = x // divisor | |
right = x - (left * divisor) | |
return (left, right) | |
# Refined version of original, a bit less multiplication | |
def even_digits(x): | |
(hix, divisor) = (100, 10) | |
while x > hix: | |
hix *= 100 | |
divisor *= 10 | |
lo = hix // 10 | |
if lo <= x: | |
left = x // divisor | |
right = x - (left * divisor) | |
return (left, right) | |
return False | |
# No multiplication, no logarithms, just a lookup table. | |
# (lo, hix, divisor) | |
steps = [(100**x/10, 100**x, 10**x) for x in range(1, math.ceil(math.log(sys.maxsize, 100)))] | |
def fast_even_digits(x): | |
for (lo, hix, divisor) in steps: | |
if x < hix: | |
if x < lo: | |
return False | |
left = x // divisor | |
right = x - (left * divisor) | |
return (left, right) | |
return False | |
if __name__ == '__main__': | |
checked = False | |
numgen = False | |
n = False | |
def setup(start, stop): | |
global checked | |
global numgen | |
global n | |
checked = 0 | |
r = iter(range(start, stop)) | |
lenr = stop-start | |
numgen = r | |
n = lenr | |
while n < 10_000_000: | |
numgen = itertools.chain(iter(range(start, stop)), numgen) | |
n += lenr | |
def checker(f): | |
global checked | |
checked += 1 | |
return f(next(numgen)) | |
for (start,stop) in [(0,1_000_000), (1_000_000, 10_000_000), (sys.maxsize-10_000_000, sys.maxsize)]: | |
print("==========") | |
for f in ('log_even_digits', 'even_digits', 'fast_even_digits'): | |
setup(start, stop) | |
t = timeit.Timer(setup='',stmt=f'checker({f})',globals=globals()) | |
secs = t.timeit(n) | |
print(f'{f}({start}->{stop}): {n} checks in {secs:.3f}s ({1e9*secs/n:.0f}ns ave)') | |
# ========== | |
# log_even_digits(0->1000000): 10000000 checks in 4.476s (448ns ave) | |
# even_digits(0->1000000): 10000000 checks in 3.331s (333ns ave) | |
# fast_even_digits(0->1000000): 10000000 checks in 3.293s (329ns ave) | |
# ========== | |
# log_even_digits(1000000->10000000): 18000000 checks in 5.394s (300ns ave) | |
# even_digits(1000000->10000000): 18000000 checks in 5.307s (295ns ave) | |
# fast_even_digits(1000000->10000000): 18000000 checks in 4.972s (276ns ave) | |
# ========== | |
# log_even_digits(9223372036844775807->9223372036854775807): 10000000 checks in 3.484s (348ns ave) | |
# even_digits(9223372036844775807->9223372036854775807): 10000000 checks in 7.204s (720ns ave) | |
# fast_even_digits(9223372036844775807->9223372036854775807): 10000000 checks in 4.251s (425ns ave) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment