24

Time complexity can't be any lower than space complexity (at least one operation is required to use a unit of memory), so what are some algorithms where space actually tends to be the limiting factor? It puts a hard upper bound on what you can do, whereas time is only a factor if you're 'impatient.'

Preferably something practical.

Adam Tolnay
  • 411
  • 3
  • 5
  • 9
    Of course the whole field of streaming algorithms is predicated on not being able to store the input. For instance packets going through a high-speed switch. You can afford to do linear time in terms of packets by examining each as it passed through but cannot store all of them. This may not be what you wanted but very well motivated from practice. – Chandra Chekuri Aug 13 '20 at 20:09
  • I would assume such an algorithm would be a poly (or above) space algorithm with low space reuse. – rus9384 Aug 14 '20 at 09:18
  • 1
    @ChandraChekuri I think streaming algorithms are motivated by latency guarantees more than by lack of storage space. – user253751 Aug 14 '20 at 14:46
  • @user253751: Not generally, no. You can have zero latency with some non-streaming algorithms but that doesn't make them streaming algorithms (because they use too much space). – user541686 Aug 14 '20 at 18:45
  • "Time complexity can't be any lower than space complexity (at least one operation is required to use a unit of memory)"... I get what you're attempting to say, but it's worth noting this is only kind-of true. For example, if you want to make a lookup table mapping every integer to some other value, the time complexity would be logarithmic in the input value (input N = log(N) bits), but the space complexity would be linear in the input value (exponential in the number of bits). – user541686 Aug 14 '20 at 18:48
  • @user541686 Wouldn't each mapping still require an operation, making it linear in N? I'm not clear why time complexity would be logarithmic in N/ linear in number of bits. – Adam Tolnay Aug 14 '20 at 20:04
  • @AdamTolnay: It depends on your model of computation, but if you assume memory is random-access rather than linear-access, then it would be constant-time (as some assume). If you relax it a bit to make it more realistic, it'd be linear in the number of bits. e.g. the input is N (say, N = 5 = 101b), and you'd need to process each bit to figure out what ultimate slot to read. Linear-access memory might be more physically accurate given space & the speed of light, but in practice it's useless unless you're dealing with e.g. tape storage (and it kind of misses the point I'm trying to make). – user541686 Aug 14 '20 at 20:08
  • @user541686 Ok, I think I misunderstood before, I thought the algorithm was creating the lookup table. I understand the space complexity of such a lookup table is exponential in the number of bits, but wouldn't the algorithm that uses it be linear time in the number of bits, and use constant space? Even if the table is huge, the number of bits in my input doesn't affect space usage. – Adam Tolnay Aug 14 '20 at 21:24
  • @AdamTolnay: The algorithm "using" it (i.e. putting stuff into it or retrieving stuff from it) might e.g. assume unwritten slots are already initialized to zero. In that way it actually requires that space, despite only writing to a portion of it. Of course you can design another algorithm that doesn't have this requirement, but (a) it would be slower, and (b) it would miss the point that this algorithm can still exist. – user541686 Aug 14 '20 at 21:26
  • @user541686 But assuming unwritten slots are initialized to zero doesn't actually use that space. I don't think an algorithm that sections off an arbitrarily large amount of space based on input size and does nothing with it can be said to have high space complexity. – Adam Tolnay Aug 14 '20 at 21:49
  • @AdamTolnay: Here's an analogy: if I give you a $100 check to a bank account that contains $100, am I "using" the money in that bank account, or should I consider it unused? Would it be honest for me to give out 100 of those checks backed by the same $100? If it turns out there's only $50 in the account, could you honestly say that's a $100 check if you pay someone with it? You can get into a pointless semantics debate over the meaning of "use", but that has no effect on the fact that you do need to set aside the full space for it, or it'll misbehave. – user541686 Aug 14 '20 at 22:06
  • @user541686 I'm not an expert, but the meaning of "use" isn't trivial when talking about complexity. If "use" means "overwrite a bit," as I suspect it would, then I can't see how space complexity can be higher than time complexity, as doing so requires time. – Adam Tolnay Aug 14 '20 at 22:37
  • @AdamTolnay: Like I said: you can get into a pointless semantics debate over the meaning of "use" (and people define it in different ways depending on what's interesting in the given context), but your interpretation has no effect on the fact that you do* need to allocate the full (exponentially-sized) space to such an algorithm, or it will* misbehave. You were asking me to clarify, and I expect it should be pretty clear at this point what I meant when I said what you wrote is "kind-of" true, so I'm not going to continue arguing semantics. – user541686 Aug 14 '20 at 23:39
  • @user541686 Okay, thanks for the discussion, I'm obviously still learning. – Adam Tolnay Aug 15 '20 at 00:02
  • Searching for "matching" (for example duplicate) entries in a large database could be an example: The whole database must be loaded into RAM to do this. – Martin Rosenau Aug 15 '20 at 12:52
  • 1
    Not an algorithm with practical application per se, but the Hashlife algorithm for computing simulations of Conway's Game of Life in logarithmic time requires a large amount of space for high-entropy patterns, so much so that decent optimizations of it require a dedicated garbage collector to remove unused nodes from the quadtree. – Patrick Roberts Aug 15 '20 at 14:30
  • @ rus9384 When thinking of space complexity the input is considered read-only, otherwise one cannot define sub-linear space complexity classes such as log-space. – Chandra Chekuri Aug 16 '20 at 17:49
  • An underlying reason why examples are hard to find is that the number of operations per minute and storage bits in a personal computer are both roughly in the same order of magnitude, let's say around 10^11.

    If the standard number of operations per minute was e.g. 10^20 instead, then space would be the clear barrier for algorithms that use linear time and space.

    – Abel Molina Aug 19 '20 at 16:28

12 Answers12

19

Most computations in algebraic geometry / commutative algebra.

Most involve computing Grobner bases, which are EXPSPACE-hard in general. There are some parameter regimes where this improves and thus some computations can reasonably be done in practice (e.g. using Macaulay2 or SINGULAR), but very often it quickly eats up all the space and crashes. I think one of the first papers to look at this was Bayer & Mumford "What can be computed in algebraic geometry?".

(FWIW, my recent experience with these programs has been that there is a trichotomy: either the answer returns in (1) seconds, (2) a few minutes, or (3) so long that you give up / so much memory that it crashes.)

Joshua Grochow
  • 37,260
  • 4
  • 129
  • 228
8

My go-to answer for this (the one I use in undergraduate algorithms classes) is the Bellman–Held–Karp dynamic programming algorithm for the traveling salesperson problem (https://en.wikipedia.org/wiki/Held%E2%80%93Karp_algorithm). It's not the choice in practice for this problem (instead, branch-and-cut methods like in CONCORDE are faster) but it has the best proven time guarantees for the general problem, and its $O(n^2 2^n)$ time and $O(n2^n)$ space are in the range to make the space bound the bottleneck. There are alternative algorithms using polynomial space but with a higher exponential time bound, roughly $4^n$.

David Eppstein
  • 50,990
  • 3
  • 170
  • 278
8

In knowledge compilation, the task is to compile some set $A\subseteq \{0,1\}^n$ into a format such that various queries can then be answered in polynomial time. For example, you can "compile" the set of satisfying assignments to a CNF formula $\psi$ into a Binary Decision Diagram (a kind of directed acyclic labelled graph). Once this is (expensive) computation is done, one can then do many things cheaply that are usually expensive.

For example, one can count the satisfying assignments of a CNF formula in time linear in the size of this BDD graph. If you have compiled two CNFs $\phi,\psi$ into BDDs, then you can check whether $\phi\implies \psi$, and count $|\phi\wedge \psi|$, in time $\Theta(|\phi|_{\text{BDD}}\cdot |\psi|_{\text{BDD}})$. This is significant, because a BDD can be exponentially smaller than the set that it encodes: some formulas have an exponential number of satisfying assignments, but have a BDD of size only, say, $\mathcal{O}(n^2)$. The BDD of any clause $(x_1\vee x_2\vee\cdots\vee x_{k})$ has size only $\Theta(k)$, so after building the BDD of a formula $\phi$ BDD once, one can then check for clausal entailment $\phi\implies (x_1\vee x_2\vee\cdots\vee x_k)$, for any clause, very quickly, in time $\Theta(|\psi|_{\text{BDD}}\cdot k)$. Normally these computations are $\#\text{P}$-Complete and $\text{NP}$-Complete, respectively.

In an ideal situation, we have the opportunity to build the BDD during "preprocessing time", and once we're done, we hear which query we are supposed to answer. Then the limiting factor is that the BDD may grow exponentially in size. This blowup was always unavoidable, of course: We are trying to do intractable computations in polynomial time, so the tradeoff that we make is that the representation is exponentially large. Fortunately, in practice, this exponential behaviour rarely happens, and many interesting functions and systems can be represented by surprisingly small BDDs. For example, formulas with small treewidth have small BDDs.

Another wonderful application: the set $A$ is be the set of reachable configurations of a piece of software, or the reachable positions in chess. This is how BDDs made their debut: One can do exhaustive search on the state space of a program by compiling it into a BDD, and then one checks, for example, whether that set contains an element in which the program counters of two threads are in the same critical section. This last predicate is a simple formula with a small BDD, so intersection checking is fast.

Since the introduction of BDDs in 1986 [1], a large zoo of new diagrams have sprung up for this purpose: ZDDs, Tagged BDDs, SDDs, d-DNNFs. They make time/space tradeoffs: They are more compact, but support less queries. A good (but slightly out-of-date) overview is A knowledge compilation map [2]. All these diagrams are ultimately Boolean circuits, so finding out which ones are more compact than others is a difficult question of circuit lower bounds, so is part of computational complexity theory.

Of course BDDs are not always the answer, and modern model checking seems to favour SAT-based approaches, but Bryant's paper has 12k citations, so it is safe to say that people have found some uses for them.

[1] Bryant, Randal E. "Graph-based algorithms for boolean function manipulation." Computers, IEEE Transactions on 100.8 (1986): 677-691.

[2] Darwiche, Adnan, and Pierre Marquis. "A knowledge compilation map." Journal of Artificial Intelligence Research 17 (2002): 229-264.

Lieuwe Vinkhuijzen
  • 1,969
  • 14
  • 20
7

Dynamic programming is probably a general case of this but one specific, practically relevant and illustrative example is (global) pairwise sequence alignment using the Needleman–Wunsch algorithm, which has both time and space complexity $\mathcal O(nm)$.

When applied to mammalian whole-genome alignments, this would naïvely require in the order of exabytes of space. Even bacterial genome alignments still require terabytes. By contrast, there’s a clever variation of the algorithm due to Hirshberg that uses divide & conquer to require only linear space ($\mathcal O(\min\{n,m\})$). This algorithm is also faster in practice (because it reduces the search space) but even if it didn’t improve the runtime it would still be practically feasible, whereas Needleman and Wunsch’s algorithm has prohibitive space requirements for all but small sequences.

Konrad Rudolph
  • 351
  • 1
  • 9
5

I don't know if the space complexity of this problem is limiting in practice (I have not personally run experiments to verify this, moreover I don't know anyone who needs to solve exact SVP in practice --- approximating it to some polynomial approx factor is already sufficient to break cryptography), but algorithms solving the Shortest Vector Problem in $n$-dimensional integer lattices fall into a few different classes:

  1. Enumeration methods: $O(n^n)$ time, poly space
  2. Sieving methods: exponential time, exponential space, and randomized
  3. Voronoi Cell Computations: exponential time, exponential space

This is to say all known exact SVP algorithms with provable running time $2^{O(n)}$ use exponential space, and algorithms with polynomial space usage have running time $2^{\omega(n)}$.

Mark Schultz-Wu
  • 978
  • 4
  • 13
5

One example is multicommodity flow problems via Simplex method. In these problems we have a graph $G=(V,E)$ with $n$ nodes and $m$ edges and $K$ commodities. The number of variables is $Km$ (one per commodity and edge pair) and the number of constraints is roughly $m$. Now if you try to run the flow problem via simplex based algorithms then the incidence matrix is too large and inverting it creates a dense matrix which does not often fit in memory even though the initial problem is of reasonable size. This is one reason people use column generation and approximate iterative methods.

Chandra Chekuri
  • 6,999
  • 31
  • 39
4

With this question we actually have to worry about $O(1)$ factors, because as you point out time can't be little o of space, but it can be much less demanding as a fraction of our hardware's abilities. A historical example, in which many algorithms could be discussed to make the point, would be old-school video games. I won't go into much detail here, but will lean on links; for now, it suffices to say it's mostly been about reducing the redundancy in data, sometimes caring literally about every single bit.

Nowadays, you can afford to give every pixel independent 24-bit colour in every frame. But there was a time you couldn't even achieve 2-bit colour that way, due to limited RAM. The reason that's no longer true is because RAM has grown much more in the last 40 years or so than screen resolution has. There were similar issues with audio.

The same period has also seen hardware expand how large the whole game can be, which may not sound like an algorithm detail, but it is because (1) game developers used to have to do all sorts of inventive things to do all they could with memory (here's a modern take on just a few of them) and (2) modern games' large size is typically used to cache a lot of data, thereby reducing time complexity (if only by a $O(1)$ factor).

The history of video games is roughly a transition from space complexity being the limiting factor to time complexity being the limiting factor, and there was a period when both were very important. For example, Andy Gavin had to be very innovative with both, but again a lot of it comes down to $O(1)$ factors.

J.G.
  • 141
  • 1
3

A look-up table algorithm is the extreme example of an algorithm where space is the limiting factor. In these types of algorithms you have an entry in a table for every possible input. This results in a time complexity of O(1) but the space complexity will be based upon the number of possible inputs. You can think of this as an analog to the old days where mathematics textbooks had charts for things like logarithms or sine/cosine etc.

I have practically used these algorithms in embedded systems where the range of inputs was limited the range of a couple of 8-bit unsigned integers. I have also seen production code that had a look up table for a range of sine values of limited input precision.

This use case does not come up very often as input ranges are not commonly nicely range constrained or an output can depend on many variable or even worse if the order of the inputs matters (think traveling salesman)

1

You may like to read about the space-time tradeoff. Generally speaking, it's a continuum of how far you're willing to go to strike a balance between space and efficiency.

From a practical perspective, just about any computational process can be drastically optimized with memoization (lookup tables), inlining, and unrolling. I would say just about all efficient algorithms ultimately boil down to the application of memoization at various points in the computational process. Inserting data into a lookup table is like pre-computing specific aspects of the problem. In the extreme case, you can completely cache any function to achieve $O(1)$ complexity, provided you're happy to precompute every possible input and use a lookup table of size $2^{\#input\ bits}$.

We don't talk about compiler optimizations like inlining and unrolling very much in practice, but they are equally important for efficient computation. The compiler often ends up inflating the executable size in order to eliminate redundant conditional checks.

You could also view data compression as being a trade-off between time and space complexity. Totally uncompressed data can be loaded linearly with respect to its size. Compressed data takes at least that long since it had to load the final data into memory and account for any computational overhead associated with compression and decompression.

user1318416
  • 121
  • 3
1

I think most non-trivial quantum algorithms fit the bill here as the space requirement to store complex amplitudes for an $n$ qubit system is $2^n$ in the general case.

Attila Kun
  • 111
  • 1
0

There are at least a few areas in practice I can think of:

  1. Lots of games are PSPACE-hard, meaning you'll necessarily need a lot of space to play them optimally. See a table here: Wikipedia – Game complexity

  2. The notion of "memory-hard functions" was developed as functions that are precisely designed to require large space to compute so that "technological shortcuts" can't allow an adversary to compute them more efficiently than expected, in other words, when hardness should translate to needing more real physical materials. They're of immense use in cryptography, especially for proof of work primitives in cryptocurrency. See Wikipedia – Memory hard function.

  3. In machine learning, space can be a limiting factor. Among the provable results, the recent work by Ran Raz stands out.

Glorfindel
  • 359
  • 1
  • 4
  • 10
Mahdi Cheraghchi
  • 4,031
  • 22
  • 30
  • 4
    PSPACE-hardness does not denote that the problem requires a lot of space: there are PSPACE-complete problems that can be solved in linear space. I would say PSPACE-completeness denotes the exact opposite: problems that probably require exponential time, but only polynomial space. – Laakeri Aug 13 '20 at 18:51
  • 2
    Linear space is a lot of space :) – Mahdi Cheraghchi Aug 13 '20 at 19:30
0

I remembering hearing that early suffix tree algorithms suffered from space constraints:

The space (obviously) isn't fully written to, but must be allocated in their models of computation to ensure the time complexities they're trying to achieve.

Unfortunately, these examples are the only info I have from old notes I have lying around, and I'm not currently clear on which precise part of each paper each one refers to. Hopefully they're correct, but if someone has more info, please help me update this answer.

user541686
  • 421
  • 2
  • 13