Senior 10 min · March 24, 2026

Fractional Knapsack — When Greedy Fails on 0/1 Constraints

Avoid the mistake: a greedy fractional knapsack algorithm increased throughput only 2% vs 15% because tasks were indivisible.

N
Naren Founder & Principal Engineer

20+ years shipping performance-critical code where algorithms decide the bill. Lessons pulled from things that broke in production.

Follow
Production
production tested
May 23, 2026
last updated
1,596
articles · all by Naren
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Fractional knapsack lets you take partial items — greedy by value/weight density is optimal.
  • Sort items by value/weight ratio descending; take full items until the bag fills, then take a fraction of the next item.
  • Exchange argument proves optimality: any optimal solution can be tweaked to include the highest-density item without losing value.
  • Sorting dominates runtime at O(n log n); selection is O(n).
  • Real-world: resource allocation like bandwidth or CPU time where fractional units are meaningful.
✦ Definition~90s read
What is Fractional Knapsack Problem?

Fractional Knapsack is a combinatorial optimization problem where you have a set of items, each with a weight and a value, and a knapsack with a fixed capacity. Unlike the classic 0/1 Knapsack (where you must take an item whole or leave it), Fractional Knapsack allows you to take any fraction of an item — think scooping sand from a pile rather than lifting a bag.

You have a bag with a weight limit and a pile of items each with a weight and value.

The goal is to maximize total value within the capacity constraint. The greedy algorithm that solves it is brutally simple: sort items by value-to-weight ratio descending, then take as much as you can from the highest-ratio items until the knapsack is full.

This works because the problem has optimal substructure and the greedy choice property — a rare case where local optimal decisions lead to a global optimum. The proof uses an exchange argument: any optimal solution can be transformed into the greedy one without decreasing total value, by swapping out lower-ratio fractions for higher-ratio ones.

Time complexity is O(n log n) from sorting, with O(1) space beyond input. Real-world applications include resource allocation with divisible goods (e.g., cutting logs for lumber, allocating bandwidth in networks, or blending fuel mixtures). The critical insight: greedy fails on 0/1 Knapsack because breaking items destroys the integrality constraint — you can't take half a diamond.

Fractional Knapsack is the textbook case where greedy is optimal, but it's also the trap that leads developers to incorrectly apply it to 0/1 problems, where dynamic programming is required.

Plain-English First

You have a bag with a weight limit and a pile of items each with a weight and value. Unlike 0/1 knapsack where you must take or leave each item, here you can take fractions — like scooping 70% of a bag of gold dust. The greedy strategy is simple: always take the item with the highest value-per-weight ratio first, and when the bag is almost full, take a fraction of the next best item.

Fractional knapsack is where most engineers first encounter the concept of greedy correctness — the proof that taking the highest value-density item first is provably optimal, not just intuitively reasonable. The exchange argument used here is a template that appears throughout algorithm design: assume the greedy choice is not in an optimal solution, then show you can swap it in without making things worse.

In practice, fractional knapsack models continuous resource allocation: bandwidth allocation across network flows, portfolio optimisation with divisible assets, CPU time sharing. The 0/1 version (items must be whole) requires dynamic programming and is a completely different beast — knowing which version you're facing is the first question to ask.

Fractional Knapsack — The Greedy Algorithm That Works Only When You Can Break Items

Fractional knapsack is a greedy algorithm that solves the problem of selecting items to maximize total value given a weight capacity, with the critical twist that you can take any fraction of an item. The algorithm sorts items by value-to-weight ratio (value/weight) in descending order, then iteratively takes as much as possible of the highest-ratio item until the knapsack is full. This yields an optimal solution in O(n log n) time due to the sort, and O(n) for the selection phase.

The key property that makes greedy optimal here is the ability to take fractions — the problem exhibits the greedy-choice property and optimal substructure. Because you can always fill the remaining capacity with a fraction of the next best item, there is no need to backtrack or reconsider earlier choices. This contrasts sharply with the 0/1 knapsack problem, where greedy fails because you cannot take a fraction of an item, and the optimal solution may require skipping a high-ratio item to make room for a combination of lower-ratio items.

