Advertisement
tzot

Library for Project Euler problems

Feb 22nd, 2012
206
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.19 KB | None | 0 0
  1. # coding: utf8
  2.  
  3. from __future__ import unicode_literals
  4.  
  5. import sys, errno
  6. import signal, resource
  7. import operator as op, itertools as it, collections, functools as ft
  8. import fractions, bisect
  9.  
  10. # runtime control functions
  11.  
  12. def cpu_used():
  13.     "Ask the system for CPU statistics"
  14.     data= resource.getrusage(resource.RUSAGE_SELF)
  15.     return data.ru_utime + data.ru_stime
  16.  
  17. def possibly_report_locals(stack_frame):
  18.     "Attempt to display status at the time of interruption"
  19.     # only needed once here, so import locally
  20.     import inspect, gc, types
  21.     import ast
  22.  
  23.     while stack_frame:
  24.         code= stack_frame.f_code
  25.         for func in gc.get_referrers(code):
  26.             if isinstance(func, types.FunctionType):
  27.                 reports= getattr(func, "__report_on_fail__", None)
  28.                 if not reports: continue
  29.                 sys.stderr.write("%s:\n" % func.__name__)
  30.                 for local_var in reports:
  31.                     try:
  32.                         body= ast.parse(local_var, mode='eval').body
  33.                     except SyntaxError:
  34.                         continue
  35.                     else:
  36.                         if isinstance(body, ast.Name):
  37.                             value= stack_frame.f_locals.get(local_var, Ellipsis)
  38.                         else:
  39.                             try:
  40.                                 value= eval(local_var, stack_frame.f_locals)
  41.                             except Exception as exc:
  42.                                 value= `exc`
  43.                     sys.stderr.write(" %s=%r\n" % (local_var, value))
  44.         stack_frame= stack_frame.f_back
  45.  
  46. def on_fail_report(*args):
  47.     "A decorator for easy reporting of locals on fail"
  48.     def _setter(func):
  49.         func.__report_on_fail__= args
  50.         return func
  51.     return _setter
  52.  
  53. def signal_xcpu_handler(sig_number, stack_frame):
  54.     "Handle gracefully time limit"
  55.     sys.stderr.write("Time limit reached, exiting.\n")
  56.     possibly_report_locals(stack_frame)
  57.     raise SystemExit
  58.  
  59. def signal_usr1_handler(sig_number, stack_frame):
  60.     "Report progress"
  61.     print "cpu used so far:", cpu_used()
  62.     possibly_report_locals(stack_frame)
  63.  
  64. def only_one_minute(seconds=60):
  65.     "Make sure that the program runs at most for a minute"
  66.     signal.signal(signal.SIGXCPU, signal_xcpu_handler)
  67.     signal.signal(signal.SIGUSR1, signal_usr1_handler) # report progress
  68.     signal.signal(signal.SIGQUIT, signal_usr1_handler) # report progress
  69.     # limit CPU time
  70.     soft_limit, hard_limit= resource.getrlimit(resource.RLIMIT_CPU)
  71.     soft_limit= seconds + cpu_used() + 1
  72.     sys.stderr.write("Setting max cpu time: %g seconds\n" % soft_limit)
  73.     resource.setrlimit(resource.RLIMIT_CPU, (seconds+cpu_used()+1, hard_limit))
  74.     # limit data to 80% of machine RAM if possible
  75.     try:
  76.         with open("/proc/meminfo", "r") as fp:
  77.             first_line= fp.readline() # assume it's MemTotal:        3014864 kB
  78.     except IOError as exc:
  79.         if exc.errno == errno.ENOENT: # non-existent
  80.             pass
  81.         else: raise
  82.     else:
  83.         kb_total= first_line.partition(':')[2].strip().partition(' ')
  84.         if kb_total[2] == 'kB':
  85.             memory_bytes= 1024*int(kb_total[0], 10)
  86.         soft_limit, hard_limit= resource.getrlimit(resource.RLIMIT_DATA)
  87.         soft_limit= memory_bytes//10*8
  88.         soft_limit= 4096 * (soft_limit+4095)//4096
  89.         sys.stderr.write("Setting max memory use: %g MiB\n" % (soft_limit/1048576.0))
  90.         resource.setrlimit(resource.RLIMIT_DATA, (soft_limit, soft_limit))
  91.         resource.setrlimit(resource.RLIMIT_RSS, (soft_limit, soft_limit))
  92.         try:
  93.             resource.setrlimit(resource.RLIMIT_VMEM, (soft_limit, soft_limit))
  94.         except AttributeError: # no RLIMIT_VMEM here
  95.             pass
  96.  
  97. # utility functions
  98.  
  99. @on_fail_report("n")
  100. def fibonaccis():
  101.     "Generate Fibonacci sequence terms; NB: 1, 2, 3, 5, …"
  102.     last_n= 1
  103.     n= 1
  104.     while 1:
  105.         yield n
  106.         n, last_n= n+last_n, n
  107.  
  108. @on_fail_report("n")
  109. def factorial(n):
  110.     "Return the factorial of a number"
  111.     return reduce(op.mul, xrange(1, 1+n), 1)
  112.  
  113. @on_fail_report("n")
  114. def triangulars():
  115.     "Generate all triangular numbers"
  116.     n= 0
  117.     i= 1
  118.     while 1:
  119.         n+= i
  120.         yield n
  121.         i+= 1
  122.  
  123. def triangular(n):
  124.     "Return triangular number n ∈ [1…∞)"
  125.     return n*(n + 1)//2
  126.  
  127. @on_fail_report("n", "dn")
  128. def hexagonals():
  129.     "Generate all hexagonal numbers"
  130.     n= 1
  131.     dn= 5
  132.     while 1:
  133.         yield n
  134.         n+= dn
  135.         dn+= 4
  136.  
  137. def hexagonal(n):
  138.     "Return hexagonal number n ∈ [1…∞)"
  139.     return n*(2*n-1)
  140.  
  141. @on_fail_report("n", "dn")
  142. def pentagonals():
  143.     "Generate all pentagonal numbers"
  144.     n= 1
  145.     dn= 4
  146.     while 1:
  147.         yield n
  148.         n+= dn
  149.         dn+= 3
  150.  
  151. def pentagonal(n):
  152.     "Return pentagonal number n ∈ [1…∞)"
  153.     return n*(3*n - 1)//2
  154.  
  155. @on_fail_report("best_n")
  156. def min_n_satisfying(limit, op, function):
  157.     "Return minimum n satisfying op(limit, function(n))"
  158.  
  159.     n= 1
  160.     value= function(n)
  161.     while not op(limit, value):
  162.         n<<= 1
  163.         value= function(n)
  164.  
  165.     best_n= n
  166.     bit= n>>1
  167.     n-= 1
  168.  
  169.     while bit:
  170.         if op(limit, function(n - bit)):
  171.             n-= bit
  172.             best_n= n
  173.         bit>>= 1
  174.     return best_n
  175.  
  176. @on_fail_report("q")
  177. def primegen():
  178.     "Generate all primes as fast as possible"
  179.     D = { 9: 3, 25: 5 }
  180.     yield 2
  181.     yield 3
  182.     yield 5
  183.     MASK= 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0,
  184.     MODULOS= frozenset( (1, 7, 11, 13, 17, 19, 23, 29) )
  185.  
  186.     for q in it.compress(
  187.             it.islice(it.count(7), 0, None, 2),
  188.             it.cycle(MASK)):
  189.         p= D.pop(q, None)
  190.         if p is None:
  191.             D[q*q]= q
  192.             yield q
  193.         else:
  194.             x= q + 2*p
  195.             while x in D or (x%30) not in MODULOS:
  196.                 x+= 2*p
  197.             D[x]= p
  198.  
  199. class Factorizer(object):
  200.     "A class to be used for multiple factorizing related questions."
  201.  
  202.     def __init__(self):
  203.         self.primes= collections.deque()
  204.         self._primegen= primegen()
  205.         self.primes.append(next(self._primegen))
  206.         self.primes.append(next(self._primegen))
  207.  
  208.     @on_fail_report("prime")
  209.     def _enlarge_primes(self):
  210.         "Generate more primes while appending them to self.primes for later reference"
  211.         while 1:
  212.             prime= next(self._primegen)
  213.             self.primes.append(prime)
  214.             ## sys.stderr.write("%d\r" % prime)
  215.             yield prime
  216.  
  217.     @on_fail_report("n")
  218.     def is_prime(self, n):
  219.         while self.primes[-1] < n:
  220.             self.primes.append(next(self._primegen))
  221.         return self.primes[bisect.bisect_left(self.primes, n)] == n
  222.  
  223.     @on_fail_report("n", "prime")
  224.     def prime_factors(self, n):
  225.         "Generate all prime factors of n"
  226.         for prime in it.chain(self.primes, self._enlarge_primes()):
  227.             if prime*prime > n:
  228.                 yield n
  229.                 break
  230.             while n > 1:
  231.                 new_n, modulo= divmod(n, prime)
  232.                 if modulo == 0:
  233.                     n= new_n
  234.                     yield prime
  235.                 else:
  236.                     break
  237.             if n == 1:
  238.                 break
  239.  
  240.     @on_fail_report("n", "prime")
  241.     def grouped_factors(self, n):
  242.         "Return (prime, power) for every prime factor of n"
  243.         for prime, occurences in it.groupby(self.prime_factors(n)):
  244.             occurences_count= sum(1 for _ in occurences)
  245.             yield prime, occurences_count
  246.  
  247.     @on_fail_report("n")
  248.     def divisor_count(self, n):
  249.         "Return the divisor count of n"
  250.         if n == 1: return 1
  251.         divisor_count= 1
  252.         for prime, power in self.grouped_factors(n):
  253.             divisor_count*= 1 + power
  254.         return divisor_count
  255.  
  256.     @on_fail_report("n")
  257.     def divisors(self, n):
  258.         if n == 1: return 1,
  259.         terms= []
  260.         for prime, power in self.grouped_factors(n):
  261.             terms.append([prime**p for p in xrange(power+1)])
  262.         return set(
  263.             reduce(op.mul, factors, 1)
  264.             for factors in
  265.             it.product(
  266.                 *(
  267.                     [prime**p for p in xrange(power+1)]
  268.                     for prime, power in self.grouped_factors(n)
  269.                 )
  270.             )
  271.         )
  272.  
  273. @on_fail_report("n", "prime")
  274. def prime_factors(n):
  275.     "Return the prime factors of n (oneshot)"
  276.     factorizer= Factorizer()
  277.     for prime in factorizer.prime_factors(n):
  278.         yield prime
  279.  
  280. @on_fail_report("m", "n")
  281. def primitive_pythagorean_triples():
  282.     m= 2
  283.     while 1:
  284.         for n in xrange(m-1, 0, -2):
  285.             if fractions.gcd(m, n) == 1:
  286.                 yield m*m - n*n, 2*m*n, m*m + n*n
  287.         m+= 1
  288.  
  289. @on_fail_report("triple", "factor")
  290. def all_pythagorean_triples(n=1000):
  291.     faults= 0
  292.     for triple in primitive_pythagorean_triples():
  293.         perimeter= sum(triple)
  294.         if perimeter > n:
  295.             faults+= 1
  296.             if faults > 10: break
  297.             continue
  298.         factor= 1
  299.         while factor*perimeter <= n:
  300.             yield factor*perimeter, (factor*triple[0], factor*triple[1], factor*triple[2])
  301.             factor+= 1
  302.  
  303. factorizer= Factorizer()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement