Algorithm Complexity
Big O Notation
Exponential Algorithms
Computational Theory
O(n^n) Complexity

Are there any real Onn algorithms?

Master System Design with Codemia

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

Introduction

Yes, real algorithms with O(n^n) behavior do exist. They are usually brute-force algorithms over problems where each of n positions, variables, or decisions has n possible choices.

What O(n^n) Actually Means

An algorithm is O(n^n) when the search space grows like:

text
n × n × n × ... × n

with n factors, which equals n^n.

That is much larger than common polynomial growth and, for large n, even larger than factorial growth in many practical comparisons. The key idea is that the input size controls both:

  • how many decisions there are
  • how many options each decision has

Once both dimensions scale together, n^n appears naturally.

A Concrete Example: Enumerating All Length-n Sequences over n Symbols

Suppose you want to generate every length-n sequence whose elements are chosen from 0 through n - 1. There are n choices for the first position, n for the second, and so on, for a total of n^n sequences.

python
1from itertools import product
2
3
4def all_sequences(n):
5    for seq in product(range(n), repeat=n):
6        yield seq
7
8
9count = 0
10for seq in all_sequences(3):
11    print(seq)
12    count += 1
13
14print("total:", count)

For n = 3, this yields 27 sequences. For n = 5, it yields 3125. That growth becomes unmanageable quickly.

This is not just a toy example. Many brute-force search problems have exactly this shape.

Real Problems That Naturally Reach O(n^n)

Several genuine search tasks can degenerate to O(n^n):

  • Assign one of n colors to each of n vertices and test a graph property
  • Assign one of n resources to each of n jobs with no pruning
  • Enumerate all functions from an n-element set to an n-element set
  • Search all states of n variables where each variable has a domain of size n

In each case, the brute-force algorithm says "try every possible assignment." That is a real algorithm, even if it is usually a bad one.

For example, a naive graph-coloring solver might try every possible coloring without any pruning:

python
1from itertools import product
2
3
4def valid_coloring(edges, coloring):
5    return all(coloring[u] != coloring[v] for u, v in edges)
6
7
8def brute_force_coloring(n, edges):
9    for coloring in product(range(n), repeat=n):
10        if valid_coloring(edges, coloring):
11            return coloring
12    return None
13
14
15edges = [(0, 1), (1, 2), (0, 2)]
16print(brute_force_coloring(3, edges))

This solver is intentionally naive, but it is real. It explores up to n^n color assignments.

Why You Often Do Not See O(n^n) Written Explicitly

Many textbooks and papers describe this kind of algorithm as O(k^n) instead, where:

  • 'n is the number of variables'
  • 'k is the number of choices per variable'

That notation is more informative because it separates two independent parameters. But if k itself grows with n, then O(k^n) becomes O(n^n).

So sometimes people think "O(n^n) is fake" simply because the same algorithm is usually written with a different parameterization.

O(n^n) vs. O(n!)

These two are easy to confuse.

  • 'O(n!) appears when you arrange n distinct items with no repetition.'
  • 'O(n^n) appears when each of n slots can choose from all n options, usually with repetition or unrestricted assignment.'

For instance:

  • Permutations of n items: n!
  • All length-n words over an alphabet of size n: n^n

That distinction is useful because it tells you what kind of combinatorial structure the brute-force search is exploring.

Practical Meaning

Algorithms with O(n^n) complexity are usually only viable when:

  • 'n is very small'
  • the algorithm is used as a baseline or verifier
  • pruning, memoization, or domain constraints cut down the actual search dramatically

In real systems, people try very hard to avoid the full n^n search space. But the fact that we optimize away from it does not mean the original algorithm was not real.

Common Pitfalls

  • Assuming O(n^n) is purely theoretical and never appears in brute-force search.
  • Confusing O(n^n) with O(n!), even though they describe different combinatorial choices.
  • Forgetting that many algorithms written as O(k^n) become O(n^n) when k = n.
  • Dismissing naive exhaustive search as "not a real algorithm." It is real, just often impractical.
  • Comparing complexities without being clear about which parameter represents input size and which represents domain size.

Summary

  • Real O(n^n) algorithms exist, especially in exhaustive assignment and enumeration problems.
  • A common pattern is n decisions with n choices for each decision.
  • Many such algorithms are written as O(k^n) and only become O(n^n) when k scales with n.
  • 'O(n^n) is different from O(n!) because the underlying search space is different.'
  • These algorithms are usually brute-force baselines, not the final scalable solution.

Course illustration
Course illustration

All Rights Reserved.