Lazy Greedy: Speeding Up Submodular Maximization for Free
Lazy Greedy keeps the greedy algorithm's (1 - 1/e) guarantee while skipping most of its oracle calls. Submodularity makes stale marginal gains sound upper bounds, so a max-heap of them is enough to certify each pick.
Series parts
On this page
In Part 2, I introduced the greedy algorithm for maximizing a monotone submodular function under a cardinality constraint , and showed that it achieves a approximation ratio, provably the best any polynomial-time algorithm can do. The best possible approximation ratio, however, doesn’t mean the best possible runtime. The algorithm costs calls to the value oracle for : at each of the steps, it recomputes the marginal gain for every remaining element .
This post is about making greedy faster without giving anything up. The trick, due to Minoux (1978)1, is called Lazy Greedy, and it falls out of taking submodularity seriously.
Recomputing What Can Only Shrink
All that recomputation is wasteful: submodularity guarantees that marginal gains can only decrease over time. If an element had a low marginal gain at step , it will have an even lower gain at step . Why recompute it?
The gains computed at earlier steps are not exact anymore, but they are still informative: each one is an upper bound on the element’s current gain. Minoux formalized this observation into an algorithm. The idea is to maintain a max-heap (priority queue) of upper bounds on marginal gains:
At each step, Lazy Greedy pops the element with the highest upper bound from the heap and recomputes its actual marginal gain. If the recomputed gain is still the largest (i.e., the next-highest upper bound in the heap), then by submodularity must have the largest true marginal gain among all remaining elements. The algorithm selects and moves on. Otherwise, it pushes back into the heap with its updated bound and tries the next candidate.
The worst-case runtime is still ; you might end up recomputing every element at every step. In practice, however, Lazy Greedy often needs only a handful of recomputations per step, because elements whose gains were already low at the previous step are even lower now and stay deep in the heap. The speedup varies depending on the problem instance, but factors of 5x to 100x over standard greedy are common. The approximation guarantee remains , unchanged. Lazy Greedy returns exactly the same solution as standard greedy (up to tie-breaking); it just spends fewer oracle calls to find it.
A Heap of Stale Thunks
If you have worked with lazy evaluation in functional programming, the pattern is familiar: avoid doing work until you know it matters. A thunk is a promise to compute a value later, and forcing it means paying the cost only when the value is actually needed. Each entry in Lazy Greedy’s heap can be thought of as a stale thunk: a marginal gain cached at some earlier step, left unforced until the element reaches the top of the heap. Popping the element forces the thunk, which in this setting means one oracle call to recompute against the current solution.
A memoized thunk caches a value that never changes, so forcing it once settles the matter forever. Lazy Greedy’s cached values do change: every element added to the solution can shrink the true gain of every other element. Submodularity is what rescues the scheme. It guarantees that gains only ever decrease, so a stale value is always an upper bound on the fresh one. A stale bound can flatter an element but never undersell it, and that asymmetry is why a single comparison against the next-best bound is enough to certify a winner.
The two shapes look like this side by side:
// Ordinary lazy evaluation: force once, cache forever.
function lazy<T>(compute: () => T): () => T {
let cached: T | undefined;
return () => (cached ??= compute());
}
// Lazy Greedy: a heap of stale thunks. The cache goes stale on
// purpose, but submodularity keeps it a sound upper bound.
type StaleThunk = {
element: number;
bound: number; // marginal gain cached at an earlier step
force: () => number; // recompute f(e | S) against the current S
};
function selectNext(heap: MaxHeap<StaleThunk>): number {
const top = heap.pop();
const fresh = top.force(); // pay the oracle call now
if (fresh >= heap.peek().bound) return top.element; // still best
heap.push({ ...top, bound: fresh }); // re-suspend and retry
return selectNext(heap);
} Ordinary laziness saves work because values never change. Lazy Greedy saves work because values change in only one direction.
Looking Ahead
Lazy Greedy’s speedup is real but opportunistic. On well-behaved instances the heap prunes almost everything; on adversarial or poorly structured ones it degrades back to standard greedy’s . For massive datasets ( in the millions, in the thousands), we need an algorithm whose theoretical runtime is better, not just one that is faster on benign inputs.
In Part 4, I will introduce the Stochastic Greedy algorithm (Mirzasoleiman et al. 2015)2, which replaces the full scan over at each step with a random subsample of size . The total cost drops from to ; the factor of becomes a logarithmic term. The approximation guarantee degrades only slightly, to in expectation, where is a tunable parameter.
Key Takeaways
- Lazy Greedy exploits the diminishing returns property to skip redundant recomputations: a marginal gain cached at an earlier step is always an upper bound on the current one.
- A max-heap of stale bounds plus one comparison against the next-best bound is enough to certify each pick, so the algorithm returns the same solution as standard greedy with the same guarantee.
- Speedups of 5x to 100x are common in practice, but the worst-case cost remains , which motivates the Stochastic Greedy algorithm in Part 4.
Further Reading
- M. Minoux, “Accelerated Greedy Algorithms for Maximizing Submodular Set Functions” (1978). The original Lazy Greedy paper.
- J. Leskovec, A. Krause, C. Guestrin, C. Faloutsos, J. VanBriesen, and N. Glance, “Cost-effective Outbreak Detection in Networks” (2007). The CELF algorithm3, which brought lazy evaluations to influence maximization at scale.
- A. Krause and D. Golovin, “Submodular Function Maximization” (2014). A survey covering greedy, Lazy Greedy, and continuous relaxation methods.