{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": true }, "outputs": [], "source": [ "from __future__ import print_function, division\n", "\n", "from itertools import starmap, product\n", "\n", "import numpy as np" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "def obtainable(coins):\n", " \"\"\"Find the set of values obtainable with the given coins.\"\"\"\n", " combos = set(coins)\n", " combos.update(map(sum, product(coins, coins)))\n", " return combos" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [], "source": [ "coins = [1, 2, 4]" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "{1, 2, 3, 4, 5, 6, 8}" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "obtainable(coins)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": true }, "outputs": [], "source": [ "values = set(range(1, 21))\n", " \n", "def unobtainable(coins):\n", " \"\"\"Find values that can't be obtained with the given coins.\"\"\"\n", " return values - obtainable(coins)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "{7, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}" ] }, "execution_count": 6, "metadata": {}, "output_type": "execute_result" } ], "source": [ "unobtainable(coins)" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": true }, "outputs": [], "source": [ "def possible_new_coin(coins, value):\n", " \"\"\"Generate new coins that would make the given value obtainable.\"\"\"\n", " if value % 2 == 0:\n", " yield value // 2\n", " \n", " for coin in coins:\n", " if value > coin:\n", " yield value - coin" ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "{3, 5, 6}" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "set(possible_new_coin(coins, 7))" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "{5, 7, 8}" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "set(possible_new_coin(coins, 9))" ] }, { "cell_type": "code", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "{5, 6, 8, 9}" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "set(possible_new_coin(coins, 10))" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [], "source": [ "class Combinator:\n", " def __init__(self, values):\n", " self.values = values\n", " \n", " combo = frozenset({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 20, 30, 40, 50, 60, 70, 80, 90})\n", " self.combos = {combo}\n", " self.smallest = len(combo)\n", "\n", " def add_combo(self, coins):\n", " \"\"\"Add a combo to the cache and update `smallest`.\"\"\"\n", " self.combos.add(coins)\n", " n = len(coins)\n", " if n < self.smallest:\n", " self.smallest = n\n", "\n", " def unobtainable(self, coins):\n", " \"\"\"Find values that can't be obtained with the given coins.\"\"\"\n", " return self.values - obtainable(coins)\n", "\n", " def consider(self, coins):\n", " \"\"\"Starting with `coins`, search for a minimal combo.\"\"\"\n", " \n", " # if we've already seen better, don't bother\n", " if len(coins) > self.smallest:\n", " return\n", " \n", " # find unobtainable values\n", " bad_values = unobtainable(coins)\n", " \n", " # if all values are obtainable, we're set\n", " if len(bad_values) == 0:\n", " self.add_combo(coins)\n", " return\n", " \n", " # otherwise, consider possible coins we could add\n", " for value in bad_values:\n", " for new_coin in possible_new_coin(coins, value):\n", " self.consider(coins | {new_coin})\n", " \n", " def print_winners(self):\n", " \"\"\"Print the best combinations.\"\"\"\n", " for combo in self.combos:\n", " if len(combo) == self.smallest:\n", " print(sorted(combo))" ] }, { "cell_type": "code", "execution_count": 14, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "frozenset({1})" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "frozenset((1,))" ] }, { "cell_type": "code", "execution_count": 16, "metadata": { "collapsed": false }, "outputs": [], "source": [ "values = set(range(1, 21))\n", "combinator = Combinator(values)\n", "combinator.consider(frozenset((1,)))" ] }, { "cell_type": "code", "execution_count": 17, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "6" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "combinator.smallest" ] }, { "cell_type": "code", "execution_count": 18, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "[1, 3, 5, 6, 13, 14]\n", "[1, 3, 4, 8, 9, 11]\n", "[1, 3, 5, 7, 9, 10]\n", "[1, 3, 4, 9, 11, 16]\n", "[1, 2, 5, 8, 9, 10]\n" ] } ], "source": [ "combinator.print_winners()" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": true }, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.5.1" } }, "nbformat": 4, "nbformat_minor": 0 }