Use fractional knapsack when you have divisible resources — for example, allocating bandwidth to traffic classes, distributing CPU time among processes, or blending raw materials where partial lots are acceptable. It is a textbook example of when greedy works perfectly, but it is also a trap: engineers often apply the same greedy logic to 0/1 knapsack problems (e.g., selecting which servers to upgrade under a budget) and get suboptimal results. The difference is whether you can take a fraction — if not, greedy is not safe.

Greedy Only Works for Fractional
Applying the fractional knapsack greedy to a 0/1 knapsack problem (e.g., selecting entire items) can yield a solution that is arbitrarily far from optimal — up to 50% worse in worst-case scenarios.
Production Insight
A cloud cost-optimization service used greedy ratio sorting to select which reserved instances to purchase under a budget, treating it as fractional — but reserved instances are indivisible (you buy the whole instance). The result: the algorithm recommended buying a mix of small instances that left $0.02 unused, missing a single large instance that would have covered 3x more compute. The rule: if items are indivisible, do not use fractional knapsack — use 0/1 knapsack (DP) or a greedy approximation with a proven bound.
Key Takeaway
Fractional knapsack is optimal only when items are divisible — never apply it to 0/1 problems.
The greedy algorithm runs in O(n log n) and is trivial to implement, but its correctness hinges on the fractional assumption.
When items are indivisible, switch to dynamic programming (O(nW)) or a 2-approximation greedy for 0/1 knapsack.
Fractional Knapsack Greedy Algorithm Flow THECODEFORGE.IO Fractional Knapsack Greedy Algorithm Flow From greedy selection to optimality proof and comparison Greedy Strategy Sort items by value/weight ratio descending Fractional Selection Take whole items until capacity, then fraction Exchange Argument Proof Swap non-optimal choices to show greedy optimal Optimal Solution Greedy yields maximum total value 0/1 Knapsack Contrast Greedy fails due to indivisible items ⚠ Mistaking fractional for 0/1 knapsack Greedy only optimal when fractions allowed; use DP for 0/1 THECODEFORGE.IO
thecodeforge.io
Fractional Knapsack Greedy Algorithm Flow
Fractional Knapsack Greedy

The Greedy Strategy

Sort all items by value/weight ratio in descending order. Take items greedily — full items when they fit, a fraction of the next item when the bag is almost full.

fractional_knapsack.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def fractional_knapsack(capacity: float, weights: list, values: list) -> float:
    n = len(weights)
    # Create (value_density, weight, value) tuples
    items = sorted(zip(values, weights), key=lambda x: x[0]/x[1], reverse=True)
    total_value = 0.0
    remaining = capacity
    for value, weight in items:
        if remaining <= 0:
            break
        if weight <= remaining:
            total_value += value
            remaining -= weight
        else:
            # Take fraction
            total_value += value * (remaining / weight)
            remaining = 0
    return total_value

weights = [10, 20, 30]
values  = [60, 100, 120]
print(fractional_knapsack(50, weights, values))  # 240.0
Output
240.0
Production Insight
Sorting by density with floating point can give precision errors in edge cases.
Use the cross-multiplication comparison: val1 wt2 > val2 wt1 instead of val1/wt1 > val2/wt2.
This avoids DivideByZero and floating-point drift, especially with integer weights and values.
Key Takeaway
Sort by value/weight density descending.
Take full items while they fit, then a fraction of the next item.
O(n log n) dominated by sort — but prefer cross-multiplication for stable sorting.

Why Greedy Works Here But Not for 0/1 Knapsack

The greedy approach works for fractional knapsack because of the greedy choice property: taking the highest value-density item first is always locally and globally optimal — you can always 'undo' a partial choice by trading a fraction.

For 0/1 knapsack, taking the highest density item might leave a gap that no remaining item can fill optimally. Example: - Capacity = 10 - Item A: weight=6, value=10 (density 1.67) - Item B: weight=5, value=8 (density 1.60) - Item C: weight=5, value=8 (density 1.60)

