Multinomial Sampling with Distribution Checks
Problem statement
Implement sampling from a list of category probabilities. For this exercise, assume probabilities are represented by nonnegative integer weights: category i has probability weights[i] / W, where W is the positive sum of all weights.
The reproducible practice task supplies the integer draws in tickets. Each ticket is in [0, W). Return one zero-based category index for each ticket, in the same order. Do not generate new random values or sort the tickets.
For this exercise, assume categories own consecutive half-open ranges in their original order. If S_i is the sum of weights before category i, its range is [S_i, S_i + weights[i]). A zero-weight category owns no tickets. A ticket exactly at a range’s upper boundary belongs to the next nonempty range.
If the supplied draws are independent and uniform on [0, W), each returned category has the requested probability. A histogram of m such draws is a multinomial count vector. Valid practice tickets need not themselves look uniformly distributed; the returned indices must always obey the mapping.
Distribution-check follow-up
Implement the separate helper maxEmpiricalDeviation(weights, samples) for a nonempty array of valid sampled category indices. For this exercise, assume it returns max_i(abs(count_i / samples.length - weights[i] / W)), using double precision. Include every category, even one with zero weight. The sampler entrypoint is the judged task; this helper is a follow-up with reference implementations in the editorial.
For example, weights [1,0,3] and samples [0,2,2,2] give deviation 0. Samples [0,0,2,2] give deviation 0.25. Finite random samples normally differ from the target. This metric measures a discrepancy; it does not prove a generator is correct or provide a statistical confidence level.
Notebook benchmark follow-up
The report asks for experiments while discussing how to run the program faster. It specifically highlights moving random-number generation outside the loop and replacing a list with a NumPy array; changing linear search to binary search was not the focus.
- Compare generating one ticket per iteration with generating the same number of tickets in one batch.
- With a common draw array and the same cumulative masses, compare Python list mapping, array mapping in a Python loop, and vectorized array mapping. Check that outputs agree before timing them.
- Measure preprocessing, random-number generation, mapping and the complete pipeline separately. Repeat runs, report the environment and sample count, and explain allocations and memory use.
- Compute a histogram and the empirical discrepancy. Explain why no particular finite random sample must match the probabilities exactly.
For this exercise, assume these timing experiments are an open discussion without a fixed speedup threshold or a judged wall-clock target.
Function
sampleCategories(weights: int[], tickets: int[]) → int[]Examples
Example 1
weights = [1,0,3]tickets = [0,1,2,3]return = [0,2,2,2]Category 0 owns [0,1), category 1 owns no tickets, and category 2 owns [1,4). The histogram is [1,0,3].
Example 2
weights = [5]tickets = [0,4,1]return = [0,0,0]The only category owns all five possible tickets. Ticket order is preserved.
Example 3
weights = [0,2,0,0,3,0]tickets = [4,0,2,1,3]return = [4,1,4,1,4]Only categories 1 and 4 have mass. Ticket 2 is the boundary after category 1 and belongs to category 4; zero-weight categories are skipped.
Constraints
1 <= weights.length <= 1000.0 <= weights[i] <= 10^6and1 <= W <= 10^9.1 <= tickets.length <= 1000and0 <= tickets[j] < W.- For the optional checker,
1 <= samples.length <= 1000and0 <= samples[j] < weights.length.