Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Data Structures & Algorithms (DSA) Module 1 – Foundations: problems, correctness and complexity

Growth rates and what fits in a time limit

Rank growth rates from constant to factorial, estimate the largest input each one handles in a second, and read problem limits to choose a target complexity.

  • Beginner
  • 20 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0 and quickjs 0.32.0
  • By MySmartCoPilot

What you will learn

  • Rank common growth rates from constant to factorial
  • Estimate the largest input each complexity can handle per runtime
  • Read problem constraints and choose a target complexity

Before you start

On this page

Asymptotic notation says how the cost of an algorithm grows with the input, but a program is judged in seconds: an online judge stops it after one or two, and a user gives up after a few. This lesson joins the two views. You will rank the common growth rates, turn a time limit into a budget of steps, see how that budget changes from one runtime to another, and read a problem’s limits to decide how fast the algorithm has to be before you write any code.

The ladder of growth rates

These are the growth rates you will meet most often, from the one that grows most slowly to the one that grows fastest, each with a typical source of that cost:

  • O(1), constant: reading a list element by its index, or looking a key up in a hash table (on average).
  • O(log n), logarithmic: binary search, which halves the part still to search at every step.
  • O(√n): testing whether n is prime by trying divisors up to √n.
  • O(n), linear: one pass over the input, such as finding the largest element.
  • O(n log n): sorting by comparing elements, as Python’s sorted() does.
  • O(n²), quadratic: looking at every pair of elements.
  • O(n³), cubic: every triple of elements, or the simple way of multiplying two n × n matrices.
  • O(2ⁿ), exponential: trying every subset of n items.
  • O(n!), factorial: trying every ordering of n items.

Each rate stands for a count of steps, so the clearest way to compare them is to work the counts out. This program prints the step count of each rate for four input sizes:

Step counts of each growth rate Python · growth_table.py
import math

SIZES = [10, 100, 1_000, 1_000_000]


def shown(x):
    """A step count as it is usually written: plain while it is short, then in e-notation (1.0e6 = 1.0 x 10^6)."""
    if x >= 10_000:
        exponent = len(str(int(x))) - 1
        return f"{x / 10**exponent:.1f}e{exponent}"
    if x == int(x) or x >= 100:
        return f"{round(x):,}"
    return f"{x:.1f}"


def two_to_the(n):
    if n <= 1_000:
        return shown(2**n)
    return f"{math.floor(n * math.log10(2)) + 1:,} digits"


def factorial(n):
    if n <= 1_000:
        return shown(math.factorial(n))
    return f"{math.floor(math.lgamma(n + 1) / math.log(10)) + 1:,} digits"


RATES = [
    ("1", lambda n: shown(1)),
    ("log2 n", lambda n: shown(math.log2(n))),
    ("sqrt n", lambda n: shown(math.sqrt(n))),
    ("n", lambda n: shown(n)),
    ("n log2 n", lambda n: shown(n * math.log2(n))),
    ("n^2", lambda n: shown(n**2)),
    ("n^3", lambda n: shown(n**3)),
    ("2^n", two_to_the),
    ("n!", factorial),
]

widths = [9, 9, 10, 17]
print(f"{'rate':<9}" + "".join(f"{f'n={n:,}':>{w}}" for n, w in zip(SIZES, widths)))
for name, steps in RATES:
    print(f"{name:<9}" + "".join(f"{steps(n):>{w}}" for n, w in zip(SIZES, widths)))

Output

rate          n=10    n=100   n=1,000      n=1,000,000
1                1        1         1                1
log2 n         3.3      6.6      10.0             19.9
sqrt n         3.2       10      31.6            1,000
n               10      100     1,000            1.0e6
n log2 n      33.2      664     9,966            2.0e7
n^2            100    1.0e4     1.0e6           1.0e12
n^3          1,000    1.0e6     1.0e9           1.0e18
2^n          1,024   1.3e30   1.1e301   301,030 digits
n!           3.6e6  9.3e157  4.0e2567 5,565,709 digits

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 growth_table.py

