基于概率表用Python脚本计算获取X个唯一元素的平均试验次数
Absolutely! This is a weighted spin on the classic coupon collector problem, and we can definitely calculate the average number of trials needed to grab X unique elements using Python. Let's break this down and build a practical solution.
Element Type & Probability Distribution
First, let's formalize the given data with a clear table:
| Type | Chance | Number of Unique Elements |
|---|---|---|
| Common | 30.00% | 21 |
| Uncommon | 30.00% | 27 |
| Rare | 20.00% | 32 |
| Ultra Rare | 15.00% | 14 |
| Epic | 5.00% | 10 |
Each individual element's draw probability is its type's overall chance divided by the number of unique elements in that type. For example, a Common element has a 0.30 / 21 ≈ 1.43% chance per trial, while an Epic element sits at 0.05 / 10 = 0.5%.
Practical Solution: Monte Carlo Simulation
The easiest and most practical way to estimate the average trials is Monte Carlo simulation. We'll run thousands of simulated draw sequences, count how many trials it takes to collect X unique elements each time, then take the average. This avoids complex math and works well even for larger X values.
Here's a Python script that does exactly this:
import random def monte_carlo_average_trials(target_count, num_simulations=10000): # Define type data: (type probability, list of unique elements in the type) type_data = [ (0.30, [f"common_{i}" for i in range(21)]), (0.30, [f"uncommon_{i}" for i in range(27)]), (0.20, [f"rare_{i}" for i in range(32)]), (0.15, [f"ultra_{i}" for i in range(14)]), (0.05, [f"epic_{i}" for i in range(10)]) ] # Build a list of all elements and their individual draw probabilities all_elements = [] element_weights = [] for type_prob, elements in type_data: elem_prob = type_prob / len(elements) for elem in elements: all_elements.append(elem) element_weights.append(elem_prob) total_trials = 0 for _ in range(num_simulations): collected = set() trial_count = 0 while len(collected) < target_count: # Draw an element according to its weighted probability drawn_element = random.choices(all_elements, weights=element_weights, k=1)[0] collected.add(drawn_element) trial_count += 1 total_trials += trial_count # Return the average number of trials across all simulations return total_trials / num_simulations # Example: Calculate average trials to collect 5 unique elements if __name__ == "__main__": target = 5 average_trials = monte_carlo_average_trials(target, num_simulations=20000) print(f"Average trials to collect {target} unique elements: {average_trials:.2f}")
Key Notes About the Script
- We use
random.choicesto simulate weighted draws, matching the real-world probability of each element. - Each simulation runs until we hit the target number of unique elements, tracking trial counts.
- For more precision, increase
num_simulations(e.g., to 50000) — this will take longer but give a more accurate estimate. - You can easily adjust the
targetvariable to calculate for any number of unique elements (up to the total 104 unique elements across all types).
Exact Calculation (For Small X Values)
If you need an exact mathematical result instead of an estimate, you could use dynamic programming to track expected trials for each state (number of unique elements collected). However, this becomes computationally expensive quickly with 104 total elements, so Monte Carlo is far more practical for most use cases.
内容的提问来源于stack exchange,提问作者Pasquale Sada

