Fill order from smaller packages?

void fill packaging
void fill packaging materials
eco friendly void fill
shopify product dimensions shipping
cheapest void fill
biodegradable void fill
how to fill empty space in a box
consolidated shipping company

The input is an integer that specifies the amount to be ordered. There are predefined package sizes that have to be used to create that order.

e.g.

Packs
3 for $5
5 for $9
9 for $16

for an input order 13 the output should be:

2x5 + 1x3

So far I've the following approach:

remaining_order = 13
package_numbers = [9,5,3]
required_packages = []

while remaining_order > 0:
    found = False
    for pack_num in package_numbers:
        if pack_num <= remaining_order:
            required_packages.append(pack_num)
            remaining_order -= pack_num
            found = True
            break

    if not found:
        break

But this will lead to the wrong result:

1x9 + 1x3 remaining: 1

So, you need to fill the order with the packages such that the total price is maximal? This is known as Knapsack problem. In that Wikipedia article you'll find several solutions written in Python.

To be more precise, you need a solution for the unbounded knapsack problem, in contrast to popular 0/1 knapsack problem (where each item can be packed only once). Here is working code from Rosetta:

from itertools import product


NAME, SIZE, VALUE = range(3)
items = (
    # NAME, SIZE, VALUE
    ('A', 3, 5),
    ('B', 5, 9),
    ('C', 9, 16))

capacity = 13


def knapsack_unbounded_enumeration(items, C):

    # find max of any one item
    max1 = [int(C / item[SIZE]) for item in items]
    itemsizes = [item[SIZE] for item in items]
    itemvalues = [item[VALUE] for item in items]

    # def totvalue(itemscount, =itemsizes, itemvalues=itemvalues, C=C):
    def totvalue(itemscount):
        # nonlocal itemsizes, itemvalues, C

        totsize = sum(n * size for n, size in zip(itemscount, itemsizes))
        totval = sum(n * val for n, val in zip(itemscount, itemvalues))

        return (totval, -totsize) if totsize <= C else (-1, 0)

    # Try all combinations of bounty items from 0 up to max1
    bagged = max(product(*[range(n + 1) for n in max1]), key=totvalue)
    numbagged = sum(bagged)
    value, size = totvalue(bagged)
    size = -size
    # convert to (iten, count) pairs) in name order
    bagged = ['%dx%d' % (n, items[i][SIZE]) for i, n in enumerate(bagged) if n]

    return value, size, numbagged, bagged


if __name__ == '__main__':
    value, size, numbagged, bagged = knapsack_unbounded_enumeration(items, capacity)
    print(value)
    print(bagged)

Output is:

23
['1x3', '2x5']

Keep in mind that this is a NP-hard problem, so it will blow as you enter some large values :)

7 Tips for Cheap Shipping, Is it cheaper to ship one big box or two smaller ones? A packing list is required for packages that are sent to Ingram Micro warehouses. A packing list is automatically generated when you create a receiving order in your Shipwire account. The packing list should be clear and indicate exactly how many packages, cartons, and containers are arriving, and the contents of each of these.

You can use itertools.product:

