Coding Interview Workshop · Lesson 21

Use a differential oracle and generated cases

Find the first value that occurs exactly once, returning a nullable integer when no such value exists.

Clarify the observable contract

Find the first value that occurs exactly once, returning a nullable integer when no such value exists. The main learning objective is testing: compare an optimized count-map implementation with an independently structured nested-loop oracle. A small specified integer generator supplies reproducible cases without assuming that framework random sequences are stable across all runtime versions.

The oracle and optimized method share a public contract but use different control structures. One counts every candidate through nested loops; the other builds a frequency map and makes a second ordered pass. This reduces the chance that a copied indexing mistake or selection shortcut appears identically in both implementations.

Walk through the verified program

Two hundred trials use lengths zero through twelve cyclically and values from minus two through two. Fast counts occurrences and then preserves input order when selecting the first unique value. Oracle recounts each candidate by scanning the entire array. Every trial must agree before the program prints differential checks:200.

The generator is a specified unchecked unsigned recurrence, followed by modulo five and a shift into the small signed domain. Its purpose is varied reproducible fixtures, not statistical randomness or security. The final count reports completed comparisons, while any mismatch throws before printing a misleading success count.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

uint state=17; int checks=0;
for(int trial=0;trial<200;trial++) {
    int length=trial%13; int[] data=new int[length];
    for(int i=0;i<length;i++) { state=unchecked(state*1664525+1013904223); data[i]=(int)(state%5)-2; }
    if(Fast(data)!=Oracle(data)) throw new Exception("differential mismatch"); checks++;
}
Console.WriteLine("differential checks:"+checks);
static int? Fast(int[] data) { var count=new Dictionary<int,int>(); foreach(int x in data) count[x]=count.GetValueOrDefault(x)+1; foreach(int x in data) if(count[x]==1) return x; return null; }
static int? Oracle(int[] data) { foreach(int x in data) { int count=0; foreach(int y in data) if(x==y) count++; if(count==1) return x; } return null; }

Account for time, storage and allocations

Fast uses expected O(n) time and O(u) stored counts. Oracle can take O(n squared) time but requires constant auxiliary state. The slow method is intentionally retained only for small verification inputs, where simplicity and independence can be more valuable than speed.

Because lengths remain at most twelve, the intentionally quadratic oracle is cheap enough for this bounded harness. Generated-case count and input bounds should be included in the test report. A passing count alone does not describe which contracts were tested or imply coverage of every integer value and array length.

Review assumptions and likely defects

Two implementations sharing the same mistaken selection rule can agree while both are wrong. Generated tests therefore complement explicit examples such as empty input, all duplicates and a unique negative value. Passing two hundred cases is bounded evidence, not an exhaustive proof or a benchmark.

The nullable return distinguishes no unique value from a legitimate zero. Returning a sentinel zero would be ambiguous in this generated domain. Also preserve first occurrence: iterating sorted dictionary keys or selecting minimum unique values can agree on many fixtures while violating the requested order.

Practice one: discriminate edge cases

Deliberately change Fast to return the smallest unique value and add a fixture whose first unique value is larger. Hint: the oracle must preserve the stated first-occurrence contract, not just membership in the set of unique values.

Keep the concrete input nine,two,two,one as an explicit discriminator between first unique and minimum unique. Hint: nine must win even though one is smaller. Seeded generation supplements this fixed example; it should not replace a small case chosen specifically to expose a known wrong interpretation.

Practice two: defend an extension

Design a testing portfolio with boundary examples, generated cases and invariants. Decide how to report the smallest reproducible failing input, and distinguish property-based testing with shrinking from this simple deterministic differential harness.

A robust test portfolio combines contract examples, independent oracles on small inputs and properties such as no source mutation. On failure, record the exact data and seed state, then reduce the case while preserving failure. This harness does not implement automatic shrinking, so label any manual reduction honestly rather than claiming a full property-testing framework.

Evaluate correctness and interview reasoning

A strong interview answer explains why the slow oracle is intentionally independent, why the finite generated domain is useful and why agreement is still bounded evidence. It distinguishes test strategy from the already familiar frequency-map algorithm. A senior review asks which wrong implementations the portfolio can reject, including smallest-unique selection and sentinel misuse. The sample's specified generator avoids dependence on framework random-sequence stability, while its exact success output makes execution easy to verify. Useful testing work produces reproducible counterexamples and meaningful boundaries, not only a large number printed after two similar implementations agree. Trial zero is deliberately empty, so both functions must return null without treating zero as an absence sentinel. Explicit zero-valued unique fixtures protect that distinction independently of generation.

A working example

using System;
using System.Linq;
using System.Collections.Generic;
using System.Globalization;
using System.Text;

uint state=17; int checks=0;
for(int trial=0;trial<200;trial++) {
    int length=trial%13; int[] data=new int[length];
    for(int i=0;i<length;i++) { state=unchecked(state*1664525+1013904223); data[i]=(int)(state%5)-2; }
    if(Fast(data)!=Oracle(data)) throw new Exception("differential mismatch"); checks++;
}
Console.WriteLine("differential checks:"+checks);
static int? Fast(int[] data) { var count=new Dictionary<int,int>(); foreach(int x in data) count[x]=count.GetValueOrDefault(x)+1; foreach(int x in data) if(count[x]==1) return x; return null; }
static int? Oracle(int[] data) { foreach(int x in data) { int count=0; foreach(int y in data) if(x==y) count++; if(count==1) return x; } return null; }

Expected output

differential checks:200
Try this
Deliberately change Fast to return the smallest unique value and add a fixture whose first unique value is larger. Hint: the oracle must preserve the stated first-occurrence contract, not just membership in the set of unique values.

Further reading

Interview questions for this lesson