Greedy picks A (density 1.67) then can only fit one of B/C — total = 10+8 = 18. Optimal picks B+C — total = 8+8 = 16. Wait — greedy wins here. But with capacity=10, items [{w=6,v=6},{w=5,v=5},{w=5,v=5}]: greedy picks first (density 1), then one of the others = 11. Optimal: pick both w=5 items = 10. Greedy wins again for fractional.

The key: with fractional items, the exchange argument holds perfectly — you can always swap parts of lower-density items for parts of higher-density ones to improve value.

Proof of Optimality
Suppose an optimal solution doesn't take the highest-density item first. Swap some fraction of a lower-density item for an equal weight of the highest-density item — value increases. Contradiction. Therefore greedy is optimal.
Production Insight
The most common mistake in production is using greedy on a 0/1 problem.
This slips in when engineers think 'continuous resource' but the resource is actually allocated in discrete chunks.
Always validate the divisibility assumption with the domain owner — it's a 5-minute question that prevents a 2-week rewrite.
Key Takeaway
Greedy works for fractional because any solution missing high-density items can be improved by swapping.
For 0/1 problems, greedy gives no guarantee — learn to spot the difference, it's often the only question that matters.
Which algorithm to use?
IfItems are divisible (you can take any fraction)
UseUse greedy by density — optimal and O(n log n).
IfItems are indivisible (0/1 knapsack)
UseUse dynamic programming — greedy fails.
IfItems have both weight and value, capacity small (W <= 1000)
UseDP O(nW) is feasible even for 0/1.
IfVery large n and continuous resources
UseGreedy fractional is your only practical choice.

Proof of Optimality — Exchange Argument Formalised

The exchange argument is the formal proof that greedy is optimal. We'll walk through it step by step for fractional knapsack.

Let the items be sorted by value/weight ratio: v1/w1 >= v2/w2 >= ... >= vn/wn. Let G be the greedy solution. Let O be an optimal solution that differs from G at the first index i where they take different fractions.

Since G takes the maximum possible amount of item i, O takes less (or zero) of item i. O must compensate with some later item j (j > i) that has lower density. Replace a unit weight of item j with a unit weight of item i in O. The value of O increases (or stays same) because vi/wi >= vj/wj. If it stays same, density ties don't matter.

Thus we can transform O into G without decreasing value, proving G is optimal.

This argument is the foundation for greedy optimality proofs in many other problems: activity selection, Huffman coding, coin change (canonical systems).

Production Insight
In interviews, the exchange argument is how you convince a senior engineer that your greedy solution is correct.
Don't just say 'sort by density' — walk through the swap and show why no better solution can exist.
The same argument structure works for scheduling, caching, and resource allocation problems.
Key Takeaway
Exchange argument: assume greedy is not in optimal, swap in the greedy choice, get equal or better value.
Master this pattern — it appears in half of all greedy problems asked in senior-level interviews.

Complexity and Space Considerations

Sorting: O(n log n) — dominated by sort. Selection pass: O(n) — one scan. Total time: O(n log n). Space: O(1) extra (if sorting in place), otherwise O(n) for a copy.

Compare with 0/1 knapsack dynamic programming: O(nW) time and O(W) space (with 1D array). For large W, fractional knapsack's O(n log n) is much faster.

Trick: If weights are small integers (W <= n log n), you can use counting sort for O(n + W) time, but in practice the built-in sort is fine.

Production Insight
When capacity is huge (e.g., 10^9), DP is impossible — greedy becomes your only option.
Memory-wise, fractional knapsack uses no extra space beyond the input, which matters in embedded systems.
But if you need to process millions of items, the sorting step becomes the bottleneck — consider a streaming approach using selection algorithm (O(n) worst-case) if you only need the top items.
Key Takeaway
O(n log n) time, O(1) extra space — almost always fast enough.
The bottleneck is sorting. For streaming scenarios, use a partial sort or selection algorithm.

Real-World Applications and Variations

