Python create order from smaller packages












6















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









share|improve this question

















This question has an open bounty worth +100
reputation from wasp256 ending in 5 days.


This question has not received enough attention.












  • 1





    As an aside, in Python, a for can have an else.

    – tripleee
    Jan 16 at 7:13






  • 1





    Can you specify boundaries? Will it be 3 package sizes max or may there be 20000? Biggest input?

    – DonQuiKong
    yesterday






  • 1





    @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.

    – DonQuiKong
    yesterday






  • 1





    @BlueSheepToken oh damn, you're right, the prices aren't quite logical. That .. basically reduces the possible input packages to 3 and 5 though in this case.

    – DonQuiKong
    yesterday






  • 1





    Which also means that trivially any input greater than 22 (2*5 + 4*3) can be solved by going mod 15 because necessarily there is either 3*5 or 5*3 in the solution which both is 15. Rest .. lut.

    – DonQuiKong
    yesterday
















6















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









share|improve this question

















This question has an open bounty worth +100
reputation from wasp256 ending in 5 days.


This question has not received enough attention.












  • 1





    As an aside, in Python, a for can have an else.

    – tripleee
    Jan 16 at 7:13






  • 1





    Can you specify boundaries? Will it be 3 package sizes max or may there be 20000? Biggest input?

    – DonQuiKong
    yesterday






  • 1





    @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.

    – DonQuiKong
    yesterday






  • 1





    @BlueSheepToken oh damn, you're right, the prices aren't quite logical. That .. basically reduces the possible input packages to 3 and 5 though in this case.

    – DonQuiKong
    yesterday






  • 1





    Which also means that trivially any input greater than 22 (2*5 + 4*3) can be solved by going mod 15 because necessarily there is either 3*5 or 5*3 in the solution which both is 15. Rest .. lut.

    – DonQuiKong
    yesterday














6












6








6


4






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









share|improve this question
















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






python






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Jan 16 at 7:17







wasp256

















asked Jan 16 at 6:44









wasp256wasp256

2,11643266




2,11643266






This question has an open bounty worth +100
reputation from wasp256 ending in 5 days.


This question has not received enough attention.








This question has an open bounty worth +100
reputation from wasp256 ending in 5 days.


This question has not received enough attention.










  • 1





    As an aside, in Python, a for can have an else.

    – tripleee
    Jan 16 at 7:13






  • 1





    Can you specify boundaries? Will it be 3 package sizes max or may there be 20000? Biggest input?

    – DonQuiKong
    yesterday






  • 1





    @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.

    – DonQuiKong
    yesterday






  • 1





    @BlueSheepToken oh damn, you're right, the prices aren't quite logical. That .. basically reduces the possible input packages to 3 and 5 though in this case.

    – DonQuiKong
    yesterday






  • 1





    Which also means that trivially any input greater than 22 (2*5 + 4*3) can be solved by going mod 15 because necessarily there is either 3*5 or 5*3 in the solution which both is 15. Rest .. lut.

    – DonQuiKong
    yesterday














  • 1





    As an aside, in Python, a for can have an else.

    – tripleee
    Jan 16 at 7:13






  • 1





    Can you specify boundaries? Will it be 3 package sizes max or may there be 20000? Biggest input?

    – DonQuiKong
    yesterday






  • 1





    @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.

    – DonQuiKong
    yesterday






  • 1





    @BlueSheepToken oh damn, you're right, the prices aren't quite logical. That .. basically reduces the possible input packages to 3 and 5 though in this case.

    – DonQuiKong
    yesterday






  • 1





    Which also means that trivially any input greater than 22 (2*5 + 4*3) can be solved by going mod 15 because necessarily there is either 3*5 or 5*3 in the solution which both is 15. Rest .. lut.

    – DonQuiKong
    yesterday








1




1





As an aside, in Python, a for can have an else.

– tripleee
Jan 16 at 7:13





As an aside, in Python, a for can have an else.

– tripleee
Jan 16 at 7:13




1




1





Can you specify boundaries? Will it be 3 package sizes max or may there be 20000? Biggest input?

– DonQuiKong
yesterday





Can you specify boundaries? Will it be 3 package sizes max or may there be 20000? Biggest input?

– DonQuiKong
yesterday




1




1





@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.

– DonQuiKong
yesterday





@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.

– DonQuiKong
yesterday




1




1





@BlueSheepToken oh damn, you're right, the prices aren't quite logical. That .. basically reduces the possible input packages to 3 and 5 though in this case.

– DonQuiKong
yesterday





@BlueSheepToken oh damn, you're right, the prices aren't quite logical. That .. basically reduces the possible input packages to 3 and 5 though in this case.

– DonQuiKong
yesterday




1




1





Which also means that trivially any input greater than 22 (2*5 + 4*3) can be solved by going mod 15 because necessarily there is either 3*5 or 5*3 in the solution which both is 15. Rest .. lut.

– DonQuiKong
yesterday





Which also means that trivially any input greater than 22 (2*5 + 4*3) can be solved by going mod 15 because necessarily there is either 3*5 or 5*3 in the solution which both is 15. Rest .. lut.

– DonQuiKong
yesterday












5 Answers
5






active

oldest

votes


















3














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 :)






share|improve this answer





















  • 1





    Am... link-only answers will be deleted in the low quality posts review page.

    – U9-Forward
    yesterday






  • 1





    @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.

    – DonQuiKong
    yesterday



















2














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.







share|improve this answer





















  • 2





    I entered 130000 as remaining_order and had to kill it with fire ;), I then entered 130 and it's still not finished

    – DonQuiKong
    yesterday













  • @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...

    – U9-Forward
    yesterday






  • 1





    130 -> MemoryError

    – DonQuiKong
    yesterday











  • @DonQuiKong That's still kinda big... can ya think of other examples, anyway, bye, i have to go.

    – U9-Forward
    yesterday






  • 1





    Try integer factorisation with itertools next :)

    – Dima Tisnek
    yesterday



















1














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





share|improve this answer


























  • 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

    – BlueSheepToken
    yesterday











  • 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.

    – DonQuiKong
    yesterday











  • 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.

    – DonQuiKong
    yesterday



















1














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.






share|improve this answer

































    1














    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.






    share|improve this answer

























      Your Answer






      StackExchange.ifUsing("editor", function () {
      StackExchange.using("externalEditor", function () {
      StackExchange.using("snippets", function () {
      StackExchange.snippets.init();
      });
      });
      }, "code-snippets");

      StackExchange.ready(function() {
      var channelOptions = {
      tags: "".split(" "),
      id: "1"
      };
      initTagRenderer("".split(" "), "".split(" "), channelOptions);

      StackExchange.using("externalEditor", function() {
      // Have to fire editor after snippets, if snippets enabled
      if (StackExchange.settings.snippets.snippetsEnabled) {
      StackExchange.using("snippets", function() {
      createEditor();
      });
      }
      else {
      createEditor();
      }
      });

      function createEditor() {
      StackExchange.prepareEditor({
      heartbeatType: 'answer',
      autoActivateHeartbeat: false,
      convertImagesToLinks: true,
      noModals: true,
      showLowRepImageUploadWarning: true,
      reputationToPostImages: 10,
      bindNavPrevention: true,
      postfix: "",
      imageUploader: {
      brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
      contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
      allowUrls: true
      },
      onDemand: true,
      discardSelector: ".discard-answer"
      ,immediatelyShowMarkdownHelp:true
      });


      }
      });














      draft saved

      draft discarded


















      StackExchange.ready(
      function () {
      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54211693%2fpython-create-order-from-smaller-packages%23new-answer', 'question_page');
      }
      );

      Post as a guest















      Required, but never shown

























      5 Answers
      5






      active

      oldest

      votes








      5 Answers
      5






      active

      oldest

      votes









      active

      oldest

      votes






      active

      oldest

      votes









      3














      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 :)






      share|improve this answer





















      • 1





        Am... link-only answers will be deleted in the low quality posts review page.

        – U9-Forward
        yesterday






      • 1





        @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.

        – DonQuiKong
        yesterday
















      3














      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 :)






      share|improve this answer





















      • 1





        Am... link-only answers will be deleted in the low quality posts review page.

        – U9-Forward
        yesterday






      • 1





        @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.

        – DonQuiKong
        yesterday














      3












      3








      3







      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 :)






      share|improve this answer















      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 :)







      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited yesterday

























      answered yesterday









      Dmytro PrylipkoDmytro Prylipko

      890618




      890618








      • 1





        Am... link-only answers will be deleted in the low quality posts review page.

        – U9-Forward
        yesterday






      • 1





        @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.

        – DonQuiKong
        yesterday














      • 1





        Am... link-only answers will be deleted in the low quality posts review page.

        – U9-Forward
        yesterday






      • 1





        @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.

        – DonQuiKong
        yesterday








      1




      1





      Am... link-only answers will be deleted in the low quality posts review page.

      – U9-Forward
      yesterday





      Am... link-only answers will be deleted in the low quality posts review page.

      – U9-Forward
      yesterday




      1




      1





      @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.

      – DonQuiKong
      yesterday





      @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.

      – DonQuiKong
      yesterday













      2














      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.







      share|improve this answer





















      • 2





        I entered 130000 as remaining_order and had to kill it with fire ;), I then entered 130 and it's still not finished

        – DonQuiKong
        yesterday













      • @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...

        – U9-Forward
        yesterday






      • 1





        130 -> MemoryError

        – DonQuiKong
        yesterday











      • @DonQuiKong That's still kinda big... can ya think of other examples, anyway, bye, i have to go.

        – U9-Forward
        yesterday






      • 1





        Try integer factorisation with itertools next :)

        – Dima Tisnek
        yesterday
















      2














      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.







      share|improve this answer





















      • 2





        I entered 130000 as remaining_order and had to kill it with fire ;), I then entered 130 and it's still not finished

        – DonQuiKong
        yesterday













      • @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...

        – U9-Forward
        yesterday






      • 1





        130 -> MemoryError

        – DonQuiKong
        yesterday











      • @DonQuiKong That's still kinda big... can ya think of other examples, anyway, bye, i have to go.

        – U9-Forward
        yesterday






      • 1





        Try integer factorisation with itertools next :)

        – Dima Tisnek
        yesterday














      2












      2








      2







      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.







      share|improve this answer















      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.








      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited yesterday

























      answered yesterday









      U9-ForwardU9-Forward

      14.2k21337




      14.2k21337








      • 2





        I entered 130000 as remaining_order and had to kill it with fire ;), I then entered 130 and it's still not finished

        – DonQuiKong
        yesterday













      • @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...

        – U9-Forward
        yesterday






      • 1





        130 -> MemoryError

        – DonQuiKong
        yesterday











      • @DonQuiKong That's still kinda big... can ya think of other examples, anyway, bye, i have to go.

        – U9-Forward
        yesterday






      • 1





        Try integer factorisation with itertools next :)

        – Dima Tisnek
        yesterday














      • 2





        I entered 130000 as remaining_order and had to kill it with fire ;), I then entered 130 and it's still not finished

        – DonQuiKong
        yesterday













      • @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...

        – U9-Forward
        yesterday






      • 1





        130 -> MemoryError

        – DonQuiKong
        yesterday











      • @DonQuiKong That's still kinda big... can ya think of other examples, anyway, bye, i have to go.

        – U9-Forward
        yesterday






      • 1





        Try integer factorisation with itertools next :)

        – Dima Tisnek
        yesterday








      2




      2





      I entered 130000 as remaining_order and had to kill it with fire ;), I then entered 130 and it's still not finished

      – DonQuiKong
      yesterday







      I entered 130000 as remaining_order and had to kill it with fire ;), I then entered 130 and it's still not finished

      – DonQuiKong
      yesterday















      @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...

      – U9-Forward
      yesterday





      @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...

      – U9-Forward
      yesterday




      1




      1





      130 -> MemoryError

      – DonQuiKong
      yesterday





      130 -> MemoryError

      – DonQuiKong
      yesterday













      @DonQuiKong That's still kinda big... can ya think of other examples, anyway, bye, i have to go.

      – U9-Forward
      yesterday





      @DonQuiKong That's still kinda big... can ya think of other examples, anyway, bye, i have to go.

      – U9-Forward
      yesterday




      1




      1





      Try integer factorisation with itertools next :)

      – Dima Tisnek
      yesterday





      Try integer factorisation with itertools next :)

      – Dima Tisnek
      yesterday











      1














      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





      share|improve this answer


























      • 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

        – BlueSheepToken
        yesterday











      • 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.

        – DonQuiKong
        yesterday











      • 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.

        – DonQuiKong
        yesterday
















      1














      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





      share|improve this answer


























      • 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

        – BlueSheepToken
        yesterday











      • 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.

        – DonQuiKong
        yesterday











      • 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.

        – DonQuiKong
        yesterday














      1












      1








      1







      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





      share|improve this answer















      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






      share|improve this answer














      share|improve this answer



      share|improve this answer








      edited yesterday

























      answered yesterday









      BlueSheepTokenBlueSheepToken

      1,066315




      1,066315













      • 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

        – BlueSheepToken
        yesterday











      • 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.

        – DonQuiKong
        yesterday











      • 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.

        – DonQuiKong
        yesterday



















      • 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

        – BlueSheepToken
        yesterday











      • 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.

        – DonQuiKong
        yesterday











      • 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.

        – DonQuiKong
        yesterday

















      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

      – BlueSheepToken
      yesterday





      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

      – BlueSheepToken
      yesterday













      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.

      – DonQuiKong
      yesterday





      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.

      – DonQuiKong
      yesterday













      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.

      – DonQuiKong
      yesterday





      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.

      – DonQuiKong
      yesterday











      1














      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.






      share|improve this answer






























        1














        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.






        share|improve this answer




























          1












          1








          1







          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.






          share|improve this answer















          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.







          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited yesterday

























          answered yesterday









          DonQuiKongDonQuiKong

          200112




          200112























              1














              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.






              share|improve this answer






























                1














                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.






                share|improve this answer




























                  1












                  1








                  1







                  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.






                  share|improve this answer















                  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.







                  share|improve this answer














                  share|improve this answer



                  share|improve this answer








                  edited yesterday

























                  answered yesterday









                  knh190knh190

                  18412




                  18412






























                      draft saved

                      draft discarded




















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid



                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.


                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function () {
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f54211693%2fpython-create-order-from-smaller-packages%23new-answer', 'question_page');
                      }
                      );

                      Post as a guest















                      Required, but never shown





















































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown

































                      Required, but never shown














                      Required, but never shown












                      Required, but never shown







                      Required, but never shown







                      Popular posts from this blog

                      Liquibase includeAll doesn't find base path

                      How to use setInterval in EJS file?

                      Petrus Granier-Deferre