0

i've a list of items, with price and amount needed.

[ [ "Sacred Foundry", 12.69, 2 ], [ "Bloodstained Mire", 26.55, 2 ], [ "Wooded Foothills", 25.97, 2 ], [ "Verdant Catacombs", 14.54, 2 ], [ "Fury", 32.64, 4 ] ]

i would like to get all combination from items in that list for a certain amount of money

if i have 100€, i could buy 3 fury, or 2 fury and a wooded foothills and a sacred foundry ...

    def find_combinations(items, target_sum, result_type='full'):
        items = sorted(items, key=lambda x: x[1], reverse=True)
        results = []
        closest_diff = float('inf')
        closest_combination = []
    
        def backtrack(start, target_sum, combination):
            nonlocal closest_diff
            nonlocal closest_combination
    
            if target_sum < 0:
                return
            if target_sum == 0:
                results.append(combination)
                return
            if start == len(items):
                return
    
            for i in range(start, len(items)):
                for j in range(1, items[i][2] + 1):
                    if target_sum - items[i][1] * j >= 0:
                        backtrack(i + 1, target_sum - items[i][1] * j, combination + [(items[i][0], j)])
                    if abs(target_sum - items[i][1] * j) < closest_diff:
                        closest_diff = abs(target_sum - items[i][1] * j)
                        closest_combination = combination + [(items[i][0], j)]
    
        backtrack(0, target_sum, [])
        if len(results) > 0:
            results = [results[0]]
        elif len(closest_combination) > 0:
            results = [closest_combination]
        else:
            return []
    
        if result_type == 'full':
            return results
        elif result_type == 'most_items':
            return [max(results, key=lambda x: sum(map(lambda y: y[1], x)))]
        elif result_type == 'most_expensive':
            return [max(results, key=lambda x: sum(map(lambda y: y[0][1] * y[1], x)))]
        else:
            return results

it tried this, with some help from different website and chatgpt but it seems to only return one result.

thanks

2
  • Is your amount needed an upper limit for how many of each kind of item you will want to include in the combinations you want to generate? Commented Feb 1, 2023 at 8:23
  • @kakben yes it is Commented Feb 1, 2023 at 8:55

1 Answer 1

1

There are some problems with this code.

First, this part of code:

 if len(results) > 0:
     results = [results[0]]
 elif len(closest_combination) > 0:
     results = [closest_combination]
 else:
     return []

Gets the first result. So it won't print all the results for you.

Second, in your backtrack function when no new items can be added to the list it won't append the combination that it already has. You can fix this part of code with a boolean like this:

can_add_new_one = False
for i in range(start, len(items)):
        for j in range(1, items[i][2] + 1):
            if target_sum - items[i][1] * j >= 0:
                can_add_new_one = True
                backtrack(i + 1, target_sum - items[i][1] * j, combination + [(items[i][0], j)])
            if abs(target_sum - items[i][1] * j) < closest_diff:
                closest_diff = abs(target_sum - items[i][1] * j)
                closest_combination = combination + [(items[i][0], j)]
 if not can_add_new_one:
     results.append(combination)

Also, the 'most_expensive' part doesn't work. You can add items prices to the results list to be able to calculate most expensive result between all the results.

This would fix the problem with the code. The final code would be like this:

def find_combinations(items, target_sum, result_type='full'):
    items = sorted(items, key=lambda x: x[1], reverse=True)
    results = []
    closest_diff = float('inf')
    closest_combination = []

    def backtrack(start, target_sum, combination):
        nonlocal closest_diff
        nonlocal closest_combination
        nonlocal results

        if target_sum < 0:
            return 
        if target_sum == 0:
            results.append(combination)
            return
        if start == len(items):
            return

        can_add_new_one = False
        for i in range(start, len(items)):
            for j in range(1, items[i][2] + 1):
                if target_sum - items[i][1] * j >= 0:
                    can_add_new_one = True
                    backtrack(i + 1, target_sum - items[i][1] * j, combination + [(items[i][0], j, items[i][1])])
                if abs(target_sum - items[i][1] * j) < closest_diff:
                    closest_diff = abs(target_sum - items[i][1] * j)
                    closest_combination = combination + [(items[i][0], j)]
        if not can_add_new_one:
            results.append(combination)
            

    backtrack(0, target_sum, [])

    if result_type == 'full':
        return results
    elif result_type == 'most_items':
        return [max(results, key=lambda x: sum(map(lambda y: y[1], x)))]
    elif result_type == 'most_expensive':
        return [max(results, key=lambda x: sum(map(lambda y: y[2] * y[1], x)))]
    else:
        return results
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.