The output writes large numbers in e-notation: 2.0e7 is 2.0 × 10⁷, twenty million. Three things stand out:

  • Up to n log n, a million items is easy. Even n log₂ n is only about twenty million steps for n = 1,000,000.
  • Quadratic and cubic costs explode at large n. A million items need 10¹² steps at O(n²): almost three hours at 10⁸ steps per second.
  • Exponential and factorial costs explode at small n. 2¹⁰⁰ is already a 31-digit number, and 2ⁿ for a million items is a number with 301,030 digits. Doubling n squares 2ⁿ, because 2²ⁿ = (2ⁿ)², so no faster computer rescues an exponential algorithm.

Big-O hides constant factors and smaller terms, so at small n a costlier rate can still be faster in practice. The ladder tells you which algorithm wins once inputs are large, and what follows tells you where “large” starts.

How many steps fit in a second

A simple step here is one turn of a tight loop that does a little arithmetic, a comparison or an index lookup. A time limit becomes a budget of steps: the number of simple steps the runtime manages per second, times the number of seconds. Given a budget, you can work out the largest input each growth rate can handle. This program does it for budgets from a million to a billion steps:

The largest n that fits each budget Python · largest_n.py
import math

RATES = [
    ("n", lambda n: n),
    ("n log2 n", lambda n: n * math.log2(n)),
    ("n^2", lambda n: n**2),
    ("n^3", lambda n: n**3),
    ("2^n", lambda n: 2**n),
    ("n!", math.factorial),
]
BUDGETS = [10**6, 10**7, 10**8, 10**9]


def largest_n(steps, budget):
    """The largest n with steps(n) <= budget, for a step count that grows with n."""
    high = 1
    while steps(high * 2) <= budget:  # double until we overshoot ...
        high *= 2
    low, high = high, high * 2  # ... then binary search between the last two guesses
    while high - low > 1:
        middle = (low + high) // 2
        if steps(middle) <= budget:
            low = middle
        else:
            high = middle
    return low


print(f"{'rate':<9}" + "".join(f"{f'1e{len(str(b)) - 1} steps':>14}" for b in BUDGETS))
for name, steps in RATES:
    print(f"{name:<9}" + "".join(f"{largest_n(steps, b):>14,}" for b in BUDGETS))

Output

rate          1e6 steps     1e7 steps     1e8 steps     1e9 steps
n             1,000,000    10,000,000   100,000,000 1,000,000,000
n log2 n         62,746       526,172     4,523,071    39,620,077
n^2               1,000         3,162        10,000        31,622
n^3                 100           215           464         1,000
2^n                  19            23            26            29
n!                    9            10            11            12

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 largest_n.py

largest_n doubles its guess until the step count overshoots the budget, then binary-searches between the last two guesses, a technique that the binary search module of this track comes back to. The diagram puts the 10⁸ column next to the ladder:

A ladder of growth rates from O(n!) down to O(1), each with the largest input size that fits in 100 million simple steps.Costliest at the topO(n!), every orderingn ≤ 11O(2ⁿ), every subsetn ≤ 26O(n³), every triplen ≤ 464O(n²), every pairn ≤ 10,000O(n log n), sortingn ≤ about 4.5 millionO(n), one passn ≤ 100 millionO(log n) and O(1)any n you can store

The growth-rate ladder for a budget of 100 million steps

Text description of the diagram

The diagram is one column of seven boxes, from the costliest growth rate at the top to the cheapest at the bottom. Each box names the rate, a typical algorithm with that cost, and the largest input size n whose step count stays within 100 million (10^8) simple steps, as printed by the lesson's largest_n.py.

  1. O(n!), trying every ordering of the items: n up to 11.
  2. O(2^n), trying every subset: n up to 26.
  3. O(n^3), looking at every triple: n up to 464.
  4. O(n^2), looking at every pair: n up to 10,000.
  5. O(n log n), sorting: n up to about 4.5 million.
  6. O(n), one pass over the input: n up to 100 million.
  7. O(log n) and O(1), halving the search space or a single lookup: any input you can store.