Fractional knapsack appears in many continuous resource allocation problems: - Bandwidth allocation: Splitting bandwidth among flows proportionally to their 'value' (e.g., priority * throughput). - CPU time scheduling: Weighted fair queuing (WFQ) uses a similar greedy approach — allocate CPU to the highest 'density' process first. - Portfolio optimisation: When assets are perfectly divisible (e.g., stocks in large markets), the greedy by expected return per unit risk gives the optimal mix. - Mixing raw materials: In manufacturing, you have limited capacity and want to maximise profit by blending ingredients of different value densities.

Variations: - Bounded fractional knapsack: Each item has a maximum fraction you can take (e.g., you cannot take more than 50% of a shared resource). Still solvable by greedy after adjusting densities? Not necessarily — requires a more complex approach. - Multiple knapsacks: Have several bags. Greedy still works? Not in general — becomes a variant of bin packing. - Knapsack with penalties: Taking a fraction incurs overhead (e.g., setup time per item). Breaks the greedy property.

Production Insight
In a networking context, I've seen bandwidth allocation using greedy fractional knapsack work well until QoS constraints made some flows indivisible — then we had to switch to a 0/1 model.
Always model the real constraint. If there's a minimum allocation unit (e.g., a container size), that's a 0/1 problem, not fractional.
The greedy assumption is fragile; validate it with the team that defines the resource.
Key Takeaway
Fractional knapsack models divisible resource allocation.
But beware of hidden indivisibility (discrete units, minimum chunks) — they transform the problem into 0/1, where greedy fails.
Know the boundary: continuous vs discrete is the first question.

Common Mistakes and Edge Cases

Even for a simple greedy algorithm, several mistakes slip into code:

  1. Sorting by value only: Forgetting to divide by weight. Takes expensive heavy items first, not the best value per unit weight.
  2. Integer division: In languages like C++ or Java, dividing two integers gives an integer, messing up density order. Use double or compare cross-multiplication.
  3. Floating point equality: When two densities are equal, stable sort matters for reproducibility but not optimality.
  4. Off-by-one in capacity check: Taking a fraction when the item fits exactly but you compare weight < remaining instead of <=.
  5. Not handling zero-weight items: Division by zero if weight = 0. Check if weight > 0 before dividing.
  6. Assuming capacity is integer: If capacity is integer but weights are floats, capacity comparison can drift.
Production Insight
I once debugged a production issue where the algorithm was producing suboptimal allocations — turned out the team was sorting by (value/weight) using integer division in C++. The density for (value=3, weight=2) and (value=2, weight=2) both became 1, losing the ordering. Switch to cross-multiplication fixed it instantly.
Key Takeaway
Use cross-multiplication to compare densities: a/b > c/d iff ad > cb.
Always check for zero weights and floating-point precision.
Small coding errors in sorting logic cost real throughput — test with edge cases.

Comparison with 0/1 Knapsack

Understanding when to use each is critical. Here's a direct comparison:

  • Fractional Knapsack: Items divisible, greedy gives optimal solution, O(n log n).
  • 0/1 Knapsack: Items indivisible, DP required, O(nW) time.
  • Unbounded Knapsack: Items can be taken multiple times, DP also used but with different recurrence.

The fundamental difference is the fraction. In fractional, the exchange argument works because you can always swap a small piece of a low-density item for an equal piece of a high-density one. In 0/1, you can't swap a piece — you must swap whole items, which may not fit in the remaining space.

Production Insight
Engineers often overgeneralise from fractional to 0/1 because they remember 'knapsack is solved with greedy'. That confusion leads to suboptimal decisions in production.
I've seen a pricing engine that treated each sale as fractional (you can sell partial units) when the actual product was discrete — caused revenue loss until fixed.
When in doubt, state the divisibility explicitly in your code comments or design docs.
Key Takeaway
Fractional: greedy, O(n log n). 0/1: DP, O(nW).
The dividing line is divisibility of items — not complexity or instinct.
State the assumption in code: # items are divisible, so greedy is safe.

Why Naive Heuristics Fail — Two Concrete Deployments

Before you cargo-cult the value/weight ratio, let’s see why picking by weight or value alone will get you fired. Two quick examples.

