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

Testing algorithms: edge cases and stress tests

List edge cases for arrays, strings and graphs, build a seeded random generator and a brute-force oracle, then stress-test fast code and shrink a failing case.

  • Beginner
  • 25 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

  • List edge cases systematically for array, string and graph inputs
  • Build a seeded random generator and a brute-force oracle
  • Test an optimised solution against a brute force and shrink a failing case

Before you start

On this page

An algorithm can pass every example in a problem statement and still be wrong: the examples show what the problem means, not where solutions break. This lesson gives you two habits that find the breaks before a judge or a user does. First, a checklist of edge cases that you run every solution through. Second, a stress test: thousands of small random inputs, each solved by your fast solution and by a slow one that is obviously right, until the two disagree. You will also shrink a failing input until removing any one of its elements makes the failure go away, which usually leaves a case small enough for the bug to explain itself.

Write down what the answer should be

A test needs an expected answer, so decide first what the function returns in the awkward cases. Take “the second-largest value of a list”. Is the second-largest of [9, 9, 7] 9 or 7? What does [6, 6, 6] give, or an empty list? The examples below settle it: the second-largest distinct value, and None when there are fewer than two distinct values. Writing that sentence down is half the work of testing; the other half is checking it.

An edge-case checklist

Most bugs hide in the same few places. Before trusting a solution, run it on one input of each kind that applies:

  • Lists and arrays: empty; one element; two elements; all values equal; already sorted; sorted in reverse; duplicates, especially of the largest or smallest value; negative numbers and zero; the largest and smallest values the problem allows (in Java, C++ and Go a sum of large values can overflow a fixed-size integer; Python’s integers grow instead); the largest n, to check the running time.
  • Strings: the empty string; one character; every character the same; a palindrome; spaces, upper and lower case; characters outside ASCII, such as é or an emoji, if the problem allows them.
  • Graphs: a single vertex; no edges at all; several disconnected parts; a self-loop and repeated edges; a cycle; a long path, which makes recursive searches go very deep; a complete graph, the densest case.
  • Numbers: 0, 1 and negative values; the limits of the type; for decimals, values such as 0.1 + 0.2, which are not exact in binary floating point.

Here is a first attempt at second_largest, run on one case from each line of the list checklist that applies:

A first attempt and nine edge cases Python · edge_cases.py
def second_largest(values):
    """The second-largest distinct value, or None with fewer than two distinct values: a first attempt."""
    return sorted(values)[-2]


# (input, expected): one case for each line of an edge-case checklist.
CASES = [
    ([4, 9, 7], 7),  # an ordinary case
    ([1, 2, 3, 4], 3),  # already sorted
    ([4, 3, 2, 1], 3),  # reverse sorted
    ([-8, -3, -5], -5),  # only negative numbers
    ([0, 0, 5], 0),  # zeros
    ([9, 9, 7], 7),  # the largest value twice
    ([6, 6, 6], None),  # all equal: no second value
    ([42], None),  # one element
    ([], None),  # empty
]

for values, expected in CASES:
    try:
        got = second_largest(values)
    except Exception as error:
        got = f"{type(error).__name__}: {error}"
    verdict = "ok  " if got == expected else "FAIL"
    print(f"{verdict} {str(values):<15} expected {expected!r}, got {got!r}")

Output

ok   [4, 9, 7]       expected 7, got 7
ok   [1, 2, 3, 4]    expected 3, got 3
ok   [4, 3, 2, 1]    expected 3, got 3
ok   [-8, -3, -5]    expected -5, got -5
ok   [0, 0, 5]       expected 0, got 0
FAIL [9, 9, 7]       expected 7, got 9
FAIL [6, 6, 6]       expected None, got 6
FAIL [42]            expected None, got 'IndexError: list index out of range'
FAIL []              expected None, got 'IndexError: list index out of range'

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

Five ordinary-looking cases pass and four fail: the duplicate largest value, the all-equal list, one element and the empty list. Sorting and taking the second-to-last element ignores duplicates and assumes at least two elements. The fixed version walks the list once and keeps the two largest distinct values seen so far:

The fixed version on the same cases Python · edge_cases_fixed.py
def second_largest(values):
    """The second-largest distinct value, or None with fewer than two distinct values: one pass, two variables."""
    largest = second = None
    for value in values:
        if largest is None or value > largest:
            largest, second = value, largest
        elif value != largest and (second is None or value > second):
            second = value
    return second