Read across a row and the effect of a faster computer appears: ten times the budget lets an O(n) algorithm handle ten times the input, an O(n²) one about three times (√10 ≈ 3.2), an O(n³) one a little over twice, and O(2ⁿ) only three or four more items.

The same loop in four runtimes

How many simple steps run in a second depends a great deal on the runtime, not only on the computer. The next two programs run the same loop, a million additions, and report the best of nine tries. First Python:

Simple steps per second in Python Python · loop_rate.py
import sys
import time

STEPS = 1_000_000


def count_up(steps):
    total = 0
    for i in range(steps):
        total += i
    return total


# Nine short tries, keeping the fastest: a slower try lost time to other programs.
best = float("inf")
for attempt in range(9):
    start = time.perf_counter()
    total = count_up(STEPS)
    best = min(best, time.perf_counter() - start)

assert total == STEPS * (STEPS - 1) // 2
rate = STEPS / best
print(f"Python {sys.version.split()[0]} on {sys.platform}")
print(f"{STEPS:,} loop steps: {best * 1000:.1f} ms at best")
print(f"about {float(f'{rate:.2g}'):,.0f} simple steps per second")

Output

Python 3.14.8 on darwin
1,000,000 loop steps: 44.4 ms at best
about 23,000,000 simple steps per second

This output changes from run to run: the time depends on the computer, the runtime and what else the computer is doing

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 loop_rate.py

Version note

The Run button runs this file in Pyodide 314.0.7, which is CPython 3.14.2 compiled to WebAssembly. There the first line names the platform emscripten, and the loop runs a few times more slowly than in a CPython installed on a computer.

Then JavaScript:

Simple steps per second in JavaScript JavaScript · loop_rate.mjs
// performance.now() where it exists (Node.js, browsers); QuickJS has only Date.now(), in whole milliseconds.
const now = typeof performance === 'object' ? () => performance.now() : () => Date.now();
const STEPS = 1_000_000;

function countUp(steps) {
  let total = 0;
  for (let i = 0; i < steps; i++) total += i;
  return total;
}

const withCommas = (n) => String(Math.round(n)).replace(/\B(?=(\d{3})+(?!\d))/g, ',');

// Nine short tries, keeping the fastest: a slower try lost time to other programs (or ran before the JIT).
let best = Infinity;
let total = 0;
for (let attempt = 0; attempt < 9; attempt++) {
  const start = now();
  total = countUp(STEPS);
  best = Math.min(best, now() - start);
}

if (total !== (STEPS * (STEPS - 1)) / 2) throw new Error(`wrong total: ${total}`);
const engine = typeof process === 'object' ? `Node.js ${process.versions.node} (V8 ${process.versions.v8})` : 'an engine without Node.js';
const rate = STEPS / (Math.max(best, 0.001) / 1000);
console.log(`JavaScript in ${engine}`);
console.log(`${withCommas(STEPS)} loop steps: ${best.toFixed(1)} ms at best`);
console.log(`about ${withCommas(Number(rate.toPrecision(2)))} simple steps per second`);

Output

JavaScript in Node.js 24.21.0 (V8 13.6.233.17-node.53)
1,000,000 loop steps: 1.3 ms at best
about 790,000,000 simple steps per second

This output changes from run to run: the time depends on the computer, the runtime and what else the computer is doing

Recorded with Node.js 24.21.0 on macOS 26 arm64. To run it yourself: mise exec node@24.21.0 -- node loop_rate.mjs

Version note

The Run button runs this file in QuickJS (quickjs-emscripten 0.32.0), a JavaScript interpreter compiled to WebAssembly, so the first line says an engine without Node.js. This QuickJS has no performance object, so the program falls back to Date.now(), which counts whole milliseconds; the loop takes long enough there for that to be precise enough.