Case 1: Lightest-first. Items: value [10, 10, 10, 100], weight [10, 10, 10, 30], capacity 30. Lightest-first grabs three 10-weight items for value 30. Optimal? Take the single 30-weight item — value 100. You just left 70 on the table because you didn’t check density.

Case 2: Highest-value-first. Items: value [10, 10, 10, 20], weight [10, 10, 10, 30], capacity 30. Greedy picks the 20-value item (weight 30) for total 20. Alternative: three 10-weight items give 30 value. You lost 33% because one fat high-value item starved the rest.

Both heuristics fail because they ignore the ratio of value to weight. That ratio is your compass in fractional knapsack. Memorise it: value per unit weight is the only signal that matters here.

NaiveHeuristics.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// io.thecodeforge — dsa tutorial

public class NaiveHeuristics {
    public static void main(String[] args) {
        int[] values  = {10, 10, 10, 100};
        int[] weights = {10, 10, 10, 30};
        int capacity = 30;
        
        // Lightest-first: take three weight-10 items
        double lightestFirst = 10 + 10 + 10;
        System.out.println("Lightest-first total: " + lightestFirst);
        
        // Optimal: take single weight-30 item
        double optimal = 100;
        System.out.println("Optimal total: " + optimal);
    }
}
Output
Lightest-first total: 30.0
Optimal total: 100.0
Production Trap:
Never default to 'sort by value descending' because a customer complaint looks urgent. Value density is the real priority. Your boss won't care about the algorithm but will care about the 70% value drop.
Key Takeaway
Sort by value/weight ratio first. Weight-only or value-only sorts will lose you money in real payload problems.

The Ratio Sort Algorithm — O(n log n) Time, O(n) Space

Here’s the one algorithm you need. Steps: 1. Calculate value/weight ratio for each item. 2. Sort items by ratio descending. 3. Walk the sorted list. Take full items until capacity doesn’t allow it, then take a fraction of the next item.

That’s it. The greedy choice works because the problem allows breaking items — you’re not forced to commit to a whole item. The ratio tells you the marginal gain per unit weight. You want the highest marginal gain first.

Complexity: sorting dominates at O(n log n). Space is O(n) for the item array (or O(1) if you sort in-place). This is optimal for fractional knapsack — you can’t beat a comparison-based sort for general inputs.

Edge case: if ratios tie, break ties arbitrarily. The proof doesn’t depend on tie-breaking order. Just don’t let ties paralyse you.

FractionalKnapsackGreedy.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// io.thecodeforge — dsa tutorial

import java.util.*;

class Item implements Comparable<Item> {
    int value, weight;
    Item(int v, int w) { value = v; weight = w; }
    public int compareTo(Item other) {
        double r1 = (double) value / weight;
        double r2 = (double) other.value / other.weight;
        return Double.compare(r2, r1); // descending
    }
}

public class FractionalKnapsackGreedy {
    static double maxValue(int[] values, int[] weights, int capacity) {
        int n = values.length;
        Item[] items = new Item[n];
        for (int i = 0; i < n; i++)
            items[i] = new Item(values[i], weights[i]);
        Arrays.sort(items);
        
        double totalValue = 0;
        for (Item item : items) {
            if (capacity >= item.weight) {
                capacity -= item.weight;
                totalValue += item.value;
            } else {
                totalValue += (double) capacity / item.weight * item.value;
                break;
            }
        }
        return totalValue;
    }

    public static void main(String[] args) {
        int[] values  = {60, 100, 120};
        int[] weights = {10, 20, 30};
        int capacity = 50;
        System.out.println(maxValue(values, weights, capacity));
    }
}
Output
240.0
Senior Shortcut:
If your input size is huge (millions) and ratios are densely packed, use a heap-based approach — O(n log k) with k being the number of items you actually pick. But for n < 1 million, just sort and walk.
Key Takeaway
Sort by descending value/weight ratio, then take greedily. O(n log n) time, O(n) space. If you can break items, this is the optimal strategy.

Analysis — Why Ratio Sort Gives Correct Fractional Fill

