Open In App

Fractional Knapsack in Python

Last Updated : 12 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Given the weights and profits of N items, in the form of {profit, weight} put these items in a knapsack of capacity W to get the maximum total profit in the knapsack.

In Fractional Knapsack, we can break items for maximizing the total value of the knapsack.

Examples:

Input: arr[] = {{60, 10}, {100, 20}, {120, 30}}, W = 50
Output: 240 
Explanation: By taking items of weight 10 and 20 kg and 2/3 fraction of 30 kg. 
Hence total price will be 60+100+(2/3)(120) = 240

Input:  arr[] = {{500, 30}}, W = 10
Output: 166.667

Naive Approach:

To solve the problem follow the below idea:

The naive approach to solve this problem is to consider all possible fractions of items and choose the one which gives the maximum value and also doesn’t exceed the weight capacity of the knapsack.

Steps:

  1. Calculate the ratio value/weight for each item.
  2. Sort all the items based on this ratio in non-increasing order.
  3. Initialize total_value = 0 and current_weight = 0.
  4. Loop through all the items and:
    • If adding the entire item is possible, add it to the knapsack and update total_value and current_weight.
    • Otherwise, add the fraction of the item to the knapsack and update total_value and current_weight.
  5. Return total_value as the maximum value in the knapsack.

Code

Python3
def fractional_knapsack(values, weights, W):
    n = len(values)
    
    # Calculate value/weight ratio for each item
    ratios = [(values[i] / weights[i], values[i], weights[i]) for i in range(n)]
    
    # Sort items based on ratio in non-increasing order
    ratios.sort(reverse=True)
    
    total_value = 0  # Initialize total value
    current_weight = 0  # Initialize current weight
    
    # Loop through all items
    for ratio, value, weight in ratios:
        if current_weight + weight <= W:
            # Add entire item
            total_value += value
            current_weight += weight
        else:
            # Add fraction of item
            fraction = (W - current_weight) / weight
            total_value += value * fraction
            break
    
    return total_value


# Example Usage
values1 = [60, 100, 120]
weights1 = [10, 20, 30]
W1 = 50
print("Maximum value in knapsack =", fractional_knapsack(values1, weights1, W1))  # Output: 240

values2 = [40, 100, 50, 60]
weights2 = [20, 10, 40, 30]
W2 = 50
print("Maximum value in knapsack =", fractional_knapsack(values2, weights2, W2))  # Output: 210

Output
Maximum value in knapsack = 240.0
Maximum value in knapsack = 180.0

Time Complexity: O(2N)
Auxiliary Space: O(N)

Fractional Knapsack Problem using Greedy algorithm:

An efficient solution is to use the Greedy approach. 

The basic idea of the greedy approach is to calculate the ratio profit/weight for each item and sort the item on the basis of this ratio. Then take the item with the highest ratio and add them as much as we can (can be the whole element or a fraction of it).

This will always give the maximum profit because, in each step it adds an element such that this is the maximum possible profit for that much weight.

Illustration:

Check the below illustration for a better understanding:

Consider the example: arr[] = {{100, 20}, {60, 10}, {120, 30}}, W = 50.

Sorting: Initially sort the array based on the profit/weight ratio. The sorted array will be {{60, 10}, {100, 20}, {120, 30}}.

Iteration:

  • For i = 0, weight = 10 which is less than W. So add this element in the knapsack. profit = 60 and remaining W = 50 – 10 = 40.
  • For i = 1, weight = 20 which is less than W. So add this element too. profit = 60 + 100 = 160 and remaining W = 40 – 20 = 20.
  • For i = 2, weight = 30 is greater than W. So add 20/30 fraction = 2/3 fraction of the element. Therefore profit = 2/3 * 120 + 160 = 80 + 160 = 240 and remaining W becomes 0.

So the final profit becomes 240 for W = 50.

Steps:

  1. Calculate the ratio value/weight for each item.
  2. Sort all the items based on this ratio in non-increasing order.
  3. Initialize total_value = 0 and current_weight = 0.
  4. Loop through all the items and:
    • If adding the entire item is possible, add it to the knapsack and update total_value and current_weight.
    • Otherwise, add the fraction of the item to the knapsack and update total_value and current_weight.
  5. Return total_value as the maximum value in the knapsack.

Below is the implementation of the above approach:

Python3
def greedy_fractional_knapsack(values, weights, W):
    n = len(values)
    
    # Calculate value/weight ratio for each item
    ratios = [(values[i] / weights[i], values[i], weights[i]) for i in range(n)]
    
    # Sort items based on ratio in non-increasing order
    ratios.sort(reverse=True)
    
    total_value = 0  # Initialize total value
    current_weight = 0  # Initialize current weight
    
    # Loop through all items
    for ratio, value, weight in ratios:
        if current_weight + weight <= W:
            # Add entire item
            total_value += value
            current_weight += weight
        else:
            # Add fraction of item
            fraction = (W - current_weight) / weight
            total_value += value * fraction
            break
    
    return total_value


# Example Usage
values1 = [60, 100, 120]
weights1 = [10, 20, 30]
W1 = 50
print("Maximum value in knapsack (Greedy Approach) =", greedy_fractional_knapsack(values1, weights1, W1))  # Output: 240

values2 = [40, 100, 50, 60]
weights2 = [20, 10, 40, 30]
W2 = 50
print("Maximum value in knapsack (Greedy Approach) =", greedy_fractional_knapsack(values2, weights2, W2))  # Output: 210

Output
Maximum value in knapsack (Greedy Approach) = 240.0
Maximum value in knapsack (Greedy Approach) = 180.0

Time Complexity: O(N * logN)
Auxiliary Space: O(N)



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads