{
  "id": "P094",
  "slug": "packing-planner",
  "key": "P094-packing-planner",
  "title": "Packing planner",
  "summary": "Items into bins by First Fit, Best Fit and First Fit Decreasing, against the fewest bins found by search; and the 0/1 knapsack - the most value that fits, by dynamic programming over every weight - against greedy best value per kilogram.",
  "entry": "main.eml",
  "ui": "terminal",
  "readme": "# P094 - Packing planner\n\nItems into bins by three quick rules - First Fit, Best Fit and First Fit\nDecreasing - against the fewest bins possible, found by search. And the\n0/1 knapsack: the most value that fits, by dynamic programming, against\ntaking the best value per kilogram first.\n\n- `main.eml` - the menu, reading items, and the reports\n- `packing.eml` - the three rules, the search for the fewest bins, the\n  knapsack table and the greedy rule\n\nHow each part works:\n\n- First Fit, as in the corpus case `bin-packing-first-fit`: each item, in\n  order, goes into the first bin it still fits in, or a new bin. On the\n  corpus's items (5 7 4 2 8 3 1 into bins of 10) it uses 4 bins, though 3\n  are enough: 8+2, 7+3, 5+4+1.\n- Best Fit puts each item where it fits most tightly. First Fit\n  Decreasing takes the largest first, which usually helps, but not always:\n  with 8 4 2 6 3 3 in bins of 13 it uses 3, where 8+3+2 and 6+4+3 fill\n  two.\n- The fewest bins: a search, as in the corpus case\n  `subset-sum-backtracking`. Items go largest first into every bin they\n  fit and into one new bin, and any branch already using as many bins as\n  the best found is dropped. When the count reaches the total divided by\n  the capacity, rounded up, nothing can do better, so the search stops.\n- The knapsack: for every weight up to the capacity, the most value that\n  fits, item by item. Going down through the weights means each item is\n  used at most once. A table of choices reads the items back. Taking the\n  best value per kilogram first can be beaten: in a 50 kg knapsack it\n  takes 10 kg worth 60 and 20 kg worth 100 (160), where 20 kg and 30 kg\n  give 220.\n\nWhat is checked: menu choices 1 to 5; a capacity of 1 to 100; 1 to 12\nitem sizes from 1 to the capacity; 1 to 12 knapsack items as weight-value\npairs, each 1 to 999. An empty answer cancels.\n\nSessions: `sessions/basic.in` gives:\n- the corpus's items: 4, 4, 3 and 3 bins;\n- the knapsack: 220 against the greedy 160;\n- 8 4 2 6 3 3 in bins of 13, where even First Fit Decreasing misses the\n  2 bins;\n- a knapsack where the greedy rule is right (90), and ten items that every\n  rule packs into the fewest bins.\n\n`sessions/bad-input.in` gives:\n- menu choices 0 and x;\n- capacities 0 and 101; an item bigger than its bin, 13 items, letters;\n- an odd number of knapsack numbers and a weight of 0; then cancels.\n\nBuilt on the verified corpus cases `bin-packing-first-fit` (first fit,\nand the bin it wastes) and `subset-sum-backtracking` (searching every\nchoice, dropping hopeless branches).\n",
  "modules": [
    {
      "name": "main.eml",
      "eml": "# P094 packing planner: items into bins by First Fit, Best Fit and First\n# Fit Decreasing, against the fewest bins possible; and the 0/1 knapsack by\n# dynamic programming, against the greedy best-value-per-weight rule.\nimport packing\n\ndef plain(s):\n    0 => i\n    len(s) => j\n    while i < j and s[i] == \" \":\n        i + 1 => i\n    while j > i and s[j - 1] == \" \":\n        j - 1 => j\n    return s[i:j]\n\ndef numbers(s):\n    [] => out\n    \"\" => w\n    for c in s + \" \":\n        if c == \" \" or c == \",\":\n            if w != \"\":\n                if len(w) > 3:\n                    return []\n                0 => n\n                for d in w:\n                    if not (d >= \"0\" and d <= \"9\"):\n                        return []\n                    n * 10 + int(d) => n\n                out + [n] => out\n            \"\" => w\n        else:\n            w + c => w\n    return out\n\ndef bins_text(sizes, bins):\n    \"\" => s\n    for b in bins:\n        \"\" => t\n        0 => used\n        for i in b:\n            if t != \"\":\n                t + \"+\" => t\n            t + str(sizes[i]) => t\n            used + sizes[i] => used\n        if s != \"\":\n            s + \"  \" => s\n        s + \"[\" + t + \"]\" => s\n    return s\n\ndef count(n):\n    if n == 1:\n        return \"1 bin \"\n    return str(n) + \" bins\"\n\ndef pack(sizes, cap):\n    packing.first_fit(sizes, cap, packing.in_order(len(sizes))) => ff\n    packing.best_fit(sizes, cap) => bf\n    packing.first_fit(sizes, cap, packing.largest_first(sizes)) => ffd\n    packing.fewest(sizes, cap) => best\n    0 => total\n    for s in sizes:\n        total + s => total\n    (str(len(sizes)) + \" items, \" + str(total) + \" in all, bins of \" + str(cap) + \":\") ^0\n    (\"  First Fit            \" + count(len(ff)) + \"  \" + bins_text(sizes, ff)) ^0\n    (\"  Best Fit             \" + count(len(bf)) + \"  \" + bins_text(sizes, bf)) ^0\n    (\"  First Fit Decreasing \" + count(len(ffd)) + \"  \" + bins_text(sizes, ffd)) ^0\n    (\"  fewest possible      \" + count(len(best)) + \"  \" + bins_text(sizes, best)) ^0\n    if len(ff) > len(best):\n        \"First Fit opens bins early for items that later ones would have fitted around; taking the largest first usually avoids it, but not always.\" ^0\n    else:\n        \"First Fit already does as well as can be done here.\" ^0\n\ndef items_text(items, chosen):\n    \"\" => s\n    for i in chosen:\n        if s != \"\":\n            s + \", \" => s\n        s + \"#\" + str(i + 1) + \" (\" + str(items[i][0]) + \" kg, \" + str(items[i][1]) + \")\" => s\n    if s == \"\":\n        \"nothing\" => s\n    return s\n\ndef knap(items, cap):\n    \"  item  weight  value\" ^0\n    for i in [0:len(items) - 1]:\n        (\"  #\" + str(i + 1) + \"     \" + str(items[i][0]) + \" kg    \" + str(items[i][1])) ^0\n    packing.knapsack(items, cap) => best\n    packing.by_value(items, cap) => greedy\n    (\"Most value within \" + str(cap) + \" kg: \" + str(best[0]) + \", from \" + items_text(items, best[1]) + \".\") ^0\n    (\"Best value per kg first: \" + str(greedy[0]) + \", from \" + items_text(items, greedy[1]) + \".\") ^0\n    if greedy[0] < best[0]:\n        \"The greedy rule fills up on the best ratios and leaves room it cannot use; the table over every weight finds the better mix.\" ^0\n    else:\n        \"Here the greedy rule finds the best too.\" ^0\n\ndef new_sizes(sizes, cap):\n    plain(input(\"bin capacity (1 to 100)> \")) => s\n    if s == \"\":\n        \"Cancelled.\" ^0\n        return [sizes, cap]\n    numbers(s) => c\n    if len(c) != 1 or c[0] < 1 or c[0] > 100:\n        \"The capacity is a whole number from 1 to 100.\" ^0\n        return [sizes, cap]\n    plain(input(\"item sizes (1 to 12 of them, each 1 to the capacity)> \")) => t\n    numbers(t) => xs\n    True => ok\n    for x in xs:\n        if x < 1 or x > c[0]:\n            False => ok\n    if not ok or len(xs) < 1 or len(xs) > 12:\n        \"Type 1 to 12 whole numbers, each from 1 to the capacity.\" ^0\n        return [sizes, cap]\n    (str(len(xs)) + \" items for bins of \" + str(c[0]) + \".\") ^0\n    return [xs, c[0]]\n\ndef new_items(items, cap):\n    plain(input(\"knapsack capacity in kg (1 to 100)> \")) => s\n    if s == \"\":\n        \"Cancelled.\" ^0\n        return [items, cap]\n    numbers(s) => c\n    if len(c) != 1 or c[0] < 1 or c[0] > 100:\n        \"The capacity is a whole number from 1 to 100.\" ^0\n        return [items, cap]\n    plain(input(\"items as weight value pairs (like 10 60 20 100)> \")) => t\n    numbers(t) => xs\n    True => ok\n    for x in xs:\n        if x < 1:\n            False => ok\n    if not ok or len(xs) < 2 or len(xs) > 24 or len(xs) % 2 != 0:\n        \"Type 1 to 12 pairs of whole numbers from 1 to 999: a weight, then its value.\" ^0\n        return [items, cap]\n    [] => out\n    0 => i\n    while i < len(xs):\n        out + [[xs[i], xs[i + 1]]] => out\n        i + 2 => i\n    (str(len(out)) + \" items for a knapsack of \" + str(c[0]) + \" kg.\") ^0\n    return [out, c[0]]\n\n[5, 7, 4, 2, 8, 3, 1] => sizes\n10 => cap\n[[10, 60], [20, 100], [30, 120]] => items\n50 => kcap\n\n\"== Packing planner ==\" ^0\n\"Bins: three quick rules against the fewest possible. A knapsack: the most value that fits.\" ^0\nTrue => running\nwhile running:\n    \"\" ^0\n    \"1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\" ^0\n    plain(input(\"choice> \")) => choice\n    if choice == \"1\":\n        pack(sizes, cap)\n    elif choice == \"2\":\n        knap(items, kcap)\n    elif choice == \"3\":\n        new_sizes(sizes, cap) => r\n        r[0] => sizes\n        r[1] => cap\n    elif choice == \"4\":\n        new_items(items, kcap) => r\n        r[0] => items\n        r[1] => kcap\n    elif choice == \"5\":\n        False => running\n    else:\n        \"Pick a number from 1 to 5.\" ^0\n\"Bye.\" ^0\n",
      "python": "import packing\n\ndef plain(s):\n    i = 0\n    j = len(s)\n    while i < j and s[i] == \" \":\n        i = i + 1\n    while j > i and s[j - 1] == \" \":\n        j = j - 1\n    return s[i:j]\n\ndef numbers(s):\n    out = []\n    w = \"\"\n    for c in s + \" \":\n        if c == \" \" or c == \",\":\n            if w != \"\":\n                if len(w) > 3:\n                    return []\n                n = 0\n                for d in w:\n                    if not (d >= \"0\" and d <= \"9\"):\n                        return []\n                    n = n * 10 + int(d)\n                out = out + [n]\n            w = \"\"\n        else:\n            w = w + c\n    return out\n\ndef bins_text(sizes, bins):\n    s = \"\"\n    for b in bins:\n        t = \"\"\n        used = 0\n        for i in b:\n            if t != \"\":\n                t = t + \"+\"\n            t = t + str(sizes[i])\n            used = used + sizes[i]\n        if s != \"\":\n            s = s + \"  \"\n        s = s + \"[\" + t + \"]\"\n    return s\n\ndef count(n):\n    if n == 1:\n        return \"1 bin \"\n    return str(n) + \" bins\"\n\ndef pack(sizes, cap):\n    ff = packing.first_fit(sizes, cap, packing.in_order(len(sizes)))\n    bf = packing.best_fit(sizes, cap)\n    ffd = packing.first_fit(sizes, cap, packing.largest_first(sizes))\n    best = packing.fewest(sizes, cap)\n    total = 0\n    for s in sizes:\n        total = total + s\n    print(str(len(sizes)) + \" items, \" + str(total) + \" in all, bins of \" + str(cap) + \":\")\n    print(\"  First Fit            \" + count(len(ff)) + \"  \" + bins_text(sizes, ff))\n    print(\"  Best Fit             \" + count(len(bf)) + \"  \" + bins_text(sizes, bf))\n    print(\"  First Fit Decreasing \" + count(len(ffd)) + \"  \" + bins_text(sizes, ffd))\n    print(\"  fewest possible      \" + count(len(best)) + \"  \" + bins_text(sizes, best))\n    if len(ff) > len(best):\n        print(\"First Fit opens bins early for items that later ones would have fitted around; taking the largest first usually avoids it, but not always.\")\n    else:\n        print(\"First Fit already does as well as can be done here.\")\n\ndef items_text(items, chosen):\n    s = \"\"\n    for i in chosen:\n        if s != \"\":\n            s = s + \", \"\n        s = s + \"#\" + str(i + 1) + \" (\" + str(items[i][0]) + \" kg, \" + str(items[i][1]) + \")\"\n    if s == \"\":\n        s = \"nothing\"\n    return s\n\ndef knap(items, cap):\n    print(\"  item  weight  value\")\n    for i in range(0, len(items)):\n        print(\"  #\" + str(i + 1) + \"     \" + str(items[i][0]) + \" kg    \" + str(items[i][1]))\n    best = packing.knapsack(items, cap)\n    greedy = packing.by_value(items, cap)\n    print(\"Most value within \" + str(cap) + \" kg: \" + str(best[0]) + \", from \" + items_text(items, best[1]) + \".\")\n    print(\"Best value per kg first: \" + str(greedy[0]) + \", from \" + items_text(items, greedy[1]) + \".\")\n    if greedy[0] < best[0]:\n        print(\"The greedy rule fills up on the best ratios and leaves room it cannot use; the table over every weight finds the better mix.\")\n    else:\n        print(\"Here the greedy rule finds the best too.\")\n\ndef new_sizes(sizes, cap):\n    s = plain(input(\"bin capacity (1 to 100)> \"))\n    if s == \"\":\n        print(\"Cancelled.\")\n        return [sizes, cap]\n    c = numbers(s)\n    if len(c) != 1 or c[0] < 1 or c[0] > 100:\n        print(\"The capacity is a whole number from 1 to 100.\")\n        return [sizes, cap]\n    t = plain(input(\"item sizes (1 to 12 of them, each 1 to the capacity)> \"))\n    xs = numbers(t)\n    ok = True\n    for x in xs:\n        if x < 1 or x > c[0]:\n            ok = False\n    if not ok or len(xs) < 1 or len(xs) > 12:\n        print(\"Type 1 to 12 whole numbers, each from 1 to the capacity.\")\n        return [sizes, cap]\n    print(str(len(xs)) + \" items for bins of \" + str(c[0]) + \".\")\n    return [xs, c[0]]\n\ndef new_items(items, cap):\n    s = plain(input(\"knapsack capacity in kg (1 to 100)> \"))\n    if s == \"\":\n        print(\"Cancelled.\")\n        return [items, cap]\n    c = numbers(s)\n    if len(c) != 1 or c[0] < 1 or c[0] > 100:\n        print(\"The capacity is a whole number from 1 to 100.\")\n        return [items, cap]\n    t = plain(input(\"items as weight value pairs (like 10 60 20 100)> \"))\n    xs = numbers(t)\n    ok = True\n    for x in xs:\n        if x < 1:\n            ok = False\n    if not ok or len(xs) < 2 or len(xs) > 24 or len(xs) % 2 != 0:\n        print(\"Type 1 to 12 pairs of whole numbers from 1 to 999: a weight, then its value.\")\n        return [items, cap]\n    out = []\n    i = 0\n    while i < len(xs):\n        out = out + [[xs[i], xs[i + 1]]]\n        i = i + 2\n    print(str(len(out)) + \" items for a knapsack of \" + str(c[0]) + \" kg.\")\n    return [out, c[0]]\n\nsizes = [5, 7, 4, 2, 8, 3, 1]\ncap = 10\nitems = [[10, 60], [20, 100], [30, 120]]\nkcap = 50\nprint(\"== Packing planner ==\")\nprint(\"Bins: three quick rules against the fewest possible. A knapsack: the most value that fits.\")\nrunning = True\nwhile running:\n    print(\"\")\n    print(\"1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\")\n    choice = plain(input(\"choice> \"))\n    if choice == \"1\":\n        pack(sizes, cap)\n    elif choice == \"2\":\n        knap(items, kcap)\n    elif choice == \"3\":\n        r = new_sizes(sizes, cap)\n        sizes = r[0]\n        cap = r[1]\n    elif choice == \"4\":\n        r = new_items(items, kcap)\n        items = r[0]\n        kcap = r[1]\n    elif choice == \"5\":\n        running = False\n    else:\n        print(\"Pick a number from 1 to 5.\")\nprint(\"Bye.\")\n"
    },
    {
      "name": "packing.eml",
      "eml": "# P094 packing planner - items of whole-number sizes into bins of one\n# capacity by three quick rules and by search for the fewest bins; and the\n# 0/1 knapsack: the most value that fits, by dynamic programming, against\n# taking the best value per unit of weight first.\n\ndef first_fit(sizes, cap, order):\n    # The corpus case bin-packing-first-fit: each item, in the given order,\n    # into the first bin it still fits in, a new bin if none. Returns the\n    # bins as lists of item indices.\n    [] => bins\n    [] => room\n    for i in order:\n        -1 => chosen\n        for b in [0:len(bins) - 1]:\n            if chosen == -1 and room[b] >= sizes[i]:\n                b => chosen\n        if chosen == -1:\n            bins + [[i]] => bins\n            room + [cap - sizes[i]] => room\n        else:\n            bins[chosen] + [i] => bins[chosen]\n            room[chosen] - sizes[i] => room[chosen]\n    return bins\n\ndef best_fit(sizes, cap):\n    # Each item into the bin it fits most tightly (least room left after),\n    # the first such bin on a tie.\n    [] => bins\n    [] => room\n    for i in [0:len(sizes) - 1]:\n        -1 => chosen\n        for b in [0:len(bins) - 1]:\n            if room[b] >= sizes[i] and (chosen == -1 or room[b] < room[chosen]):\n                b => chosen\n        if chosen == -1:\n            bins + [[i]] => bins\n            room + [cap - sizes[i]] => room\n        else:\n            bins[chosen] + [i] => bins[chosen]\n            room[chosen] - sizes[i] => room[chosen]\n    return bins\n\ndef largest_first(sizes):\n    # The item indices by size, largest first, keeping the order of equals.\n    [0] * len(sizes) => order\n    for i in [0:len(sizes) - 1]:\n        i => order[i]\n    for i in [1:len(order) - 1]:\n        order[i] => x\n        i - 1 => j\n        while j >= 0 and sizes[order[j]] < sizes[x]:\n            order[j] => order[j + 1]\n            j - 1 => j\n        x => order[j + 1]\n    return order\n\ndef in_order(n):\n    [0] * n => out\n    for i in [0:n - 1]:\n        i => out[i]\n    return out\n\ndef fewest(sizes, cap):\n    # The fewest bins, by search (as in the corpus case\n    # subset-sum-backtracking): items largest first, each tried in every\n    # bin it fits and in one new bin, giving up on any branch that already\n    # uses as many bins as the best found. A bin count that reaches\n    # ceil(total / cap) cannot be beaten, so the search stops there.\n    largest_first(sizes) => order\n    first_fit(sizes, cap, order) => best\n    0 => total\n    for s in sizes:\n        total + s => total\n    int((total + cap - 1 - (total + cap - 1) % cap) / cap) => bound\n    if len(best) == bound:\n        return best\n    [best] => holder\n    place(sizes, cap, order, 0, [], [], holder, bound)\n    return holder[0]\n\ndef place(sizes, cap, order, k, bins, room, holder, bound):\n    if len(holder[0]) == bound:\n        return\n    if k == len(order):\n        if len(bins) < len(holder[0]):\n            [] => copy\n            for b in bins:\n                copy + [b] => copy\n            copy => holder[0]\n        return\n    order[k] => i\n    for b in [0:len(bins) - 1]:\n        if room[b] >= sizes[i]:\n            bins[b] => old\n            old + [i] => bins[b]\n            room[b] - sizes[i] => room[b]\n            place(sizes, cap, order, k + 1, bins, room, holder, bound)\n            old => bins[b]\n            room[b] + sizes[i] => room[b]\n    if len(bins) + 1 < len(holder[0]):\n        place(sizes, cap, order, k + 1, bins + [[i]], room + [cap - sizes[i]], holder, bound)\n\ndef knapsack(items, cap):\n    # best[c] is the most value within weight c, item by item, going down\n    # in c so each item is used at most once; took[k][c] records whether\n    # item k was taken for c, to read the choice back. [value, items].\n    [0] * (cap + 1) => best\n    [] => took\n    for k in [0:len(items) - 1]:\n        items[k][0] => w\n        items[k][1] => v\n        [False] * (cap + 1) => row\n        cap => c\n        while c >= w:\n            if best[c - w] + v > best[c]:\n                best[c - w] + v => best[c]\n                True => row[c]\n            c - 1 => c\n        took + [row] => took\n    [] => chosen\n    cap => c\n    len(items) - 1 => k\n    while k >= 0:\n        if took[k][c]:\n            [k] + chosen => chosen\n            c - items[k][0] => c\n        k - 1 => k\n    return [best[cap], chosen]\n\ndef by_value(items, cap):\n    # Greedy: best value per unit of weight first (compared by\n    # cross-multiplication), taking each that still fits. [value, items].\n    [0] * len(items) => order\n    for i in [0:len(items) - 1]:\n        i => order[i]\n    for i in [1:len(order) - 1]:\n        order[i] => x\n        i - 1 => j\n        while j >= 0 and items[order[j]][1] * items[x][0] < items[x][1] * items[order[j]][0]:\n            order[j] => order[j + 1]\n            j - 1 => j\n        x => order[j + 1]\n    0 => value\n    cap => left\n    [] => chosen\n    for i in order:\n        if items[i][0] <= left:\n            left - items[i][0] => left\n            value + items[i][1] => value\n            chosen + [i] => chosen\n    return [value, chosen]\n",
      "python": "def first_fit(sizes, cap, order):\n    bins = []\n    room = []\n    for i in order:\n        chosen = -1\n        for b in range(0, len(bins)):\n            if chosen == -1 and room[b] >= sizes[i]:\n                chosen = b\n        if chosen == -1:\n            bins = bins + [[i]]\n            room = room + [cap - sizes[i]]\n        else:\n            bins[chosen] = bins[chosen] + [i]\n            room[chosen] = room[chosen] - sizes[i]\n    return bins\n\ndef best_fit(sizes, cap):\n    bins = []\n    room = []\n    for i in range(0, len(sizes)):\n        chosen = -1\n        for b in range(0, len(bins)):\n            if room[b] >= sizes[i] and (chosen == -1 or room[b] < room[chosen]):\n                chosen = b\n        if chosen == -1:\n            bins = bins + [[i]]\n            room = room + [cap - sizes[i]]\n        else:\n            bins[chosen] = bins[chosen] + [i]\n            room[chosen] = room[chosen] - sizes[i]\n    return bins\n\ndef largest_first(sizes):\n    order = [0] * len(sizes)\n    for i in range(0, len(sizes)):\n        order[i] = i\n    for i in range(1, len(order)):\n        x = order[i]\n        j = i - 1\n        while j >= 0 and sizes[order[j]] < sizes[x]:\n            order[j + 1] = order[j]\n            j = j - 1\n        order[j + 1] = x\n    return order\n\ndef in_order(n):\n    out = [0] * n\n    for i in range(0, n):\n        out[i] = i\n    return out\n\ndef fewest(sizes, cap):\n    order = largest_first(sizes)\n    best = first_fit(sizes, cap, order)\n    total = 0\n    for s in sizes:\n        total = total + s\n    bound = int((total + cap - 1 - (total + cap - 1) % cap) / cap)\n    if len(best) == bound:\n        return best\n    holder = [best]\n    place(sizes, cap, order, 0, [], [], holder, bound)\n    return holder[0]\n\ndef place(sizes, cap, order, k, bins, room, holder, bound):\n    if len(holder[0]) == bound:\n        return\n    if k == len(order):\n        if len(bins) < len(holder[0]):\n            copy = []\n            for b in bins:\n                copy = copy + [b]\n            holder[0] = copy\n        return\n    i = order[k]\n    for b in range(0, len(bins)):\n        if room[b] >= sizes[i]:\n            old = bins[b]\n            bins[b] = old + [i]\n            room[b] = room[b] - sizes[i]\n            place(sizes, cap, order, k + 1, bins, room, holder, bound)\n            bins[b] = old\n            room[b] = room[b] + sizes[i]\n    if len(bins) + 1 < len(holder[0]):\n        place(sizes, cap, order, k + 1, bins + [[i]], room + [cap - sizes[i]], holder, bound)\n\ndef knapsack(items, cap):\n    best = [0] * (cap + 1)\n    took = []\n    for k in range(0, len(items)):\n        w = items[k][0]\n        v = items[k][1]\n        row = [False] * (cap + 1)\n        c = cap\n        while c >= w:\n            if best[c - w] + v > best[c]:\n                best[c] = best[c - w] + v\n                row[c] = True\n            c = c - 1\n        took = took + [row]\n    chosen = []\n    c = cap\n    k = len(items) - 1\n    while k >= 0:\n        if took[k][c]:\n            chosen = [k] + chosen\n            c = c - items[k][0]\n        k = k - 1\n    return [best[cap], chosen]\n\ndef by_value(items, cap):\n    order = [0] * len(items)\n    for i in range(0, len(items)):\n        order[i] = i\n    for i in range(1, len(order)):\n        x = order[i]\n        j = i - 1\n        while j >= 0 and items[order[j]][1] * items[x][0] < items[x][1] * items[order[j]][0]:\n            order[j + 1] = order[j]\n            j = j - 1\n        order[j + 1] = x\n    value = 0\n    left = cap\n    chosen = []\n    for i in order:\n        if items[i][0] <= left:\n            left = left - items[i][0]\n            value = value + items[i][1]\n            chosen = chosen + [i]\n    return [value, chosen]\n"
    }
  ],
  "sessions": [
    {
      "name": "bad-input",
      "input": "0\nx\n3\n0\n3\n10\n11 2\n3\n10\n1 1 1 1 1 1 1 1 1 1 1 1 1\n3\n10\na b\n3\n\n4\n50\n10 60 20\n4\n101\n4\n50\n0 5\n4\n\n5\n",
      "screen": "== Packing planner ==\nBins: three quick rules against the fewest possible. A knapsack: the most value that fits.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 0\nPick a number from 1 to 5.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> x\nPick a number from 1 to 5.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 3\nbin capacity (1 to 100)> 0\nThe capacity is a whole number from 1 to 100.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 3\nbin capacity (1 to 100)> 10\nitem sizes (1 to 12 of them, each 1 to the capacity)> 11 2\nType 1 to 12 whole numbers, each from 1 to the capacity.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 3\nbin capacity (1 to 100)> 10\nitem sizes (1 to 12 of them, each 1 to the capacity)> 1 1 1 1 1 1 1 1 1 1 1 1 1\nType 1 to 12 whole numbers, each from 1 to the capacity.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 3\nbin capacity (1 to 100)> 10\nitem sizes (1 to 12 of them, each 1 to the capacity)> a b\nType 1 to 12 whole numbers, each from 1 to the capacity.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 3\nbin capacity (1 to 100)> \nCancelled.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 4\nknapsack capacity in kg (1 to 100)> 50\nitems as weight value pairs (like 10 60 20 100)> 10 60 20\nType 1 to 12 pairs of whole numbers from 1 to 999: a weight, then its value.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 4\nknapsack capacity in kg (1 to 100)> 101\nThe capacity is a whole number from 1 to 100.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 4\nknapsack capacity in kg (1 to 100)> 50\nitems as weight value pairs (like 10 60 20 100)> 0 5\nType 1 to 12 pairs of whole numbers from 1 to 999: a weight, then its value.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 4\nknapsack capacity in kg (1 to 100)> \nCancelled.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    },
    {
      "name": "basic",
      "input": "1\n2\n3\n13\n8 4 2 6 3 3\n1\n4\n10\n5 10 4 40 6 30 3 50\n2\n3\n10\n6 6 5 5 4 4 3 3 2 2\n1\n5\n",
      "screen": "== Packing planner ==\nBins: three quick rules against the fewest possible. A knapsack: the most value that fits.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 1\n7 items, 30 in all, bins of 10:\n  First Fit            4 bins  [5+4+1]  [7+2]  [8]  [3]\n  Best Fit             4 bins  [5+4+1]  [7+2]  [8]  [3]\n  First Fit Decreasing 3 bins  [8+2]  [7+3]  [5+4+1]\n  fewest possible      3 bins  [8+2]  [7+3]  [5+4+1]\nFirst Fit opens bins early for items that later ones would have fitted around; taking the largest first usually avoids it, but not always.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 2\n  item  weight  value\n  #1     10 kg    60\n  #2     20 kg    100\n  #3     30 kg    120\nMost value within 50 kg: 220, from #2 (20 kg, 100), #3 (30 kg, 120).\nBest value per kg first: 160, from #1 (10 kg, 60), #2 (20 kg, 100).\nThe greedy rule fills up on the best ratios and leaves room it cannot use; the table over every weight finds the better mix.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 3\nbin capacity (1 to 100)> 13\nitem sizes (1 to 12 of them, each 1 to the capacity)> 8 4 2 6 3 3\n6 items for bins of 13.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 1\n6 items, 26 in all, bins of 13:\n  First Fit            3 bins  [8+4]  [2+6+3]  [3]\n  Best Fit             3 bins  [8+4]  [2+6+3]  [3]\n  First Fit Decreasing 3 bins  [8+4]  [6+3+3]  [2]\n  fewest possible      2 bins  [8+3+2]  [6+4+3]\nFirst Fit opens bins early for items that later ones would have fitted around; taking the largest first usually avoids it, but not always.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 4\nknapsack capacity in kg (1 to 100)> 10\nitems as weight value pairs (like 10 60 20 100)> 5 10 4 40 6 30 3 50\n4 items for a knapsack of 10 kg.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 2\n  item  weight  value\n  #1     5 kg    10\n  #2     4 kg    40\n  #3     6 kg    30\n  #4     3 kg    50\nMost value within 10 kg: 90, from #2 (4 kg, 40), #4 (3 kg, 50).\nBest value per kg first: 90, from #4 (3 kg, 50), #2 (4 kg, 40).\nHere the greedy rule finds the best too.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 3\nbin capacity (1 to 100)> 10\nitem sizes (1 to 12 of them, each 1 to the capacity)> 6 6 5 5 4 4 3 3 2 2\n10 items for bins of 10.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 1\n10 items, 40 in all, bins of 10:\n  First Fit            4 bins  [6+4]  [6+4]  [5+5]  [3+3+2+2]\n  Best Fit             4 bins  [6+4]  [6+4]  [5+5]  [3+3+2+2]\n  First Fit Decreasing 4 bins  [6+4]  [6+4]  [5+5]  [3+3+2+2]\n  fewest possible      4 bins  [6+4]  [6+4]  [5+5]  [3+3+2+2]\nFirst Fit already does as well as can be done here.\n\n1) pack the items  2) fill the knapsack  3) new items to pack  4) new knapsack items  5) quit\nchoice> 5\nBye.\n",
      "interpreter": "equal"
    }
  ],
  "builtOn": [
    {
      "slug": "bin-packing-first-fit",
      "caseId": "095-bin-packing-first-fit",
      "title": "Bin packing (first-fit)"
    },
    {
      "slug": "subset-sum-backtracking",
      "caseId": "123-subset-sum-backtracking",
      "title": "Subset sum (backtracking)"
    }
  ],
  "updated": "2026-10-15"
}