The outputs shown above were recorded with CPython and Node.js on one computer; the Run buttons measure the two browser runtimes on yours. The four rates for the same million additions are far apart, and each program checks the total, so none of them skips work:

  • CPython compiles Python to bytecode and interprets it: a few tens of millions of steps per second.
  • V8, the engine inside Node.js and Chrome, compiled the loop to machine code with its just-in-time (JIT) compiler: close to a billion steps per second.
  • Pyodide, the same CPython compiled to WebAssembly, and QuickJS, a JavaScript interpreter compiled to WebAssembly, manage millions of steps per second: a few times fewer than CPython and roughly a hundred times fewer than V8.

A tight loop flatters a JIT

A loop of additions is the best case for a JIT compiler: everything stays in processor registers. Real steps read memory, call functions and allocate objects, so plan with about 10⁸ simple steps per second for compiled code and JIT engines, not with the rate this loop shows. For CPython plan with about 10⁷, and expect less in the browser runtimes.

Code that a runtime runs in C, such as Python’s sorted(), sum() and dictionary operations, does not pay the interpreter’s cost for every element, so in Python a built-in is often faster than a hand-written loop of the same growth rate. The documented costs of Python’s built-in types are listed on the page of the Python documentation named in this lesson’s references.

From limits to a target complexity

A problem statement tells you most of what you need: the largest input, the time limit, and often more. Before choosing an approach, read:

  • The largest n, and whether there are several inputs. “The sum of n over all test cases is at most 200,000” means the total is limited, not each test, so an O(n log n) algorithm per test is fine.
  • The time limit, multiplied by the steps per second of the language you write in.
  • The size of the values. Values up to 10⁹ summed over 200,000 items overflow a 32-bit integer in Java or C++; Python’s integers grow as needed.
  • The number of queries. 100,000 queries on an array of 100,000 items cost 10¹⁰ steps if each query scans the array, so each query has to be O(log n) or O(1) after some preparation.

Then compare each idea’s step count with the budget. This worksheet does it for four made-up problems and two budgets, 10⁸ steps per second for compiled code and 10⁷ for CPython; press Edit to try your own limits:

A constraints worksheet Python · worksheet.py
import math

COMPILED = 10**8  # simple steps per second in compiled code or a JIT engine (a planning figure)
CPYTHON = 10**7  # the same for CPython, leaving room for steps that do more than one addition

COSTS = {
    "log2 n": lambda n: math.log2(n),
    "n": lambda n: n,
    "n log2 n": lambda n: n * math.log2(n),
    "n^2": lambda n: n**2,
    "n^3": lambda n: n**3,
    "2^n * n": lambda n: 2**n * n,
}

# (what the problem asks, the largest n it allows, its time limit in seconds, the candidates you thought of)
PROBLEMS = [
    ("two marks that add up to k", 200_000, 1, ["n^2", "n log2 n"]),
    ("the best team out of n people", 20, 1, ["2^n * n"]),
    ("travel times between all pairs of n towns", 400, 2, ["n^3"]),
    ("the number of binary digits of n", 10**18, 1, ["n", "log2 n"]),
]


def shown(x):
    if x < 10_000:
        return f"{round(x):,}"
    exponent = len(str(int(x))) - 1
    return f"{x / 10**exponent:.1f}e{exponent}"


for task, n, seconds, candidates in PROBLEMS:
    print(f"{task}: n <= {n:,}" if n < 10**7 else f"{task}: n <= {shown(n)}", f"in {seconds} s")
    for name in candidates:
        steps = COSTS[name](n)
        if steps <= CPYTHON * seconds:
            verdict = "fits, even in CPython"
        elif steps <= COMPILED * seconds:
            verdict = "fits in compiled code, too slow for CPython"
        else:
            verdict = "too slow"
        print(f"  {name:<9}{shown(steps):>8} steps  {verdict}")

Output

two marks that add up to k: n <= 200,000 in 1 s
  n^2        4.0e10 steps  too slow
  n log2 n    3.5e6 steps  fits, even in CPython
