1 / 6
Oct 2012

Currently I'm struglling with problem EASYMATH2.

I figured out that it a possible approach is

  • iterate through all non-empty subsets of {a, a+d,...,a+4d} (powerset)
  • calculate the least common multiple (lcm) of the elements of each subset
  • add or subtract the number of multiples of this lcm within the range n..m

And I think that my python 3 code is doing exactly this without too much overhead, but still keep getting TLE. Obviously, some of the subsets return in the same lcm, so they are calculated unnecessarily, but I thought I could compensate this by memoization. And the respective (redundant) operations are quite compact.

Where is my code too slow? Or do I have to optimize the algo to avoid redundant subsets? But how?

[bbone=python3,502]""" SPOJ (classical).11391. EASY MATH. Problem code: EASYMATH"""
import sys
import itertools as it

def gcd(a, b):
"""Calculate the Greatest Common Divisor of a and b.

Unless b==0, the result will have the same sign as b (so that when
b is divided by it, the result comes out positive).
"""
while b:
    a, b = b, a%b
return a

def memoize(fn):
stored_results = {}

def memoized(*args):
    if args in stored_results:
        # try to get the cached result
        return stored_results[args]
    else:
        # nothing was cached for those args. let's fix that.
        result = stored_results[args] = fn(*args)
        return result
return memoized

def count_n_in_range( start, end, div):
if start > end:
return 0
res = (end - start) // div
if (start % div == 0) or (start % div > end % div):
return res + 1
else:
return res
count_n_in_range = memoize(count_n_in_range)

def lcml(numbers):
if len(numbers) == 1:
return sum(numbers)
elif len(numbers) == 2:
return numbers [0] * numbers[1] // gcd(*numbers)
else:
return lcml((lcml(numbers[:-1]), numbers[-1]))
lcml = memoize(lcml)

def powerset(iterable):
"powerset([1,2,3]) --> () (1,) (2,) (3,) (1,2) (1,3) (2,3) (1,2,3)"
s = list(iterable)
return it.chain.from_iterable(it.combinations(s, r)
for r in range(1, len(s)+1))

def calc_numbers(data):
n, m, a, d = map(int, data)
res = m - n + 1
if a > m:
return res
divis = filter(lambda x:x<=m, range(a, a+5*d, d))
for ps in powerset(divis):
remove = lcml(ps)
if remove < m:
count = count_n_in_range(n, m, remove)
if len(ps) % 2:
res -= count
else:
res += count
return str(res)

def main():
cases = sys.stdin.read().split('\n')
output = sys.stdout.write
cases = [ c for c in cases if c]
results = []
for line in cases[1:]:
data = line.split()
res = calc_numbers(data)
results.append(res)
sys.stdout.write('\n'.join(results))
sys.stdout.write('\n')

if name == 'main':
main()
[/bbone]

Any Hints?
Thanks Daft

  • created

    Oct '12
  • last reply

    Oct '12
  • 5

    replies

  • 1.1k

    views

  • 2

    users

  • 1

    link

A totally other way:
from problem n°1 of project Euler, you can have a "formula" for two numbers,
try to find some for 3 numbers, and then for 4 numbers, ...
try to generate your formula for several numbers in input.
I used string method to build my_formula_Str and
exec( my_formula_Str ) did the job.
My shortest code is 223 bytes of python3 ; many tricks inside.

--For my C code, I had to torture the compiler, but I'm quite sure there are many ways to do this problem.

Hope this could help.
Good luck.
F

I'm not sure if your approach is so different (beside of your implementation of a big "formula", of course).

I understand, your formula for the number of euler1 would be something like 1000 - num_5 - num_3 + num_15 with num_x being the number of multiples of number x in the given range. And 15 being lcm of 5 and 3.

For problems with 3 different divisiors this will extend "combinatoric", e.g. for 3,5,7: 1000 - num 7 - num5 - num3 + num_35 + num_21 + num_15 - num_105

and so on... of course keeping in mind you have to use the least common multiple for the combinations.

If i got this right, it would be the same kind of thing as my code, just very different implemented. If not, I misunderstood your approach wink

EDIT: Formula corrected

It's ok (I didn't read all of your code, ...),

for your example there is a 15 instead a 21

I don't know how to help you more.

thanks again. It is good to see that in principle my approach is sensible smile

Just one more question regarding your formula: lets assume the numbers would be 4,6,8,10,12. Then, a lot of lcm of combinations would be identical. lcm(4,12) = lcm(6,12) = lcm(4,6,12) = lcm(4,6). Does yor formula take care of this special situations and avoids the recalculation? If so, what criteria does it use?

Cheers
Daft

I compute several times the same lcm, it's true, but I've insert a trick to avoid to compute useless ones.
I don't want to spoil, but search what kind of lcm could be useless, and then simply try to not compute them.