from itertools import combinations
def primal_numbers(n):
a = [True for i in range(n + 1)]
a[0] = False
a[1] = False
out = []
for i in range(2, n + 1):
if not a[i]:
continue
out.append(i)
for j in range(i, n + 1, i):
a[j] = False
return out
def factorize_number(n, primlist, primset):
out = []
while n not in primset:
for p in primlist:
if n % p == 0:
out.append(p)
n //= p
break
else:
break
if not out:
return None
if n != 1:
out.append(n)
return out
def product(*a):
r = 1
for x in a:
r *= x
return r
def step_count(step, k):
shift = step % k
a, r = 0, 0
while True:
r += 1
a += shift
if a >= k:
a -= k
if a == 0:
return r
def check_result_set(value, mset):
for i in mset:
if value % i != 0:
return False
return True
def find_divisible_by_set(mset, simple=False, debug=False):
plist = primal_numbers(max(mset))
pset = set(plist)
step_mults = mset & pset
step = product(*list(step_mults))
if debug:
print('STEP MULT', sorted(step_mults))
print('STEP', step)
ax = set()
for j in mset:
f = factorize_number(j, plist, pset)
if f is None:
continue
ax.update(f)
for k in range(2, len(f)):
for comb in combinations(f, k):
ax.add(product(*comb))
if debug:
print('ALL POSSIBLE FACTORIZATION MULTIPLIERS', list(reversed(sorted(list(ax)))))
ax = mset - ax
if debug:
print('INVERTED', list(reversed(sorted(list(ax)))))
ax = ax - step_mults
if debug:
print('REMOVED PRIMALS (THEY ARE ALL INCLUDED IN STEP)', list(reversed(sorted(list(ax)))))
discards = []
for k in ax:
if step % k == 0:
discards.append(k)
for k in discards:
ax.discard(k)
if debug:
print('FILTERED BY STEP MOD', list(reversed(sorted(list(ax)))))
max_result = product( *list(ax | step_mults) )
assert check_result_set(max_result, mset)
if debug:
print('WORST CASE RESULT', max_result)
print('ITER COUNT', max_result // step)
ax = list(reversed(sorted(list(ax))))
if simple:
i = step
while True:
for k in ax:
if i % k != 0:
break
else:
break
i += step
if debug:
print('RESULT', i)
return i
else:
sset = set()
for k in ax:
# print(k, step % k, step_count(step, k))
sset.add(step_count(step, k))
if debug:
print('STEPS SET', sorted(list(sset)))
print('RECURSING IN...')
fx = find_divisible_by_set(sset, simple=True)
if debug:
print('RECURSING OUT...')
return step * fx
# from sys import argv
# N = int(argv[1])
N = 300
Nset = set(range(2, N + 1))
x = find_divisible_by_set(Nset)
assert check_result_set(x, Nset)
print(x)
ZnJvbSBpdGVydG9vbHMgaW1wb3J0IGNvbWJpbmF0aW9ucwoKCmRlZiBwcmltYWxfbnVtYmVycyhuKToKICAgIGEgPSBbVHJ1ZSBmb3IgaSBpbiByYW5nZShuICsgMSldCiAgICBhWzBdID0gRmFsc2UKICAgIGFbMV0gPSBGYWxzZQogICAgb3V0ID0gW10KICAgIGZvciBpIGluIHJhbmdlKDIsIG4gKyAxKToKICAgICAgICBpZiBub3QgYVtpXToKICAgICAgICAgICAgY29udGludWUKICAgICAgICBvdXQuYXBwZW5kKGkpCiAgICAgICAgZm9yIGogaW4gcmFuZ2UoaSwgbiArIDEsIGkpOgogICAgICAgICAgICBhW2pdID0gRmFsc2UKICAgIHJldHVybiBvdXQKCgpkZWYgZmFjdG9yaXplX251bWJlcihuLCBwcmltbGlzdCwgcHJpbXNldCk6CiAgICBvdXQgPSBbXQogICAgd2hpbGUgbiBub3QgaW4gcHJpbXNldDoKICAgICAgICBmb3IgcCBpbiBwcmltbGlzdDoKICAgICAgICAgICAgaWYgbiAlIHAgPT0gMDoKICAgICAgICAgICAgICAgIG91dC5hcHBlbmQocCkKICAgICAgICAgICAgICAgIG4gLy89IHAKICAgICAgICAgICAgICAgIGJyZWFrCiAgICAgICAgZWxzZToKICAgICAgICAgICAgYnJlYWsKICAgIGlmIG5vdCBvdXQ6CiAgICAgICAgcmV0dXJuIE5vbmUKICAgIGlmIG4gIT0gMToKICAgICAgICBvdXQuYXBwZW5kKG4pCiAgICByZXR1cm4gb3V0CgoKZGVmIHByb2R1Y3QoKmEpOgogICAgciA9IDEKICAgIGZvciB4IGluIGE6CiAgICAgICAgciAqPSB4CiAgICByZXR1cm4gcgoKCmRlZiBzdGVwX2NvdW50KHN0ZXAsIGspOgogICAgc2hpZnQgPSBzdGVwICUgawogICAgYSwgciA9IDAsIDAKICAgIHdoaWxlIFRydWU6CiAgICAgICAgciArPSAxCiAgICAgICAgYSArPSBzaGlmdAogICAgICAgIGlmIGEgPj0gazoKICAgICAgICAgICAgYSAtPSBrCiAgICAgICAgaWYgYSA9PSAwOgogICAgICAgICAgICByZXR1cm4gcgoKCmRlZiBjaGVja19yZXN1bHRfc2V0KHZhbHVlLCBtc2V0KToKICAgIGZvciBpIGluIG1zZXQ6CiAgICAgICAgaWYgdmFsdWUgJSBpICE9IDA6CiAgICAgICAgICAgIHJldHVybiBGYWxzZQogICAgcmV0dXJuIFRydWUKCgpkZWYgZmluZF9kaXZpc2libGVfYnlfc2V0KG1zZXQsIHNpbXBsZT1GYWxzZSwgZGVidWc9RmFsc2UpOgogICAgcGxpc3QgPSBwcmltYWxfbnVtYmVycyhtYXgobXNldCkpCiAgICBwc2V0ID0gc2V0KHBsaXN0KQoKICAgIHN0ZXBfbXVsdHMgPSBtc2V0ICYgcHNldAogICAgc3RlcCA9IHByb2R1Y3QoKmxpc3Qoc3RlcF9tdWx0cykpCiAgICBpZiBkZWJ1ZzoKICAgICAgICBwcmludCgnU1RFUCBNVUxUJywgc29ydGVkKHN0ZXBfbXVsdHMpKQogICAgICAgIHByaW50KCdTVEVQJywgc3RlcCkKCiAgICBheCA9IHNldCgpCiAgICBmb3IgaiBpbiBtc2V0OgogICAgICAgIGYgPSBmYWN0b3JpemVfbnVtYmVyKGosIHBsaXN0LCBwc2V0KQogICAgICAgIGlmIGYgaXMgTm9uZToKICAgICAgICAgICAgY29udGludWUKICAgICAgICBheC51cGRhdGUoZikKICAgICAgICBmb3IgayBpbiByYW5nZSgyLCBsZW4oZikpOgogICAgICAgICAgICBmb3IgY29tYiBpbiBjb21iaW5hdGlvbnMoZiwgayk6CiAgICAgICAgICAgICAgICBheC5hZGQocHJvZHVjdCgqY29tYikpCiAgICBpZiBkZWJ1ZzoKICAgICAgICBwcmludCgnQUxMIFBPU1NJQkxFIEZBQ1RPUklaQVRJT04gTVVMVElQTElFUlMnLCBsaXN0KHJldmVyc2VkKHNvcnRlZChsaXN0KGF4KSkpKSkKICAgIGF4ID0gbXNldCAtIGF4CiAgICBpZiBkZWJ1ZzoKICAgICAgICBwcmludCgnSU5WRVJURUQnLCBsaXN0KHJldmVyc2VkKHNvcnRlZChsaXN0KGF4KSkpKSkKICAgIGF4ID0gYXggLSBzdGVwX211bHRzCiAgICBpZiBkZWJ1ZzoKICAgICAgICBwcmludCgnUkVNT1ZFRCBQUklNQUxTIChUSEVZIEFSRSBBTEwgSU5DTFVERUQgSU4gU1RFUCknLCBsaXN0KHJldmVyc2VkKHNvcnRlZChsaXN0KGF4KSkpKSkKCiAgICBkaXNjYXJkcyA9IFtdCiAgICBmb3IgayBpbiBheDoKICAgICAgICBpZiBzdGVwICUgayA9PSAwOgogICAgICAgICAgICBkaXNjYXJkcy5hcHBlbmQoaykKICAgIGZvciBrIGluIGRpc2NhcmRzOgogICAgICAgIGF4LmRpc2NhcmQoaykKICAgIGlmIGRlYnVnOgogICAgICAgIHByaW50KCdGSUxURVJFRCBCWSBTVEVQIE1PRCcsIGxpc3QocmV2ZXJzZWQoc29ydGVkKGxpc3QoYXgpKSkpKQoKICAgIG1heF9yZXN1bHQgPSBwcm9kdWN0KCAqbGlzdChheCB8IHN0ZXBfbXVsdHMpICkKICAgIGFzc2VydCBjaGVja19yZXN1bHRfc2V0KG1heF9yZXN1bHQsIG1zZXQpCiAgICBpZiBkZWJ1ZzoKICAgICAgICBwcmludCgnV09SU1QgQ0FTRSBSRVNVTFQnLCBtYXhfcmVzdWx0KQogICAgICAgIHByaW50KCdJVEVSIENPVU5UJywgbWF4X3Jlc3VsdCAvLyBzdGVwKQoKICAgIGF4ID0gbGlzdChyZXZlcnNlZChzb3J0ZWQobGlzdChheCkpKSkKCiAgICBpZiBzaW1wbGU6CiAgICAgICAgaSA9IHN0ZXAKICAgICAgICB3aGlsZSBUcnVlOgogICAgICAgICAgICBmb3IgayBpbiBheDoKICAgICAgICAgICAgICAgIGlmIGkgJSBrICE9IDA6CiAgICAgICAgICAgICAgICAgICAgYnJlYWsKICAgICAgICAgICAgZWxzZToKICAgICAgICAgICAgICAgIGJyZWFrCiAgICAgICAgICAgIGkgKz0gc3RlcAogICAgICAgIGlmIGRlYnVnOgogICAgICAgICAgICBwcmludCgnUkVTVUxUJywgaSkKICAgICAgICByZXR1cm4gaQogICAgZWxzZToKICAgICAgICBzc2V0ID0gc2V0KCkKICAgICAgICBmb3IgayBpbiBheDoKICAgICAgICAgICAgIyBwcmludChrLCBzdGVwICUgaywgc3RlcF9jb3VudChzdGVwLCBrKSkKICAgICAgICAgICAgc3NldC5hZGQoc3RlcF9jb3VudChzdGVwLCBrKSkKICAgICAgICBpZiBkZWJ1ZzoKICAgICAgICAgICAgcHJpbnQoJ1NURVBTIFNFVCcsIHNvcnRlZChsaXN0KHNzZXQpKSkKICAgICAgICAgICAgcHJpbnQoJ1JFQ1VSU0lORyBJTi4uLicpCiAgICAgICAgZnggPSBmaW5kX2RpdmlzaWJsZV9ieV9zZXQoc3NldCwgc2ltcGxlPVRydWUpCiAgICAgICAgaWYgZGVidWc6CiAgICAgICAgICAgIHByaW50KCdSRUNVUlNJTkcgT1VULi4uJykKICAgICAgICByZXR1cm4gc3RlcCAqIGZ4CgoKIyBmcm9tIHN5cyBpbXBvcnQgYXJndgojIE4gPSBpbnQoYXJndlsxXSkKCk4gPSAzMDAKCk5zZXQgPSBzZXQocmFuZ2UoMiwgTiArIDEpKQp4ID0gZmluZF9kaXZpc2libGVfYnlfc2V0KE5zZXQpCmFzc2VydCBjaGVja19yZXN1bHRfc2V0KHgsIE5zZXQpCnByaW50KHgp