the best team out of n people: n <= 20 in 1 s
  2^n * n     2.1e7 steps  fits in compiled code, too slow for CPython
travel times between all pairs of n towns: n <= 400 in 2 s
  n^3         6.4e7 steps  fits in compiled code, too slow for CPython
the number of binary digits of n: n <= 1.0e18 in 1 s
  n          1.0e18 steps  too slow
  log2 n         60 steps  fits, even in CPython

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 worksheet.py

Together with the table of largest inputs, the worksheet gives the usual reading of the most common limits. With n up to about 10 or 11, even n! fits; up to about 20 to 25, 2ⁿ fits; a few hundred allows n³; a few thousand allows n²; and from about 100,000 upwards you need n log n or better. It also shows that the language matters near the edges: the team problem and the towns problem fit in compiled code but would need a smarter algorithm, or a faster language, in CPython.

GATE CS

The GATE 2027 syllabus for Computer Science and Information Technology lists “Asymptotic worst case time and space complexity” in its Algorithms section, so ranking growth rates and working out step counts as in this lesson is part of what the exam covers. GATE is a computer-based test of multiple-choice, multiple-select and numerical-answer questions, with no program to run, so the per-runtime budgets matter for coding rounds and contests rather than for GATE.

Graphing Calculator Plot n·log2(n), n² and 2ⁿ on the same axes and watch where they cross. Logarithm & Antilog Calculator Work out log2 of a problem's limit, the number of halvings a binary search needs.

Key takeaways

  • From slowest-growing to fastest: 1, log n, √n, n, n log n, n², n³, 2ⁿ, n!. Exponential and factorial costs explode while n is still small; quadratic and cubic costs explode as n reaches thousands and millions.
  • A time limit is a budget of steps: steps per second times seconds. Plan with about 10⁸ steps per second for compiled code and JIT engines and about 10⁷ for CPython.
  • For a budget of 10⁸ steps, the largest inputs are about 11 for n!, 26 for 2ⁿ, 464 for n³, 10,000 for n² and 4.5 million for n log n.
  • Read the largest n, the time limit, the size of the values and the number of queries, then choose the costliest approach whose step count still fits.

Exercise

Exercise · Easy · Python, JavaScript

Find the costliest growth rate that fits a budget

Turn a problem's limits into a target complexity, the way this lesson's worksheet does. Write costliest_fit(n, budget) in Python (or costliestFit(n, budget) in JavaScript). It returns the costliest growth rate on this ladder whose step count for an input of size n is at most budget steps. The ladder, costliest first, with the step count each rate stands for:

  • "n!": n factorial, 1 × 2 × … × n
  • "2^n": 2 to the power n
  • "n^3": n × n × n
  • "n^2": n × n
  • "n log n": n × log2(n)
  • "n": n
  • "log n": log2(n)
  • "1": 1

Go down the ladder from "n!" and return the first rate that fits; a step count equal to the budget fits. Both arguments are whole numbers with n ≥ 1 and budget ≥ 1. For n = 1, n! is a single step (1! = 1), so the answer is "n!" whatever the budget.

n can be as large as 10^18, so the check itself must stay cheap: never compute n! or 2^n for a huge n in full (in Python that number would need more memory than any computer has).

Python · Starter code · costliest_fit.py

def costliest_fit(n, budget):
    """Return the costliest growth rate ("n!", "2^n", ... "1") whose step count for n is at most budget."""
    return "n"
The sample tests · test_costliest_fit.py
from costliest_fit import costliest_fit


def test_factorial_fits_for_ten():
    """n = 10 allows n! within 10^8 steps (10! = 3,628,800)"""
    assert costliest_fit(10, 10**8) == "n!"


def test_subsets_for_twelve():
    """n = 12: 12! is too many steps, 2^12 fits"""
    assert costliest_fit(12, 10**8) == "2^n"