CASES = [
    ([4, 9, 7], 7),
    ([1, 2, 3, 4], 3),
    ([4, 3, 2, 1], 3),
    ([-8, -3, -5], -5),
    ([0, 0, 5], 0),
    ([9, 9, 7], 7),
    ([6, 6, 6], None),
    ([42], None),
    ([], None),
]

failures = [(values, expected) for values, expected in CASES if second_largest(values) != expected]
print(f"{len(CASES) - len(failures)} of {len(CASES)} edge cases pass")
for values, expected in failures:
    print(f"FAIL {values}: expected {expected!r}, got {second_largest(values)!r}")

Output

9 of 9 edge cases pass

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

A table of cases like this is easy to keep. In a project, put the same table into a test framework: Python’s unittest has subTest, which reports every failing case separately with its values instead of stopping at the first one.

Stress testing against a brute force

A checklist only covers the cases you thought of. A stress test covers the ones you did not:

  1. Write a brute force: the simplest correct solution you can think of, however slow. It is the oracle that decides who is right, so it must be easy to trust; reusing the fast solution’s idea would reuse its bugs.
  2. Write a generator of small random inputs. Small matters: short lists with small values hit equal values, negative values and boundaries far more often than long ones, and a failing case of five numbers is easy to read.
  3. Seed the generator, so that the same inputs come out in the same order on every run and a failure can be repeated while you debug.
  4. Loop: generate an input, run both solutions, compare, and stop at the first difference.
A stress-test loop: a seeded generator feeds a brute force and a fast solution; if they agree, try again; if not, shrink and report.Seeded generator:a small random inputBrute force:slow, obviously rightFast solution:the one you testSame answer?Shrink: remove elementswhile the answers differPrint the seed andthe shrunk failing inputyes: nextno

The stress-test loop

Text description of the diagram

The diagram shows a loop from top to bottom.

  1. A seeded generator makes a small random input.
  2. The brute force solves it: slow, but obviously right.
  3. The fast solution, the one you are testing, solves the same input.
  4. A check asks whether the two gave the same answer. If yes, an arrow goes back to the generator for the next input.
  5. If not, the input is shrunk: elements are removed one at a time for as long as the two answers still differ.
  6. Finally the seed and the shrunk failing input are printed, ready for debugging.

The next program stress-tests a fast solution of the maximum-subarray problem (the largest sum of a slice of consecutive values), which a later lesson of this track develops properly. The fast version has a bug planted in it, and the stress test has not been told where:

A stress test with a seeded generator Python · stress_test.py
import random


def max_subarray_brute(values):
    """The largest sum of a non-empty slice, by trying every slice: slow, but too simple to get wrong."""
    best = values[0]
    for start in range(len(values)):
        total = 0
        for end in range(start, len(values)):
            total += values[end]
            best = max(best, total)
    return best


def max_subarray_fast(values):
    """The same in one pass (Kadane's algorithm), with a planted bug."""
    best = current = values[0]
    for value in values[1:-1]:
        current = max(value, current + value)
        best = max(best, current)
    return best


def random_case(rng):
    """A small list: short lists with small numbers hit the special cases often."""
    return [rng.randint(-5, 5) for _ in range(rng.randint(1, 8))]


def disagree(values):
    return max_subarray_fast(values) != max_subarray_brute(values)


def shrink(values):
    """Remove one element at a time, keeping each removal after which the two still disagree."""
    smaller = True
    while smaller:
        smaller = False
        for i in range(len(values)):
            candidate = values[:i] + values[i + 1 :]
            if candidate and disagree(candidate):
                values, smaller = candidate, True
                break
    return values


SEED = 9  # a fixed seed: the same cases, in the same order, on every run
rng = random.Random(SEED)
for case in range(1, 201):
    values = random_case(rng)
    if disagree(values):
        print(f"seed {SEED}, case {case} fails: {values}")
        print(f"  brute force {max_subarray_brute(values)}, fast {max_subarray_fast(values)}")
        smallest = shrink(values)
        print(f"shrunk to {smallest}")
        print(f"  brute force {max_subarray_brute(smallest)}, fast {max_subarray_fast(smallest)}")
        break
else:
    print("200 random cases, no difference")

Output