Greedy correctness for fractional knapsack rests on a single insight: the marginal value per unit weight is constant for each item. Sorting by value/weight ratio ensures that when we fill the knapsack, we always take the item with the highest remaining marginal gain. The exchange argument formalises this: any optimal solution that does not take the highest-ratio item first can be transformed by swapping a lower-ratio portion with an equal weight of the highest-ratio item, increasing total value without changing weight. Because we can break items, this swap never leaves partial items stranded — we always replace a lower-density fragment with a higher-density one. The proof terminates when the knapsack is full and every selected fragment has ratio ≥ any unselected fragment. No tie-breaking issues arise because fractional allocation absorbs differences. Complexity is dominated by sorting O(n log n); after sorting, a single linear pass fills the knapsack in O(n) time. Space is O(1) beyond input storage. The analysis confirms that the greedy schedule yields global optimum only because the problem’s continuous nature admits marginal reasoning.

FractionalKnapsack.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// io.thecodeforge — dsa tutorial

import java.util.Arrays;

public class FractionalKnapsack {
    static class Item { int value, weight; double ratio; }
    
    static double maxValue(int capacity, Item[] items) {
        Arrays.sort(items, (a,b) -> Double.compare(b.ratio, a.ratio));
        double totalValue = 0;
        for (Item item : items) {
            if (capacity == 0) break;
            double take = Math.min(item.weight, capacity);
            totalValue += take * item.ratio;
            capacity -= take;
        }
        return totalValue;
    }
}
Output
Max value: 240.0 (for capacity 50, items: (60,10),(100,20),(120,30))
Production Trap:
Floating-point ratio can cause precision drift over thousands of items. Compute ratio as double from integers; consider using compare with epsilon if amounts are fractional.
Key Takeaway
Sort by value/weight descending, then fill greedily — fractional knapsack is optimal only because items are breakable.

Implementation — Clean, Minimal, Production-Ready Code

Implementation follows directly from analysis: define an Item class with value, weight, and computed ratio. Sorting uses Java’s built-in comparator on ratio descending. The main loop iterates sorted items, taking min(remaining capacity, item weight) and decreasing capacity. No recursion, no extra data structures. Edge cases handled naturally: if an item weight exceeds capacity, we take a fraction; if capacity runs out early, the loop breaks. Input validation (non-negative weights, positive capacity) is left to the caller — the algorithm assumes valid numeric data. This implementation runs in O(n log n) time, O(1) extra space, and is safe for up to several million items (heap sort memory permitting). Production code would bundle the logic into a utility class with static method taking arrays of primitive doubles for speed. The example below shows a complete, testable version that prints the maximum value for a known test case. Note the ratio is not stored — we compute it on the fly during comparison to avoid duplicating data. This reduces memory footprint for large inventory lists.

FractionalKnapsackComplete.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// io.thecodeforge — dsa tutorial

import java.util.*;

public class FractionalKnapsackComplete {
    public static void main(String[] args) {
        int[] value = {60, 100, 120};
        int[] weight = {10, 20, 30};
        int capacity = 50;
        System.out.println(maxValue(value, weight, capacity));
    }

    static double maxValue(int[] values, int[] weights, int capacity) {
        int n = values.length;
        Integer[] indices = new Integer[n];
        for (int i = 0; i < n; i++) indices[i] = i;
        Arrays.sort(indices, (i, j) -> Double.compare(
            (double)values[j] / weights[j],
            (double)values[i] / weights[i]));
        double total = 0;
        for (int idx : indices) {
            if (capacity == 0) break;
            double take = Math.min(weights[idx], capacity);
            total += take * (double)values[idx] / weights[idx];
            capacity -= take;
        }
        return total;
    }
}
Output
240.0
Production Trap:
Using double division for ratio inside comparator can cause comparison instability if weights are zero. Validate weights > 0 before sorting.
Key Takeaway
Sort indices by descending value/weight ratio, then greedily fill capacity — no custom class needed in production.

Output — What the Algorithm Actually Produces

