Home

Recursive Weight Plate Calculator Explained

This is a step-by-step walkthrough of a recursive function that calculates which weight plates are needed to reach a target weight. It’s a practical example that shows how recursion builds results from the bottom up.


The Problem

Given a set of weight plates [25, 20, 15, 10, 5, 2.5] kg, determine which plates are needed to reach a target weight. If the target is impossible with these plates, return None.


The Solution

plates = [25, 20, 15, 10, 5, 2.5]

def weight_calc_clean(base, index=0):
    # BASE CASE 1: We hit the target exactly
    if base == 0:
        return {}

    # BASE CASE 2: We ran out of plates but still have weight left (Impossible)
    if index >= len(plates):
        return None

    # Calculate how many of the CURRENT plate fit
    count = int(base // plates[index])
    rem_weight = base % plates[index]  # Using modulo for the remainder

    # Move to the NEXT plate index
    result = weight_calc_clean(rem_weight, index + 1)

    # If the path below us was successful, add our plates to the tally
    if result is not None:
        if count > 0:
            result[plates[index]] = count
        return result

    return None

print(f"35kg: {weight_calc_clean(35)}")
print(f"3kg (Impossible): {weight_calc_clean(3)}")

Output:

35kg: {25.0: 1, 10.0: 1}
3kg (Impossible): None

Understanding the Parameters

The function receives two arguments:

Parameter Meaning
base The weight still to be accounted for
index Which plate we’re currently considering (0 = 25kg, 1 = 20kg, etc.)

The plates list serves as our lookup table:


The Three Logical Blocks

Base Case 1 — Nothing left to fill (if base == 0)

When the remaining weight hits zero, we’ve found a valid combination. Return an empty dictionary {}. This is the success signal that starts the unwinding process.

Base Case 2 — We ran out of plates (if index >= len(plates))

We’ve worked through every plate size and still have weight left over. The target is impossible with these plates. Return None.

Recursive Case — The Working Step

count = int(base // plates[index])      # How many of this plate fit
rem_weight = base % plates[index]       # What's left over
result = weight_calc_clean(rem_weight, index + 1)  # Try smaller plates

The frame pauses at that line and waits until the recursive call returns. When it does:

  1. If the result is not None and we used at least one of this plate (count > 0)
  2. Add this plate to the dictionary
  3. Return the result upward

Step-by-Step Execution: weight_calc_clean(35)

Call 1 — Considering the 25 kg plate

base = 35, index = 0, plates[0] = 25
35 // 25 = 1  → count = 1 (one 25kg plate fits)
35 % 25 = 10  → rem_weight = 10 (ten kilograms still to account for)

Call: weight_calc_clean(10, 1)

Call 2 — Considering the 20 kg plate

base = 10, index = 1, plates[1] = 20
10 // 20 = 0  → count = 0 (20kg plate doesn't fit)
10 % 20 = 10  → rem_weight = 10 (nothing subtracted)

Call: weight_calc_clean(10, 2)

Call 3 — Considering the 15 kg plate

base = 10, index = 2, plates[2] = 15
10 // 15 = 0  → count = 0 (15kg plate doesn't fit)
10 % 15 = 10  → rem_weight = 10 (still the same)

Call: weight_calc_clean(10, 3)

Call 4 — Considering the 10 kg plate

base = 10, index = 3, plates[3] = 10
10 // 10 = 1  → count = 1 (one 10kg plate fits exactly)
10 % 10 = 0   → rem_weight = 0 (nothing left!)

Call: weight_calc_clean(0, 4)

Call 5 — Base Case 1 Triggers!

base = 0
Condition: if base == 0 is TRUE
Return: {} (empty dictionary)

The recursion stops going deeper. This is the moment the stack begins to unwind.


Understanding Unwinding

Up to this point, every call has been paused, waiting for the call below it to finish. Think of them as a stack of sticky notes, each saying “I’ll finish my job once I hear back from the one below me.”

When Call 5 returns {}, that bottom sticky note is removed, and Call 4 wakes up.

Unwinding Call 4 — The 10 kg plate

Receives: result = {} from Call 5
result is not None → enter the block
count = 1, which is > 0
Execute: result[10] = 1

Dictionary is now: {10: 1}
Return upward to Call 3

Unwinding Call 3 — The 15 kg plate

Receives: result = {10: 1}
result is not None → enter the block
count = 0, which is NOT > 0
Nothing added to dictionary

Dictionary remains: {10: 1}
Return upward to Call 2

Unwinding Call 2 — The 20 kg plate

Receives: result = {10: 1}
result is not None → enter the block
count = 0, which is NOT > 0
Nothing added to dictionary

Dictionary remains: {10: 1}
Return upward to Call 1

Unwinding Call 1 — The 25 kg plate

Receives: result = {10: 1}
result is not None → enter the block
count = 1, which is > 0
Execute: result[25] = 1

Dictionary is now: {10: 1, 25: 1}
Stack is fully unwound!

Final Output: {25.0: 1, 10.0: 1}


The Impossible Case: weight_calc_clean(3)

3 kg cannot be assembled from these plates (the smallest is 2.5 kg, and 3 is not a multiple of it).

Plates 25, 20, 15, 10, 5 → all produce count = 0, rem_weight = 3
Index 5 (2.5 kg plate): 3 % 2.5 = 0.5, count = 1, rem_weight = 0.5
Call: weight_calc_clean(0.5, 6)

Now index >= len(plates) is TRUE — we’ve exhausted the plate list with weight still remaining. Base Case 2 triggers and returns None.

Every paused frame wakes up, checks if result is not None, finds it false, skips the block, and falls through to return None. The None propagates unchanged all the way up the stack.


Key Insight

The result dictionary is built from the bottom up:

  1. The deepest successful call returns an empty {}
  2. Each frame above adds its own plate count on the way back up
  3. Frames with count = 0 simply pass the dictionary through untouched

This means the output is clean by construction — no post-processing filter needed, and no zero-count entries cluttering the result.


Summary

Concept What It Means
Base Case 1 base == 0 → Success! Return {}
Base Case 2 index >= len(plates) → Impossible! Return None
Recursive Step Calculate plates needed, recurse with remainder
Unwinding Build the dictionary on the way back up
Clean Output Only plates with count > 0 appear in the result
Tags: PythonRecursionAlgorithmsProgramming