git @ Cat's Eye Technologies NaNoGenLab / master perm-comb-finder / perm-comb-finder.py
master

Tree @master (Download .tar.gz)

perm-comb-finder.py @masterraw · history · blame

#!/usr/bin/env python

from math import factorial, pow
from optparse import OptionParser
import sys


try:
    from tqdm import tqdm
except ImportError:
    def tqdm(x):
        return x


# order matters. repetition not allowed.
def P(n, r):
    return factorial(n) / factorial(n - r)


# order matters. repetition allowed.
def P_duplicates(n, r):
    try:
        return pow(n, r)
    except OverflowError:
        return float('inf')  # everything in Python is Pythonic, esp. this


# order doesn't matter. repetition not allowed.
def C(n, r):
    return factorial(n) / (factorial(r) * factorial(n - r))


# order doesn't matter. repetition is allowed.
def C_duplicates(n, r):
    return factorial(n + r - 1) / (factorial(r) * factorial(n - 1))


# C and R are *how many ways*; this is *how many things* in those ways
def times_r(f):
    return lambda n, r: f(n, r) * r


TARGET = 50000
MIN_R = 1
memo = {}
MEMOIZE = False


def find(name, fun, top):
    best = 10000000000
    best_src = (None, None)
    for n in tqdm(xrange(1, top)):
        if best == TARGET:
            print "BINGO!"
            break
        for r in tqdm(xrange(MIN_R, n+1)):
            attempt = fun(n, r)
            if MEMOIZE and attempt < TARGET:
                memo.setdefault(attempt, []).append(
                    (name, n, r)
                )
            if attempt >= TARGET and attempt < best:
                best = attempt
                best_src = (n, r)
            if best == TARGET:
                print "BINGO!"
                break
    return (best, best_src[0], best_src[1])


def main(argv):
    global MIN_R, MEMOIZE

    optparser = OptionParser(__doc__)
    optparser.add_option("--minimum-r", default=1,
                         help="lowest value of _r_ to consider")
    optparser.add_option("--top", default=400,
                         help="highest value of _n_ to consider")
    optparser.add_option("--memoize", default=False, action='store_true',
                         help="memoize low results for later search")
    (options, args) = optparser.parse_args(argv[1:])
    MIN_R = int(options.minimum_r)
    MEMOIZE = options.memoize
    options.top = int(options.top)

    if False:
        (best, n, r) = find('P', P, options.top)
        print "best: P(%s,%s) = %s" % (n, r, best)
        
        (best, n, r) = find('fact', lambda n, r: P(n, n), options.top)
        print "best: P(%s,%s) = %s" % (n, n, best)
        
        (best, n, r) = find('P_duplicates', P_duplicates, options.top)
        print "best: P_duplicates(%s,%s) = %s" % (n, r, best)
        
        (best, n, r) = find('C', C, options.top)
        print "best: C(%s,%s) = %s" % (n, r, best)
        
        (best, n, r) = find('C_duplicates', C_duplicates, options.top)
        print "best: C_duplicates(%s,%s) = %s" % (n, r, best)

    (best, n, r) = find('r*P', times_r(P), options.top)
    print "best: r*P(%s,%s) = %s" % (n, r, best)

    # does not make much sense
    #(best, n, r) = find('fact', times_r(lambda n, r: P(n, n)), options.top)
    #print "best: P(%s,%s) = %s" % (n, n, best)

    (best, n, r) = find('r*P_duplicates', times_r(P_duplicates), options.top)
    print "best: r*P_duplicates(%s,%s) = %s" % (n, r, best)

    (best, n, r) = find('r*C', times_r(C), options.top)
    print "best: r*C(%s,%s) = %s" % (n, r, best)

    (best, n, r) = find('r*C_duplicates', times_r(C_duplicates), options.top)
    print "best: r*C_duplicates(%s,%s) = %s" % (n, r, best)

    if options.memoize:
        print
        for key1, value1 in memo.iteritems():
            for key2, value2 in memo.iteritems():
                if key1 + key2 == TARGET:
                    print "%s + %s == %s" % (key1, key2, TARGET)
                    print "%s: %s" % (key1, value1)
                    print "%s: %s" % (key2, value2)
                    print


if __name__ == '__main__':
    import sys
    main(sys.argv)