import math
from decimal import Decimal
from itertools import islice
def blhsing(numbers):
return Decimal(math.gcd(*numbers)).normalize().as_tuple().exponent
def batched(iterable, n):
"Batch data into tuples of length n. The last batch may be shorter."
# batched('ABCDEFG', 3) --> ABC DEF G
if n < 1:
raise ValueError('n must be at least one')
it = iter(iterable)
while batch := tuple(islice(it, n)):
yield batch
def Kelly(numbers):
E = float('inf')
for e in map(blhsing, batched(numbers, 100)):
if e < E:
E = e
if not E:
return 0
return E
funcs = blhsing, Kelly
from random import randrange
from timeit import timeit, default_timer as timer
from statistics import mean, fmean, stdev
import sys
d = [randrange(100) * 10 ** randrange(100) for i in range(10000)]
times = {f: [] for f in funcs}
def stats(f):
ts = [t * 1e3 for t in sorted(times[f])[:5]]
return f'{mean(ts):6.3f} ± {stdev(ts):4.3f} ms '
for _ in range(50):
for f in funcs:
t0 = timer()
f(d)
times[f].append(timer() - t0)
for f in sorted(funcs, key=stats):
print(stats(f), f.__name__)
print('\nPython:', sys.version)
aW1wb3J0IG1hdGgKZnJvbSBkZWNpbWFsIGltcG9ydCBEZWNpbWFsCmZyb20gaXRlcnRvb2xzIGltcG9ydCBpc2xpY2UKCmRlZiBibGhzaW5nKG51bWJlcnMpOgogICAgcmV0dXJuIERlY2ltYWwobWF0aC5nY2QoKm51bWJlcnMpKS5ub3JtYWxpemUoKS5hc190dXBsZSgpLmV4cG9uZW50CgpkZWYgYmF0Y2hlZChpdGVyYWJsZSwgbik6CiAgICAiQmF0Y2ggZGF0YSBpbnRvIHR1cGxlcyBvZiBsZW5ndGggbi4gVGhlIGxhc3QgYmF0Y2ggbWF5IGJlIHNob3J0ZXIuIgogICAgIyBiYXRjaGVkKCdBQkNERUZHJywgMykgLS0+IEFCQyBERUYgRwogICAgaWYgbiA8IDE6CiAgICAgICAgcmFpc2UgVmFsdWVFcnJvcignbiBtdXN0IGJlIGF0IGxlYXN0IG9uZScpCiAgICBpdCA9IGl0ZXIoaXRlcmFibGUpCiAgICB3aGlsZSBiYXRjaCA6PSB0dXBsZShpc2xpY2UoaXQsIG4pKToKICAgICAgICB5aWVsZCBiYXRjaAoKZGVmIEtlbGx5KG51bWJlcnMpOgogICAgRSA9IGZsb2F0KCdpbmYnKQogICAgZm9yIGUgaW4gbWFwKGJsaHNpbmcsIGJhdGNoZWQobnVtYmVycywgMTAwKSk6CiAgICAgICAgaWYgZSA8IEU6CiAgICAgICAgICAgIEUgPSBlCiAgICAgICAgICAgIGlmIG5vdCBFOgogICAgICAgICAgICAgICAgcmV0dXJuIDAKICAgIHJldHVybiBFCgpmdW5jcyA9IGJsaHNpbmcsIEtlbGx5Cgpmcm9tIHJhbmRvbSBpbXBvcnQgcmFuZHJhbmdlCmZyb20gdGltZWl0IGltcG9ydCB0aW1laXQsIGRlZmF1bHRfdGltZXIgYXMgdGltZXIKZnJvbSBzdGF0aXN0aWNzIGltcG9ydCBtZWFuLCBmbWVhbiwgc3RkZXYKaW1wb3J0IHN5cwoKZCA9IFtyYW5kcmFuZ2UoMTAwKSAqIDEwICoqIHJhbmRyYW5nZSgxMDApIGZvciBpIGluIHJhbmdlKDEwMDAwKV0KCnRpbWVzID0ge2Y6IFtdIGZvciBmIGluIGZ1bmNzfQpkZWYgc3RhdHMoZik6CiAgICB0cyA9IFt0ICogMWUzIGZvciB0IGluIHNvcnRlZCh0aW1lc1tmXSlbOjVdXQogICAgcmV0dXJuIGYne21lYW4odHMpOjYuM2Z9IMKxIHtzdGRldih0cyk6NC4zZn0gbXMgJwpmb3IgXyBpbiByYW5nZSg1MCk6CiAgICBmb3IgZiBpbiBmdW5jczoKICAgICAgICB0MCA9IHRpbWVyKCkKICAgICAgICBmKGQpCiAgICAgICAgdGltZXNbZl0uYXBwZW5kKHRpbWVyKCkgLSB0MCkKZm9yIGYgaW4gc29ydGVkKGZ1bmNzLCBrZXk9c3RhdHMpOgogICAgcHJpbnQoc3RhdHMoZiksIGYuX19uYW1lX18pCgpwcmludCgnXG5QeXRob246Jywgc3lzLnZlcnNpb24p