seed 9, case 3 fails: [1, -3, -3, -2, -5, -4, -3, 3]
  brute force 3, fast 1
shrunk to [-3, 3]
  brute force 3, fast -3

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

random.Random(SEED) creates a generator of its own from the seed 9, so the third case is the same failing list on every run, and the program prints the seed with it, ready to repeat. The fast version’s loop runs over values[1:-1], which leaves out the last element, so it never considers a slice that ends with the last element.

Keep the failing input, not only the seed

Python’s documentation guarantees that a seed reproduces the same random() values in future versions, but not that other functions of the module, such as randint(), will keep producing the same values. A seed reproduces a failure on the version you ran it with; to keep a failing case as a regression test, store the input itself.

JavaScript cannot do the same with Math.random(): the browser or engine chooses its seed, and a program cannot set it. So the JavaScript version brings its own generator, a few lines of xorshift, from George Marsaglia’s paper on xorshift generators. It is fast and repeatable, which is all a test needs, but it is no good for anything secret, just like Math.random() and Python’s random module (use crypto.getRandomValues() or Python’s secrets module for that):

The same stress test in JavaScript JavaScript · stress_test.mjs
// Math.random() cannot be seeded, so a failure it finds cannot be repeated. This small generator can: xorshift32,
// with the shifts 13, 17 and 5 from George Marsaglia's paper "Xorshift RNGs". Same seed, same numbers, every run.
function makeRandom(seed) {
  let x = seed >>> 0 || 1;
  return () => {
    x ^= x << 13;
    x ^= x >>> 17;
    x ^= x << 5;
    return x >>> 0;
  };
}
const between = (next, low, high) => low + (next() % (high - low + 1));

/** The largest sum of a non-empty slice, by trying every slice: slow, but too simple to get wrong. */
function maxSubarrayBrute(values) {
  let best = values[0];
  for (let start = 0; start < values.length; start++) {
    let total = 0;
    for (let end = start; end < values.length; end++) {
      total += values[end];
      best = Math.max(best, total);
    }
  }
  return best;
}

/** The same in one pass (Kadane's algorithm), with a planted bug. */
function maxSubarrayFast(values) {
  let best = values[0];
  let current = values[0];
  for (const value of values.slice(1, -1)) {
    current = Math.max(value, current + value);
    best = Math.max(best, current);
  }
  return best;
}

const disagree = (values) => maxSubarrayFast(values) !== maxSubarrayBrute(values);

/** Remove one element at a time, keeping each removal after which the two still disagree. */
function shrink(values) {
  let smaller = true;
  while (smaller) {
    smaller = false;
    for (let i = 0; i < values.length; i++) {
      const candidate = [...values.slice(0, i), ...values.slice(i + 1)];
      if (candidate.length && disagree(candidate)) {
        values = candidate;
        smaller = true;
        break;
      }
    }
  }
  return values;
}

const show = (values) => `[${values.join(', ')}]`;
const SEED = 37; // a fixed seed: the same cases, in the same order, on every run
const next = makeRandom(SEED);
let found = false;
for (let c = 1; c <= 200 && !found; c++) {
  const values = Array.from({ length: between(next, 1, 8) }, () => between(next, -5, 5));
  if (disagree(values)) {
    found = true;
    console.log(`seed ${SEED}, case ${c} fails: ${show(values)}`);
    console.log(`  brute force ${maxSubarrayBrute(values)}, fast ${maxSubarrayFast(values)}`);
    const smallest = shrink(values);
    console.log(`shrunk to ${show(smallest)}`);
    console.log(`  brute force ${maxSubarrayBrute(smallest)}, fast ${maxSubarrayFast(smallest)}`);
  }
}
if (!found) console.log('200 random cases, no difference');

Output

seed 37, case 1 fails: [-2, 0, 1, -1, -1, -2, -4, 2]
  brute force 2, fast 1
shrunk to [-4, 2]
  brute force 2, fast -4

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

Shrink the failing case before you debug

The first failing input is random, so it is usually longer than it needs to be. Both programs shrink it before printing: they try removing each element in turn and keep any removal after which the two solutions still disagree, until no single removal keeps the bug alive. Eight numbers became two, [-3, 3] in Python and [-4, 2] in JavaScript, and two numbers are enough to see that the last element is the problem.

