Subset Sum
Algorithm
Pisinger
Optimization
Computer Science

Fast solution to Subset sum algorithm by Pisinger

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Pisinger's work on subset sum is famous because it pushed exact solving much further than the naive dynamic programming most people learn first. If you are looking for a practical fast exact solver, the important idea is not just "use DP," but use bit-parallel state updates and problem assumptions that fit pseudo-polynomial algorithms well.

What Problem Pisinger Targets Well

Subset sum asks whether some subset of positive integers adds up to a target value. In the fully general sense it is NP-complete, so there is no single magic polynomial-time algorithm for all inputs.

Pisinger's algorithms are strong exact methods for important special cases, especially when numbers are positive and the target or weight bounds make pseudo-polynomial techniques practical. That is why they are fast in many real instances even though the worst-case problem remains hard.

If what you need is a solid implementation idea rather than a research-paper translation, the practical version is usually a bitset-based exact DP.

The Core Idea Behind Fast Exact Solutions

Classic DP says: if sum s is reachable, then after adding value x, sum s + x is reachable too. Instead of storing that as a full two-dimensional table, you can store reachable sums as bits and shift them.

That makes updates word-parallel, which is one reason exact subset sum can be surprisingly fast for moderate targets.

A Practical C++ Bitset-Style Solver

The following C++ implementation uses a dynamic bitset built from uint64_t blocks. It solves the decision version exactly for positive integers.

cpp
1#include <cstdint>
2#include <iostream>
3#include <vector>
4
5bool subsetSumBitset(const std::vector<int>& values, int target) {
6    if (target < 0) {
7        return false;
8    }
9
10    int words = target / 64 + 1;
11    std::vector<std::uint64_t> bits(words, 0);
12    bits[0] = 1ULL; // sum 0 is reachable
13
14    for (int x : values) {
15        if (x < 0) {
16            throw std::invalid_argument("This implementation expects positive integers");
17        }
18
19        std::vector<std::uint64_t> shifted(words, 0);
20        int wordShift = x / 64;
21        int bitShift = x % 64;
22
23        for (int i = words - 1; i >= 0; --i) {
24            std::uint64_t value = 0;
25
26            if (i - wordShift >= 0) {
27                value |= bits[i - wordShift] << bitShift;
28
29                if (bitShift != 0 && i - wordShift - 1 >= 0) {
30                    value |= bits[i - wordShift - 1] >> (64 - bitShift);
31                }
32            }
33
34            shifted[i] = value;
35        }
36
37        for (int i = 0; i < words; ++i) {
38            bits[i] |= shifted[i];
39        }
40
41        if ((bits[target / 64] >> (target % 64)) & 1ULL) {
42            return true;
43        }
44    }
45
46    return ((bits[target / 64] >> (target % 64)) & 1ULL) != 0;
47}
48
49int main() {
50    std::vector<int> values = {3, 34, 4, 12, 5, 2};
51    std::cout << std::boolalpha << subsetSumBitset(values, 9) << '\n';
52}

This does not implement every detail from Pisinger's papers, but it captures the practical performance principle: update many reachable sums at once using word operations instead of scalar boolean transitions.

Why This Is Fast in Practice

The naive DP update inspects every candidate sum one by one. Bitset methods compress many boolean states into machine words, so a single CPU operation updates dozens of reachable sums at once.

That does not change the pseudo-polynomial nature of the algorithm. Runtime still depends on the numeric target range. But for moderate targets, bit-parallelism is often the difference between a toy solution and a usable exact solver.

This is also why Pisinger-style exact methods are associated with strong engineering performance: careful state representation matters.

When This Approach Is the Wrong Tool

If the target sum is extremely large, even fast pseudo-polynomial DP becomes impractical because the bitset size grows with the target. In that regime, you may need meet-in-the-middle techniques, approximation schemes, or a solver tailored to your exact constraints.

Likewise, if the input contains negative numbers, the straightforward positive-integer bitset formulation no longer applies cleanly.

Common Pitfalls

The biggest pitfall is ignoring the problem assumptions. Fast exact subset sum methods like this work best for non-negative integers and moderate target ranges.

Another issue is benchmarking only by item count. For pseudo-polynomial algorithms, the magnitude of the target matters just as much as the number of values.

Developers also sometimes implement a two-dimensional DP table first and then wonder why memory usage explodes. A compact bitset representation is usually the more serious baseline.

Finally, do not call every exact subset-sum DP "Pisinger's algorithm." His published algorithms are more specialized than the basic textbook table. Bitset DP is a practical fast technique inspired by the same performance mindset, but it is not a literal rewrite of the research paper.

Summary

  • Subset sum remains NP-complete, but exact pseudo-polynomial methods can still be very fast on practical instances.
  • Pisinger's work is important because it shows how much exact performance can be improved with the right representation and assumptions.
  • A bitset-based DP is a strong practical exact solver for positive integers and moderate targets.
  • Runtime depends heavily on the target range, not just the number of items.
  • If targets are huge or values can be negative, you likely need a different technique.

Course illustration
Course illustration

All Rights Reserved.