InterviewPrepKit

Home / Coding / Agent Coding / Evaluation / pass@k

pass@k

medium 00:00
Solving tips
  • Do not estimate pass@k by sampling k of the n attempts and checking if any passed — that is high-variance and biased for small n. There is a closed form: the chance all k draws miss is comb(n-c, k) / comb(n, k), so pass@k is one minus that.
  • The failure combinatorics only make sense when there are at least k failures to choose from. When n - c < k every k-subset must contain a passing sample, so pass@k for that task is exactly 1.0 — special-case it before calling comb.
  • pass@k is a per-task quantity; the reported number is the average across tasks. Keep those two steps separate so an empty suite or a single bad task is easy to reason about.

When you sample a code-generating agent many times per problem, the honest quality metric is not “did the single best sample pass” but “if a user had drawn k samples, how likely is at least one to pass.” That is pass@k. Estimating it by literally drawing k of your n attempts is noisy and biased downward, so the standard defines an unbiased closed-form estimate from the counts alone. This exercise implements that estimator.

The estimator

For a single task you sampled n attempts and c of them passed. Draw k of the n attempts uniformly at random. The draw fails only when all k come from the n - c failing attempts, which happens with probability comb(n - c, k) / comb(n, k). So the per-task pass@k is:

1 - comb(n - c, k) / comb(n, k)

This is the unbiased estimator from the HumanEval / Codex evaluation. The reported pass@k for a benchmark is the mean of this quantity over all tasks.

The one edge to watch: if n - c < k there are not even k failing samples to choose, comb(n - c, k) is 0, and the task’s pass@k is 1.0. math.comb already returns 0 when the second argument exceeds the first, but handling it explicitly keeps the intent obvious and avoids relying on that behavior.

Task

Complete pass_at_k(results, k):

  1. If results is empty, return 0.0.
  2. For each (n, c) task, compute the per-task estimate. If n - c < k, the estimate is 1.0; otherwise it is 1 - comb(n - c, k) / comb(n, k).
  3. Return the average of the per-task estimates across all tasks.

Use math.comb. Do not approximate by sampling.

Example

results = [
    (5, 3),    # 5 attempts, 3 passed
    (5, 0),    # 5 attempts, none passed
    (10, 10),  # all 10 passed
    (4, 1),    # 4 attempts, 1 passed
]

# per task at k=2:
#   (5,3):  1 - comb(2,2)/comb(5,2) = 1 - 1/10  = 0.9
#   (5,0):  1 - comb(5,2)/comb(5,2) = 1 - 10/10 = 0.0
#   (10,10): n-c = 0 < 2            -> 1.0
#   (4,1):  1 - comb(3,2)/comb(4,2) = 1 - 3/6   = 0.5

round(pass_at_k(results, k=2), 4)   # -> 0.6
round(pass_at_k(results, k=1), 4)   # -> 0.4625  (mean of 0.6, 0.0, 1.0, 0.25)

Constraints

  • n >= 1 and 0 <= c <= n for every task; 1 <= k <= n.
  • Use integer combinatorics (math.comb), not floating-point factorials, to avoid overflow and precision loss for large n.
  • Treat each task independently; the result is a plain average (every task weighted equally).
  • Return 0.0 for an empty results list.

Write your solution, then hit Run tests to check it — or get a mock grade from the AI coach.

The coach remembers this session — revise your code and ask again, and it grades your progress. It gives hints, not the answer.
Report a bug