import itertools
remaining_order = 13
package_numbers = [9,5,3]
required_packages = []
a=min([x for i in range(1,remaining_order+1//min(package_numbers)) for x in itertools.product(package_numbers,repeat=i)],key=lambda x: abs(sum(x)-remaining_order))
remaining_order-=sum(a)
print(a)
print(remaining_order)

Output:

(5, 5, 3)
0

This simply does the below steps:

  1. Get value closest to 13, in the list with all the product values.

  2. Then simply make it modify the number of remaining_order.

If you want it output with 'x':

import itertools
from collections import Counter
remaining_order = 13
package_numbers = [9,5,3]
required_packages = []
a=min([x for i in range(1,remaining_order+1//min(package_numbers)) for x in itertools.product(package_numbers,repeat=i)],key=lambda x: abs(sum(x)-remaining_order))
remaining_order-=sum(a)
print(' '.join(['{0}x{1}'.format(v,k) for k,v in Counter(a).items()]))
print(remaining_order)

Output:

2x5 + 1x3
0

Comprehensive Logistics, Elementary units, which are packed e.g. in order to create sales units [SU], are filling units of the first packaging stage. In the second stage, smaller packages are  Customs Forms Filling Out Customs Forms Online. You usually need a customs form to ship overseas, even with APO, FPO, and DPO mail. The form you need depends on the USPS ® mail service you’re using and the total value of what you're sending.

For you problem, I tried two implementations depending on what you want, in both of the solutions I supposed you absolutely needed your remaining to be at 0. Otherwise the algorithm will return you -1. If you need them, tell me I can adapt my algorithm.

As the algorithm is implemented via dynamic programming, it handles good inputs, at least more than 130 packages !

In the first solution, I admitted we fill with the biggest package each time. I n the second solution, I try to minimize the price, but the number of packages should always be 0.

remaining_order = 13
package_numbers = sorted([9,5,3], reverse=True) # To make sure the biggest package is the first element
prices = {9: 16, 5: 9, 3: 5}
required_packages = []

# First solution, using the biggest package each time, and making the total order remaining at 0 each time
ans = [[] for _ in range(remaining_order + 1)]
ans[0] = [0, 0, 0]
for i in range(1, remaining_order + 1):
    for index, package_number in enumerate(package_numbers):
        if i-package_number > -1:
            tmp = ans[i-package_number]
            if tmp != -1:
                ans[i] = [tmp[x] if x != index else tmp[x] + 1 for x in range(len(tmp))]
                break
    else: # Using for else instead of a boolean value `found`
        ans[i] = -1 # -1 is the not found combinations

print(ans[13]) # [0, 2, 1]
print(ans[9]) # [1, 0, 0]

# Second solution, minimizing the price with order at 0

def price(x):
    return 16*x[0]+9*x[1]+5*x[2]

ans = [[] for _ in range(remaining_order + 1)]
ans[0] = ([0, 0, 0],0) # combination + price
for i in range(1, remaining_order + 1):
    # The not found packages will be (-1, float('inf'))
    minimal_price = float('inf')
    minimal_combinations = -1
    for index, package_number in enumerate(package_numbers):
        if i-package_number > -1:
            tmp = ans[i-package_number]
            if tmp != (-1, float('inf')):
                tmp_price = price(tmp[0]) + prices[package_number] 
                if tmp_price < minimal_price:
                    minimal_price = tmp_price
                    minimal_combinations = [tmp[0][x] if x != index else tmp[0][x] + 1 for x in range(len(tmp[0]))]
    ans[i] = (minimal_combinations, minimal_price)

print(ans[13]) # ([0, 2, 1], 23)
print(ans[9]) # ([0, 0, 3], 15) Because the price of three packages is lower than the price of a package of 9

Adjustment of Postal Rates, Hearings Before 82-1, on S. 1046, , Since books comprise less than 3 percent of the total parcel post shipments, filling orders completely so that many books reach libraries in small packages, yet  Order picking is the process of finding and extracting products from a warehouse to fulfill customer orders. Since the order picking process involves significant cost and can affect customer satisfaction levels, there has been an increasing number of improvements proposed to help companies with this supply chain issue.

In case you need a solution for a small number of possible

package_numbers

but a possibly very big

remaining_order, 

in which case all the other solutions would fail, you can use this to reduce remaining_order:

import numpy as np

remaining_order = 13
package_numbers = [9,5,3]
required_packages = []
sub_max=np.sum([(np.product(package_numbers)/i-1)*i for i in package_numbers])

while remaining_order > sub_max:
    remaining_order -= np.product(package_numbers)
    required_packages.append([max(package_numbers)]*np.product(package_numbers)/max(package_numbers))

Because if any package is in required_packages more often than (np.product(package_numbers)/i-1)*i it's sum is equal to np.product(package_numbers). In case the package max(package_numbers) isn't the one with the samllest price per unit, take the one with the smallest price per unit instead.

Example:

remaining_order = 100
package_numbers = [5,3]

Any part of remaining_order bigger than 5*2 plus 3*4 = 22 can be sorted out by adding 5 three times to the solution and taking remaining_order - 5*3. So remaining order that actually needs to be calculated is 10. Which can then be solved to beeing 2 times 5. The rest is filled with 6 times 15 which is 18 times 5.

In case the number of possible package_numbers is bigger than just a handful, I recommend building a lookup table (with one of the others answers' code) for all numbers below sub_max which will make this immensely fast for any input.

Adjustment of Postal Rates: Hearings Before the Committee on Post , Since books comprise less than 3 percent of the total parcel post shipments, filling orders completely so that many books reach libraries in small packages, yet  Stick pack filling is a greener alternative to standard packaging that produces less waste and leaves a smaller carbon footprint. In fact, our cylindrical, single unit stick packs require 10–40% less packaging material than equivalent rectangular or square pouches for production.

Since no declaration about the object function is found, I assume your goal is to maximize the package value within the pack's capability.

Explanation: time complexity is fixed. Optimal solution may not be filling the highest valued item as many as possible, you have to search all possible combinations. However, you can reuse the possible optimal solutions you have searched to save space. For example, [5,5,3] is derived from adding 3 to a previous [5,5] try so the intermediate result can be "cached". You may either use an array or you may use a set to store possible solutions. The code below runs the same performance as the rosetta code but I think it's clearer.

To further optimize, use a priority set for opts.

costs = [3,5,9]
value = [5,9,16]

volume = 130

# solutions
opts = set()
opts.add(tuple([0]))

# calc total value
cost_val = dict(zip(costs, value))

def total_value(opt):
    return sum([cost_val.get(cost,0) for cost in opt])

def possible_solutions():
    solutions = set()
    for opt in opts:
        for cost in costs:
            if cost + sum(opt) > volume:
                continue
            cnt = (volume - sum(opt)) // cost
            for _ in range(1, cnt + 1):
                sol = tuple(list(opt) + [cost] * _)
                solutions.add(sol)
    return solutions

def optimize_max_return(opts):
    if not opts:
        return tuple([])
    cur = list(opts)[0]
    for sol in opts:
        if total_value(sol) > total_value(cur):
            cur = sol
    return cur

while sum(optimize_max_return(opts)) <= volume - min(costs):
    opts = opts.union(possible_solutions())

print(optimize_max_return(opts))

If your requirement is "just fill the pack" it'll be even simpler using the volume for each item instead.

Packaging and Labeling Practices, They have found it to be more economical to buy small packages and use the offering of packages which are deliberately slack filled to deceive consumers. fill your freezer with kenrick's meats! Kenrick's Meat packages are the best option for those looking to stock-up and save. With over 20 different freezer meat packages and six meat saver packages, saving on groceries has never been easier. Makes great gifts for the meat lover in your life! Meat saver packages Perfect for your everyday meal necessities, our Meat Saver Packages help you save

The Bee Keepers' Review, This smaller package was introduced to overcome the heavy transportation he can hardly depend upon buying queens in sufficient numbers to fill orders. Bulk Meat Freezer Packages The freezer packs below offer you a selection of packages for the Health Conscious up to those that have a Love of Pork (The Other White Meat™)!! All are built for a small freezer that has about 2- 3 cubic feet of free space.

Barrel and Box and Packages, Judging from the volume and size of orders now coming in, business will an increase in the number of orders that have come to hand for small boxes and this cigar _box manufacturers should be working day and night to fill orders at this  Tryptic Soy Broth (TSB), USP, 500ml bottle with needle-port septum screw cap, 300ml fill, order by the package of 10, by Hardy Diagnostics. MINIMUM ORDER: 10 packs. U435. Tryptic Soy Broth (TSB), USP, 200ml fill, 250ml square polycarbonate bottle, order by the package of 12, by Hardy Diagnostics.

The Retail Druggist, In a great many instances the mail will carry small packages, cheaper than any is given the goods and in each and every order filled a few order blanks and  For small number of objects and packages, you might be able to simply use the brute force method of trying every possibility. Beyond that, you'll need to use a heuristic of some sort. The Wikipedia article has some details, along with references to papers you probably want to check out.

Comments
  • As an aside, in Python, a for can have an else.
  • Write down how you would solve this problem yourself (how would you decide to use 2x5 + 1x3 instead of 1x9 + 1x3), then you'll have the correct algorithm....
  • What should be the ouput for remaining_order = 9 ? 3x3 or 1x9? Just to be sure before I start writting a solution
  • Can you specify boundaries? Will it be 3 package sizes max or may there be 20000? Biggest input?
  • @BlueSheepToken lowest price I think, so 9. I'd even think that combination of biggest inputs should suffice, prices don't normally favor smaller package_sizes, but op should answer that.
  • Am... link-only answers will be deleted in the low quality posts review page.
  • @U9-Forward there's a meta discussion around somewhere, this is, barely, but still, an answer because it can be reproduced even if the link goes dead -> knapsack problem, wikipedia, done.
  • I entered 130000 as remaining_order and had to kill it with fire ;), I then entered 130 and it's still not finished
  • @DonQuiKong Well... any code doing that number would have to be killed and you would actually have your window being not responding, so restart shell...
  • 130 -> MemoryError
  • @DonQuiKong That's still kinda big... can ya think of other examples, anyway, bye, i have to go.
  • Try integer factorisation with itertools next :)
  • Mmmhh... this is right, I will remove the side note for the moment, will put it back later if I find the mathematical proof :p
  • It should be 9*(5*3-1) + 5*(3*9-1) + 3*(9*5-1) because for example for 5 and 3 5*3 would be 15, but two times 5 and two times 3 is 16 which is a possible number but can't be solved with mod 15.
  • However, if any package is in there for self_value*product_of_rest, the mod takes it out. So any combination of a single package bigger than each package times product_of_all_other_package_values-1 can be ignored.