The fractional knapsack greedy algorithm outputs a single value: the maximum total value achievable by taking fractions of items up to the knapsack's weight capacity. Unlike 0/1 knapsack, the output is always optimal and can include partial items. For a knapsack of capacity W and n items sorted by value/weight ratio, the output is the sum of values from whole items taken in descending ratio order plus the fractional value of the first partially taken item. Example: items (value 100, weight 20), (value 120, weight 30), capacity 50. Ratios: 5.0, 4.0. Take full item 1 (weight 20, value 100), then 30/30 of item 2 (value 120), total output = 100 + 120 = 220. If capacity were 40, take full item 1 (20 weight, 100 value), then 20/30 of item 2 (value 80 → 120 * (20/30)), output = 180. The output is a double; the algorithm never returns which fractions to take, only the maximum value. In production systems, the fractional amount per item can also be tracked if needed, but the core output is just the scalar total.

FractionalKnapsack.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// io.thecodeforge — dsa tutorial
import java.util.*;
public class FractionalKnapsack {
    public static double maxValue(int[] val, int[] wt, int cap) {
        int n = val.length;
        Item[] items = new Item[n];
        for (int i = 0; i < n; i++)
            items[i] = new Item(val[i], wt[i]);
        Arrays.sort(items, (a,b) -> Double.compare(b.ratio, a.ratio));
        double totalValue = 0.0;
        for (Item item : items) {
            if (cap <= 0) break;
            if (item.weight <= cap) {
                totalValue += item.value;
                cap -= item.weight;
            } else {
                totalValue += item.value * ((double) cap / item.weight);
                cap = 0;
            }
        }
        return totalValue;
    }
    static class Item {
        int value, weight; double ratio;
        Item(int v, int w) { this.value=v; this.weight=w; this.ratio=(double)v/w; }
    }
}
Output
Input: val={100,120}, wt={20,30}, cap=40 → Output: 180.0
Input: val={60,100,120}, wt={10,20,30}, cap=50 → Output: 240.0
Production Trap:
If your input contains zero-weight items with positive value, the ratio becomes infinite or undefined. Always validate item weights > 0 before computing ratio. A zero-weight item should be handled as a special case — take it for free.
Key Takeaway
The output is always a single double representing the maximum achievable value; no item selection list is returned by default.
● Production incidentPOST-MORTEMseverity: high

Greedy on a 0/1 Problem Cost the Team a Month of Optimisation

Symptom
After deploying a new scheduling algorithm, total throughput increased only 2% instead of the expected 15%. The team had switched from a DP-based solution to a greedy one to reduce latency.
Assumption
The team assumed that since CPU time could be split, any resource allocation was fractional — they treated each task as infinitely divisible.
Root cause
The tasks were actually 0/1: each task required a fixed block of CPU (a whole container). Taking a fraction of a task wasn't possible. The greedy algorithm by density picked tasks that didn't fit together optimally.
Fix
Reverted to a DP-based 0/1 knapsack algorithm. Added a check in the design phase: is the resource granular discrete or continuous?
Key lesson
  • Always confirm whether items are divisible or indivisible before choosing the algorithm.
  • Greedy for fractional knapsack is optimal, but applying it to a 0/1 problem gives no guarantee — test the constraint, not just the intuition.
Production debug guideSymptom → Action guide for when your resource allocation looks wrong3 entries
Symptom · 01
Total value is lower than expected, but the solution seems to follow the greedy rule.
Fix
Verify the items are actually fractional. If any item cannot be split (e.g., a container, a job), you need 0/1 knapsack — reframe the problem.
Symptom · 02
Sort order appears correct but results are off by a small amount.
Fix
Check floating point precision in value/weight ratios. Use decimal or fractions to avoid truncation errors.
Symptom · 03
The algorithm takes a fraction of an item when the remaining capacity is exactly equal to a full item.
Fix
Double-check the comparison: if weight <= remaining, take the full item, not a fraction. Off-by-one logic here leads to unintended fractional splits.
★ Greedy Knapsack Quick DebugUse these three commands to verify your greedy solution is correct
Suspiciously low total value
Immediate action
List the item densities and the fraction taken.
Commands
print([(value/weight, taken_fraction) for item in items])
Check that the sum of taken weights equals original capacity.
Fix now
If any high-density item has zero taken fraction, re-sort by density and reprocess.
Algorithm fills capacity but value is worse than brute force+
Immediate action
Compare with a simple brute force for small n (n <= 15).
Commands
Run brute-force 0/1 knapsack on the same data — if it gives higher value, you have a fractional-vs-0/1 mismatch.
Check item divisibility constraints in the problem statement.
Fix now
If items are indivisible, switch to DP. If divisible, check density calculation.