def test_last_n_for_subsets():
    """2^26 fits in 10^8 steps, 2^27 does not"""
    assert costliest_fit(26, 10**8) == "2^n"
    assert costliest_fit(27, 10**8) == "n^3"


def test_cubic_boundary():
    """464^3 fits in 10^8 steps, 465^3 does not"""
    assert costliest_fit(464, 10**8) == "n^3"
    assert costliest_fit(465, 10**8) == "n^2"


def test_two_hundred_thousand():
    """n = 200,000 needs n log n or better"""
    assert costliest_fit(200_000, 10**8) == "n log n"


def test_smaller_budget():
    """5,000 allows n^2 in 10^8 steps but not in 10^7"""
    assert costliest_fit(5_000, 10**8) == "n^2"
    assert costliest_fit(5_000, 10**7) == "n log n"


def test_linear_only():
    """n = 10^8 in 10^8 steps leaves one pass"""
    assert costliest_fit(10**8, 10**8) == "n"


def test_huge_n():
    """n = 10^18 leaves only log n, and the check must stay quick"""
    assert costliest_fit(10**18, 10**8) == "log n"


def test_exact_budget():
    """a step count equal to the budget still fits (10! = 3,628,800; 2^20 = 1,048,576; 10,000^2 = 10^8)"""
    assert costliest_fit(10, 3_628_800) == "n!"
    assert costliest_fit(20, 2**20) == "2^n"
    assert costliest_fit(10_000, 10**8) == "n^2"


def test_logarithms_are_base_2():
    """log n means log2(n): 10^7 * log2(10^7) is about 2.3 * 10^8, and log2(2^20) is 20"""
    assert costliest_fit(10**7, 2 * 10**8) == "n"
    assert costliest_fit(2**20, 15) == "1"


def test_edge_cases():
    """n = 1 fits n! (1! = 1 step); for n = 4 a budget of 1 step fits only constant time"""
    assert costliest_fit(1, 1) == "n!"
    assert costliest_fit(4, 1) == "1"

JavaScript · Starter code · costliest_fit.mjs

/** The costliest growth rate ('n!', '2^n', … '1') whose step count for n is at most budget. */
export function costliestFit(n, budget) {
  return 'n';
}
The sample tests · costliest_fit.test.mjs
import { test, assert } from 'toolverse:test';
import { costliestFit } from './costliest_fit.mjs';

test('n = 10 allows n! within 10^8 steps (10! = 3,628,800)', () => assert.equal(costliestFit(10, 1e8), 'n!'));
test('n = 12: 12! is too many steps, 2^12 fits', () => assert.equal(costliestFit(12, 1e8), '2^n'));
test('2^26 fits in 10^8 steps, 2^27 does not', () => {
  assert.equal(costliestFit(26, 1e8), '2^n');
  assert.equal(costliestFit(27, 1e8), 'n^3');
});
test('464^3 fits in 10^8 steps, 465^3 does not', () => {
  assert.equal(costliestFit(464, 1e8), 'n^3');
  assert.equal(costliestFit(465, 1e8), 'n^2');
});
test('n = 200,000 needs n log n or better', () => assert.equal(costliestFit(200_000, 1e8), 'n log n'));
test('5,000 allows n^2 in 10^8 steps but not in 10^7', () => {
  assert.equal(costliestFit(5_000, 1e8), 'n^2');
  assert.equal(costliestFit(5_000, 1e7), 'n log n');
});
test('n = 10^8 in 10^8 steps leaves one pass', () => assert.equal(costliestFit(1e8, 1e8), 'n'));
test('n = 10^18 leaves only log n, and the check must stay quick', () => assert.equal(costliestFit(1e18, 1e8), 'log n'));
test('a step count equal to the budget still fits (10! = 3,628,800; 2^20 = 1,048,576; 10,000^2 = 10^8)', () => {
  assert.equal(costliestFit(10, 3_628_800), 'n!');
  assert.equal(costliestFit(20, 2 ** 20), '2^n');
  assert.equal(costliestFit(10_000, 1e8), 'n^2');
});
test('log n means log2(n): 10^7 * log2(10^7) is about 2.3 * 10^8, and log2(2^20) is 20', () => {
  assert.equal(costliestFit(1e7, 2e8), 'n');
  assert.equal(costliestFit(2 ** 20, 15), '1');
});
test('n = 1 fits n! (1! = 1 step); for n = 4 a budget of 1 step fits only constant time', () => {
  assert.equal(costliestFit(1, 1), 'n!');
  assert.equal(costliestFit(4, 1), '1');
});
A hint