Testing tools do the same at a larger scale. Go has fuzzing built into go test: it changes inputs automatically, guided by which code they reach, and when it finds a failure it minimises the input and saves it under testdata/fuzz as a test that runs from then on. For Python, the Hypothesis library chooses the inputs for you from a description of the values a test should work for.

Testing the tests

The same thinking can check the tests themselves. Start from a solution you trust: it passes all its tests, while an unfinished version, such as an exercise’s starter code, fails at least one. Then make copies of the solution, each with one operator flipped, a < turned into >= say, and run the tests on every copy. At least one test should fail for each copy. A copy that passes every test is a warning: either the flip changes nothing the task asks for, or it is a broken solution that the tests cannot tell from a correct one, and then the tests need another case. This idea is called mutation testing, and it is a good way to test your own test suites.

Text Diff Paste the outputs of the brute force and the fast solution to see exactly where they differ.

Key takeaways

  • Decide what the answer is in every awkward case before testing, then run each solution through a checklist: empty, one element, all equal, sorted, reversed, duplicates, negatives and zero, extreme values; for graphs, disconnected parts, self-loops, cycles and long paths.
  • A stress test compares the fast solution with a slow, obviously correct brute force on many small random inputs and stops at the first difference.
  • Seed the generator so a failure repeats: random.Random(seed) in Python, a small generator such as xorshift in JavaScript, because Math.random() cannot be seeded. Keep failing inputs as tests.
  • Shrink a failing input by removing elements while it still fails; a two-element case usually points straight at the bug.

Exercise

Exercise · Easy · Python, JavaScript

Stress-test a buggy inversion counter

An inversion of a list is a pair of positions i < j whose values are out of order: values[i] > values[j]. The starter file has two inversion counters: count_inversions_brute, which checks every pair, and count_inversions_fast, a merge sort that counts while it merges. The fast one has a bug. Do not fix it: write the tests that find it.

- find_counterexample(seed) (JavaScript: findCounterexample(seed)) generates random lists of 1 to 8 whole numbers from a generator seeded with seed and returns the first list on which the two counters disagree. The same seed must always give the same list, and different seeds should lead to different lists. In Python use random.Random(seed); in JavaScript use the starter's makeRandom(seed), because Math.random() cannot be seeded. - shrink(values) takes a list on which the counters disagree and returns a smaller one on which they still disagree: keep removing one element at a time while they keep disagreeing, so that removing any single element of the result makes them agree.

The tests check what your functions return with their own copies of the two counters, so changing the counters in your file does not help.

Python · Starter code · stress.py

import random


def count_inversions_brute(values):
    """Pairs i < j with values[i] > values[j], by checking every pair."""
    return sum(1 for i in range(len(values)) for j in range(i + 1, len(values)) if values[i] > values[j])


def count_inversions_fast(values):
    """The same with a merge sort that counts while it merges. It has a bug: find an input that shows it."""

    def sort(items):
        if len(items) <= 1:
            return items, 0
        middle = len(items) // 2
        left, left_count = sort(items[:middle])
        right, right_count = sort(items[middle:])
        merged, count, i, j = [], left_count + right_count, 0, 0
        while i < len(left) and j < len(right):
            if left[i] < right[j]:
                merged.append(left[i])
                i += 1
            else:
                merged.append(right[j])
                j += 1
                count += len(left) - i
        return merged + left[i:] + right[j:], count

    return sort(list(values))[1]


def find_counterexample(seed):
    """The first random list (1 to 8 numbers, from random.Random(seed)) on which the two counters disagree."""
    return []


def shrink(values):
    """A smaller list on which the counters still disagree: remove elements one at a time while they do."""
    return values
The sample tests · test_stress.py
from stress import find_counterexample, shrink


def brute(values):
    return sum(1 for i in range(len(values)) for j in range(i + 1, len(values)) if values[i] > values[j])


def fast(values):
    def sort(items):
        if len(items) <= 1:
            return items, 0
        middle = len(items) // 2
        left, left_count = sort(items[:middle])
        right, right_count = sort(items[middle:])
        merged, count, i, j = [], left_count + right_count, 0, 0
        while i < len(left) and j < len(right):
            if left[i] < right[j]:
                merged.append(left[i])
                i += 1
            else:
                merged.append(right[j])
                j += 1
                count += len(left) - i
        return merged + left[i:] + right[j:], count

    return sort(list(values))[1]