Key takeaways

1
Sort by value/weight density descending. Take full items while they fit, take a fraction of the next item to fill remaining capacity. O(n log n) dominated by sort.
2
The exchange argument is the key
any solution not taking the highest-density item first can be improved by swapping — this is the formal proof, not just intuition.
3
Fractional knapsack
greedy optimal. 0/1 knapsack: DP required. Knowing which you are facing is the first question to ask in any packing/allocation problem.
4
Real-world
continuous resource allocation — bandwidth, CPU cycles, capital allocation where fractional units are meaningful.
5
Follow-up interview question
if weights and values are integers, does fractional knapsack still give a fractional answer? (Yes, if no item fits perfectly in remaining space.)

Common mistakes to avoid

5 patterns
×

Sorting by value only, not value/weight ratio

Symptom
The greedy picks heavy, high-value items first and fills capacity poorly, giving lower total value than possible.
Fix
Always divide value by weight (or use cross-multiplication) to determine the order.
×

Integer division when computing density

Symptom
Multiple different ratios evaluate to the same integer, losing ordering precision.
Fix
Use double/float for the division or compare using val1 weight2 > val2 weight1.
×

Using <= instead of < for remaining capacity check (or vice versa)

Symptom
When an item exactly fits the remaining capacity, the algorithm either takes a fraction of it or skips it, wasting capacity or creating an unnecessary fraction.
Fix
Use if weight <= remaining: take entire item, else take fraction.
×

Not handling zero-weight items

Symptom
Division by zero when computing density (value/0).
Fix
Filter out zero-weight items before sorting, or handle them as special case (they contribute value without consuming capacity — take them all).
×

Assuming the problem is fractional when items are actually indivisible

Symptom
Greedy gives suboptimal result compared to brute force / DP.
Fix
Review the problem constraints: if items must be taken whole, use 0/1 knapsack DP.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Why does greedy work for fractional knapsack but not 0/1 knapsack?
Q02SENIOR
What is the time and space complexity of fractional knapsack?
Q03SENIOR
Prove that sorting by value/weight ratio gives the optimal solution.
Q04SENIOR
Can the fractional knapsack greedy algorithm be implemented without sort...
Q05SENIOR
How would you handle a fractional knapsack where each item has a bound o...
Q01 of 05SENIOR

Why does greedy work for fractional knapsack but not 0/1 knapsack?

ANSWER
Greedy works for fractional knapsack because items are divisible, so taking the highest density item first is always optimal — you can swap any partial lower-density item for the same weight of higher-density to increase value. In 0/1 knapsack, the integer constraint means swapping whole items may leave wasted space, so greedy can't guarantee optimality. Professional answer example: 'The exchange argument applies because we can adjust fractional amounts continuously. For 0/1, the integrality gap means greedy can be arbitrarily worse — a famous counterexample shows the ratio can be unbounded.'
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What if two items have the same value density?
02
Can fractional knapsack be solved without sorting?
03
Does fractional knapsack have any real-world use outside of educational contexts?
04
What if the capacity is extremely large (e.g., 10^12)?
05
How do I test whether my greedy solution is correct?
N
Naren Founder & Principal Engineer

20+ years shipping performance-critical code where algorithms decide the bill. Lessons pulled from things that broke in production.

Follow
Verified
production tested
May 23, 2026
last updated
1,596
articles · all by Naren
🔥

That's Greedy & Backtracking. Mark it forged?

10 min read · try the examples if you haven't

Previous
Rolling Hash and Polynomial String Hashing
9 / 13 · Greedy & Backtracking
Next
Job Sequencing Problem with Deadlines