Subset Sum Problem: Recursive Algorithm Implementation and Multi-language Solutions

Nov 09, 2025 · Programming · 10 views · 7.8

Keywords: Subset Sum Problem | Recursive Algorithm | Combinatorial Optimization | NP-hard Problem | Multi-language Implementation

Abstract: This paper provides an in-depth exploration of recursive approaches to the subset sum problem, detailing implementations in Python, Java, C#, and Ruby programming languages. Through comprehensive code examples and complexity analysis, it demonstrates efficient methods for finding all number combinations that sum to a target value. The article compares syntactic differences across programming languages and offers optimization recommendations for practical applications.

Problem Definition and Algorithm Overview

The subset sum problem is a classic combinatorial optimization challenge in computer science, with the core objective of finding all subsets from a given number set where the sum of elements equals a specified target value. This problem holds significant application value in areas such as resource allocation and portfolio optimization.

Recursive Algorithm Principles

The fundamental approach to solving the subset sum problem recursively involves traversing all possible number combinations through depth-first search. The algorithm maintains a partial solution list, selecting available numbers to add to the partial solution at each recursive step while updating the remaining available number set. When the partial sum equals the target value, the solution is recorded; when it exceeds the target, that branch is pruned early, effectively reducing unnecessary computations.

Python Implementation

Python offers two implementation variants leveraging its concise syntax: a basic version and a generator version. The basic version directly outputs valid combinations:

def subset_sum(numbers, target, partial=[]):
    s = sum(partial)
    if s == target: 
        print("sum(" + str(partial) + ")=" + str(target))
    if s >= target:
        return
    for i in range(len(numbers)):
        n = numbers[i]
        remaining = numbers[i+1:]
        subset_sum(remaining, target, partial + [n])

The generator version utilizes yield statements for lazy evaluation, suitable for large-scale data processing:

def subset_sum(numbers, target, partial=[], partial_sum=0):
    if partial_sum == target:
        yield partial
    if partial_sum >= target:
        return
    for i, n in enumerate(numbers):
        remaining = numbers[i + 1:]
        yield from subset_sum(remaining, target, partial + [n], partial_sum + n)

Java Implementation

The Java version adopts an object-oriented design approach using ArrayList for number collection management:

import java.util.ArrayList;
import java.util.Arrays;

class SumSet {
    static void sum_up_recursive(ArrayList<Integer> numbers, int target, ArrayList<Integer> partial) {
        int s = 0;
        for (int x: partial) s += x;
        if (s == target)
            System.out.println("sum(" + Arrays.toString(partial.toArray()) + ")=" + target);
        if (s >= target)
            return;
        for(int i = 0; i < numbers.size(); i++) {
            ArrayList<Integer> remaining = new ArrayList<Integer>();
            int n = numbers.get(i);
            for (int j = i + 1; j < numbers.size(); j++) 
                remaining.add(numbers.get(j));
            ArrayList<Integer> partial_rec = new ArrayList<Integer>(partial);
            partial_rec.add(n);
            sum_up_recursive(remaining, target, partial_rec);
        }
    }
}

C# Implementation

The C# version shares syntactic similarities with Java but employs List<T> generic collections:

private static void sum_up_recursive(List<int> numbers, int target, List<int> partial) {
    int s = 0;
    foreach (int x in partial) s += x;
    if (s == target)
        Console.WriteLine("sum(" + string.Join(",", partial.ToArray()) + ")=" + target);
    if (s >= target)
        return;
    for (int i = 0; i < numbers.Count; i++) {
        List<int> remaining = new List<int>();
        int n = numbers[i];
        for (int j = i + 1; j < numbers.Count; j++) 
            remaining.Add(numbers[j]);
        List<int> partial_rec = new List<int>(partial);
        partial_rec.Add(n);
        sum_up_recursive(remaining, target, partial_rec);
    }
}

Ruby Implementation

The Ruby version fully leverages functional programming features, resulting in more concise code:

def subset_sum(numbers, target, partial=[])
    s = partial.inject(0, :+)
    puts "sum(#{partial})=#{target}" if s == target
    return if s >= target
    (0..(numbers.length - 1)).each do |i|
        n = numbers[i]
        remaining = numbers.drop(i + 1)
        subset_sum(remaining, target, partial + [n])
    end
end

Algorithm Complexity Analysis

The subset sum problem is classified as NP-hard, with worst-case time complexity of O(2^n), where n represents the input set size. Actual algorithm performance heavily depends on the target value magnitude. When the target is relatively small, pruning strategies significantly reduce the search space. For instance, with set [1,2,3,4,5,6,7,8,9,10] and target 10, the algorithm generates only 175 branches; whereas with the same set and target 100000, it produces the full 1024 branches.

Practical Applications and Optimization Recommendations

In practical scenarios involving large-scale data, consider these optimization strategies: dynamic programming approaches suit situations with limited target value ranges; approximation algorithms can quickly find solutions within acceptable error margins; parallel computing can leverage multi-core processors to accelerate search processes. Reference articles indicate that in certain tools like Power BI, due to computational limitations, external programming languages are recommended for handling such combinatorial problems.

Multi-language Implementation Comparison

Different programming languages demonstrate distinct characteristics when implementing the same algorithm: Python excels in conciseness for rapid prototyping; Java and C# emphasize type safety and object-oriented design; Ruby embodies the elegance of functional programming. Developers should select appropriate implementation approaches based on specific project requirements and technical stacks.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.