Multiply 2 × 3 × 4 … one factor at a time and stop as soon as the product is larger than the budget: for a huge n that happens after a few steps. For 2^n, compare n with log2(budget) instead of building the power (in Python, 2**n <= budget is the same test as n < budget.bit_length()).

The sample tests run on this device, in your browser (Pyodide, QuickJS): nothing is sent to mysmartcopilot.com. The first run of each language downloads it: Python (about 13.5 MB) or JavaScript (about 0.6 MB), which is kept for the next runs. A check in your browser is feedback for you, not proof that the code is right for every input.

Check yourself

6 questions about this lesson. Every answer and why it is right is on the page, behind “Show the answer”. Your score stays in this browser.

  1. Question 1 of 6 Put these growth rates in order, from the one that grows most slowly to the one that grows fastest.

    Give each item its position, from 1 (first).

    Show the answer to question 1

    Answer:

    1. log n
    2. n
    3. n log n
    4. n^2
    5. 2^n
    6. n!

    Halving (log n) beats one pass (n), which beats sorting (n log n), which beats all pairs (n^2). Every polynomial is eventually overtaken by 2^n, and n! grows faster still: at n = 100 the growth table shows 1.3e30 for 2^n and 9.3e157 for n!.

  2. Question 2 of 6 With a budget of 10^8 steps, what is the largest n for which an O(2^n) algorithm fits? Use the table that largest_n.py printed.

    Type a number.

    Show the answer to question 2

    Answer: 26

    2^26 is 67,108,864 steps, which fits; 2^27 is 134,217,728, which does not. The 1e8 column of largest_n.py shows 26 in the 2^n row.

  3. Question 3 of 6 A problem allows n up to 200,000 and gives one second. Which target should you aim for before you write any code?

    Choose one answer.

    Show the answer to question 3

    Answer: O(n log n) or better

    n^2 is 4 × 10^10 steps, hundreds of times more than a second allows even in compiled code. n log2 n is about 3.5 × 10^6 steps, which fits even in CPython, as the worksheet printed.

  4. Question 4 of 6 Which of these fit in a budget of 10^8 steps when n = 5,000?

    Choose every answer that is right.

    Show the answer to question 4

    Answer:

    • O(n^2)
    • O(n)
    • O(n log n)

    5,000^2 is 2.5 × 10^7 steps, which fits; 5,000^3 is 1.25 × 10^11, more than a thousand times too many. Anything cheaper than n^2 fits as well.

  5. Question 5 of 6 What happens to the step count of an O(2^n) algorithm when n doubles?

    Choose one answer.

    Show the answer to question 5

    Answer: It is squared

    2^(2n) = (2^n)^2, so the count is squared: 2^20 is about a million, 2^40 about a million million. Doubling n doubles a linear count, multiplies a quadratic one by four and adds one step to log2 n.

  6. Question 6 of 6 The same loop ran at very different speeds in CPython, Pyodide, V8 and QuickJS. Which explanation fits?

    Choose one answer.

    Show the answer to question 6

    Answer: V8 compiles the loop to machine code, while CPython, Pyodide and QuickJS interpret it, two of them inside WebAssembly

    Every runtime did the same million additions (each program checks the total). What differs is how a step is executed: V8's just-in-time compiler turns the loop into machine code, the others interpret bytecode, and the browser runtimes run inside WebAssembly. That is why time budgets depend on the runtime as well as on the growth rate.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.