def check_failing(values):
    assert isinstance(values, list), f"expected a list, got {values!r}"
    assert 1 <= len(values) <= 8, f"expected 1 to 8 numbers, got {values!r}"
    assert brute(values) != fast(values), f"the counters agree on {values!r}"


def test_finds_a_failing_list():
    """returns a list of 1 to 8 numbers on which the counters disagree"""
    for seed in [1, 2, 3, 4, 5]:
        check_failing(find_counterexample(seed))


def test_same_seed_same_list():
    """the same seed always gives the same list"""
    assert find_counterexample(11) == find_counterexample(11)


def test_different_seeds_search_differently():
    """different seeds give different random lists, so the lists really come from the generator"""
    found = {tuple(find_counterexample(seed)) for seed in [1, 2, 3, 4, 5]}
    assert len(found) >= 2, f"seeds 1 to 5 all gave {sorted(found)!r}"


def test_shrink_keeps_it_failing():
    """shrinking a failing list returns a list on which the counters still disagree"""
    check_failing(shrink([3, 1, 2, 2, 0, 1]))


def test_shrink_is_as_small_as_removal_allows():
    """removing any one element of the shrunk list makes the counters agree"""
    smallest = shrink([3, 1, 2, 2, 0, 1])
    for i in range(len(smallest)):
        smaller = smallest[:i] + smallest[i + 1 :]
        assert brute(smaller) == fast(smaller), f"{smaller!r} still fails, so {smallest!r} can shrink further"


def test_shrinks_a_found_list():
    """the list found for a seed shrinks to one that still fails"""
    check_failing(shrink(find_counterexample(3)))

JavaScript · Starter code · stress.mjs

/** A small seeded generator (xorshift32): the same seed gives the same numbers on every run. */
export function makeRandom(seed) {
  let x = seed >>> 0 || 1;
  return () => {
    x ^= x << 13;
    x ^= x >>> 17;
    x ^= x << 5;
    return x >>> 0;
  };
}

/** Pairs i < j with values[i] > values[j], by checking every pair. */
export function countInversionsBrute(values) {
  let count = 0;
  for (let i = 0; i < values.length; i++) for (let j = i + 1; j < values.length; j++) if (values[i] > values[j]) count++;
  return count;
}

/** The same with a merge sort that counts while it merges. It has a bug: find an input that shows it. */
export function countInversionsFast(values) {
  let items = [...values];
  let count = 0;
  // Bottom-up merge sort: merge runs of width 1, then 2, 4 … until one run is left.
  for (let width = 1; width < items.length; width *= 2) {
    const merged = [];
    for (let start = 0; start < items.length; start += 2 * width) {
      const left = items.slice(start, start + width);
      const right = items.slice(start + width, start + 2 * width);
      let i = 0;
      let j = 0;
      while (i < left.length && j < right.length) {
        if (left[i] < right[j]) merged.push(left[i++]);
        else {
          merged.push(right[j++]);
          count += left.length - i;
        }
      }
      merged.push(...left.slice(i), ...right.slice(j));
    }
    items = merged;
  }
  return count;
}

/** The first random list (1 to 8 numbers, from makeRandom(seed)) on which the two counters disagree. */
export function findCounterexample(seed) {
  return [];
}

/** A smaller list on which the counters still disagree: remove elements one at a time while they do. */
export function shrink(values) {
  return values;
}
The sample tests · stress.test.mjs
import { test, assert } from 'toolverse:test';
import { findCounterexample, shrink } from './stress.mjs';

function brute(values) {
  let count = 0;
  for (let i = 0; i < values.length; i++) for (let j = i + 1; j < values.length; j++) if (values[i] > values[j]) count++;
  return count;
}

function fast(values) {
  let items = [...values];
  let count = 0;
  // Bottom-up merge sort: merge runs of width 1, then 2, 4 … until one run is left.
  for (let width = 1; width < items.length; width *= 2) {
    const merged = [];
    for (let start = 0; start < items.length; start += 2 * width) {
      const left = items.slice(start, start + width);
      const right = items.slice(start + width, start + 2 * width);
      let i = 0;
      let j = 0;
      while (i < left.length && j < right.length) {
        if (left[i] < right[j]) merged.push(left[i++]);
        else {
          merged.push(right[j++]);
          count += left.length - i;
        }
      }
      merged.push(...left.slice(i), ...right.slice(j));
    }
    items = merged;
  }
  return count;
}

function checkFailing(values) {
  assert.ok(Array.isArray(values), `expected an array, got ${JSON.stringify(values)}`);
  assert.ok(values.length >= 1 && values.length <= 8, `expected 1 to 8 numbers, got ${JSON.stringify(values)}`);
  assert.ok(brute(values) !== fast(values), `the counters agree on ${JSON.stringify(values)}`);
}

test('returns a list of 1 to 8 numbers on which the counters disagree', () => {
  for (const seed of [1, 2, 3, 4, 5]) checkFailing(findCounterexample(seed));
});
test('the same seed always gives the same list', () => assert.deepEqual(findCounterexample(11), findCounterexample(11)));
test('different seeds give different random lists, so the lists really come from the generator', () => {
  const found = new Set([1, 2, 3, 4, 5].map((seed) => JSON.stringify(findCounterexample(seed))));
  assert.ok(found.size >= 2, `seeds 1 to 5 all gave ${[...found].join(' ')}`);
});
test('shrinking a failing list returns a list on which the counters still disagree', () => checkFailing(shrink([3, 1, 2, 2, 0, 1])));
test('removing any one element of the shrunk list makes the counters agree', () => {
  const smallest = shrink([3, 1, 2, 2, 0, 1]);
  for (let i = 0; i < smallest.length; i++) {
    const smaller = [...smallest.slice(0, i), ...smallest.slice(i + 1)];
    assert.ok(brute(smaller) === fast(smaller), `${JSON.stringify(smaller)} still fails, so ${JSON.stringify(smallest)} can shrink further`);
  }
});
test('the list found for a seed shrinks to one that still fails', () => checkFailing(shrink(findCounterexample(3))));
A hint

Small lists with small values find most bugs: try lengths from 1 to 8 and values from 0 to 3, so that equal values are common. For shrink, copy the list without one element (values[:i] + values[i + 1:] in Python), keep the copy when the counters still disagree, and start again from the shorter list until no single removal keeps them apart.

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 A function takes a list of whole numbers. Which of these belong on its edge-case checklist?

    Choose every answer that is right.

    Show the answer to question 1

    Answer:

    • A list where every value is the same
    • Only negative numbers, and zero
    • An empty list and a list with one element

    The examples in a statement are a start, not a checklist: they rarely include the empty list, a single element, all-equal values or negative numbers, which is exactly where the first attempt at second_largest failed.

  2. Question 2 of 6 Why does a stress test create its random generator from a fixed seed?

    Choose one answer.

    Show the answer to question 2

    Answer: So that a failure can be repeated exactly, as often as needed while you debug

    The same seed gives the same inputs in the same order, so the case that failed comes back on every run. A seed does not make the numbers better; it makes them repeatable.

  3. Question 3 of 6 What makes a good brute-force solution to compare a fast one against?

    Choose one answer.

    Show the answer to question 3

    Answer: It is so simple that it is obviously correct, even if it is slow

    The brute force is the reference that decides who is right, so it must be easy to trust. Sharing the fast solution's idea would share its bugs, and it only ever runs on small inputs, so speed does not matter.

  4. Question 4 of 6 Put the steps of a stress test in order.

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

    Show the answer to question 4

    Answer:

    1. Generate a small random input from a seeded generator
    2. Run the brute force and the fast solution on it
    3. Compare the two answers
    4. Stop at the first difference and print the input
    5. Shrink the failing input before debugging

    The loop repeats the first three steps until the answers differ; then it stops, prints what failed and shrinks it until removing any one element would hide the bug.

  5. Question 5 of 6 Why does the JavaScript stress test use its own xorshift generator instead of Math.random()?

    Choose one answer.

    Show the answer to question 5

    Answer: Math.random() cannot be given a seed, so its numbers cannot be repeated

    MDN's reference says the seed of Math.random() is chosen by the implementation and cannot be chosen or reset, so a failing input it produced could not be generated again.

  6. Question 6 of 6 The Python stress test shrank its failing case to [-3, 3]: the brute force says 3, the fast version says -3. Where would you look for the bug first?

    Choose one answer.

    Show the answer to question 6

    Answer: At how the fast version handles the last element, because the best slice is the last element alone

    With two elements and the answer 3, the only slice that matters is the last element. The fast version never looks at it: its loop runs over values[1:-1], which stops one element early.

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.