In [1]:
ranks = '123456789TJQKA'
values = [0, 0, 0, 0, 0,
          0, 0, 0, 0, 0,
          1, 2, 3, 4]
suits = 'cdhs'
hcp = dict((rank + suit, value)
    for suit in suits
    for rank, value in zip(ranks, values))
cards = sorted(hcp.keys())
def count_points(hand):
    return sum(hcp[card] for card in hand)
In [2]:
def bad():
    scores = {}
    for c0 in range(len(cards)):
      for c1 in range(c0):
        for c2 in range(c1):
          for c3 in range(c2):
            for c4 in range(c3):
                hand = map(cards.__getitem__,
                    [c4, c3, c2, c1, c0])
                score = count_points(hand)
                scores[score] = scores.get(score, 0) + 1
    return scores
In [3]:
%time bad()
CPU times: user 10.3 s, sys: 24 ms, total: 10.3 s
Wall time: 10.3 s

Out[3]:
{0: 658008,
 1: 365560,
 2: 424840,
 3: 526760,
 4: 601680,
 5: 354240,
 6: 291204,
 7: 250588,
 8: 155892,
 9: 84156,
 10: 52256,
 11: 31524,
 12: 13044,
 13: 5884,
 14: 2724,
 15: 1056,
 16: 276,
 17: 92,
 18: 28,
 19: 4}
In [4]:
def tricky():
    scores = {}
    c = [None] * 5
    for c[4] in range(len(cards)):
      for c[3] in range(c[4]):
        for c[2] in range(c[3]):
          for c[1] in range(c[2]):
            for c[0] in range(c[1]):
                hand = map(cards.__getitem__, c)
                score = count_points(hand)
                scores[score] = scores.get(score, 0) + 1
    return scores
In [5]:
%time tricky()
CPU times: user 9.36 s, sys: 0 ns, total: 9.36 s
Wall time: 9.35 s

Out[5]:
{0: 658008,
 1: 365560,
 2: 424840,
 3: 526760,
 4: 601680,
 5: 354240,
 6: 291204,
 7: 250588,
 8: 155892,
 9: 84156,
 10: 52256,
 11: 31524,
 12: 13044,
 13: 5884,
 14: 2724,
 15: 1056,
 16: 276,
 17: 92,
 18: 28,
 19: 4}
In [6]:
def better():
    scores = {}
    for indices in subset_generator(len(cards), 5):
        hand = map(cards.__getitem__, indices)
        score = count_points(hand)
        scores[score] = scores.get(score, 0) + 1
    return scores

def subset_generator(set_size, subset_size, accumulator=()):
    if subset_size == 0:
        yield accumulator
    else:
        for index in range(set_size):
            for indices in subset_generator(
                    set_size=index,
                    subset_size=subset_size - 1,
                    accumulator=(index,) + accumulator,
                    ):
                yield indices
In [7]:
list(subset_generator(5, 3))
Out[7]:
[(0, 1, 2),
 (0, 1, 3),
 (0, 2, 3),
 (1, 2, 3),
 (0, 1, 4),
 (0, 2, 4),
 (1, 2, 4),
 (0, 3, 4),
 (1, 3, 4),
 (2, 3, 4)]
In [8]:
%time better()
CPU times: user 14.9 s, sys: 4 ms, total: 14.9 s
Wall time: 14.9 s

Out[8]:
{0: 658008,
 1: 365560,
 2: 424840,
 3: 526760,
 4: 601680,
 5: 354240,
 6: 291204,
 7: 250588,
 8: 155892,
 9: 84156,
 10: 52256,
 11: 31524,
 12: 13044,
 13: 5884,
 14: 2724,
 15: 1056,
 16: 276,
 17: 92,
 18: 28,
 19: 4}
In [9]:
def new_subset_generator(set_size, subset_size, accumulator=()):
    if subset_size == 0:
        yield accumulator
    else:
        for index in range(set_size):
            yield from new_subset_generator(
                    set_size=index,
                    subset_size=subset_size - 1,
                    accumulator=(index,) + accumulator,
                    )
In [10]:
def even_better():
    scores = {}
    for indices in new_subset_generator(len(cards), 5):
        hand = map(cards.__getitem__, indices)
        score = count_points(hand)
        scores[score] = scores.get(score, 0) + 1
    return scores
In [11]:
%time even_better()
CPU times: user 14.4 s, sys: 0 ns, total: 14.4 s
Wall time: 14.4 s

Out[11]:
{0: 658008,
 1: 365560,
 2: 424840,
 3: 526760,
 4: 601680,
 5: 354240,
 6: 291204,
 7: 250588,
 8: 155892,
 9: 84156,
 10: 52256,
 11: 31524,
 12: 13044,
 13: 5884,
 14: 2724,
 15: 1056,
 16: 276,
 17: 92,
 18: 28,
 19: 4}
In [12]:
from itertools import combinations
from collections import Counter
def best():
    hands = combinations(cards, 5)
    scores = Counter(map(count_points, hands))
    return dict(scores)
In [13]:
%time best()
CPU times: user 7.81 s, sys: 0 ns, total: 7.81 s
Wall time: 7.82 s

Out[13]:
{0: 658008,
 1: 365560,
 2: 424840,
 3: 526760,
 4: 601680,
 5: 354240,
 6: 291204,
 7: 250588,
 8: 155892,
 9: 84156,
 10: 52256,
 11: 31524,
 12: 13044,
 13: 5884,
 14: 2724,
 15: 1056,
 16: 276,
 17: 92,
 18: 28,
 19: 4}
In [14]:
from itertools import permutations
from collections import Counter
def queens_filter(n):
    tally = Counter(
        solution[0]
            for solution in permutations(range(n))
            if is_valid(solution))
    return dict(tally)

def is_valid(solution):
    rows = set(solution)
    diagonals = set(i-j for i, j in enumerate(solution))
    antidiags = set(i+j for i, j in enumerate(solution))
    return len(solution) == len(rows) == len(diagonals) == len(antidiags)
In [15]:
queens_filter(4)
Out[15]:
{1: 1, 2: 1}
In [16]:
%time queens_filter(9)
CPU times: user 2.83 s, sys: 32 ms, total: 2.86 s
Wall time: 2.87 s

Out[16]:
{0: 28, 1: 30, 2: 47, 3: 44, 4: 54, 5: 44, 6: 47, 7: 30, 8: 28}
In [17]:
def queens_prune(n):
    tally = Counter(s[0] for s in solution_generator(n))
    return dict(tally)

def solution_generator(n, accumulator=()):
    if not is_valid(accumulator):
        return
    elif len(accumulator) == n:
        yield accumulator
    else:
        for new_queen in range(n):
            yield from solution_generator(n, accumulator + (new_queen,))
In [18]:
list(solution_generator(4))
Out[18]:
[(1, 3, 0, 2), (2, 0, 3, 1)]
In [19]:
%time queens_prune(9)
CPU times: user 544 ms, sys: 12 ms, total: 556 ms
Wall time: 558 ms

Out[19]:
{0: 28, 1: 30, 2: 47, 3: 44, 4: 54, 5: 44, 6: 47, 7: 30, 8: 28}
In [19]: