Probability Calculator experiment problem


While performing the test_prob_experiment I am getting a higher probability that I should and I am not able to figure out the cause. Could someone please help here?

My code:

import random
import copy
import collections

class Hat:
    def __init__(self, **kwargs):
        self.contents = []
        for k,v in kwargs.items():
            self.contents.extend([k for i in range(v)])

    def draw(self,balls_number):
        removed = []
        if balls_number < len(self.contents):
            for i in range(balls_number):
                random_item_from_list = random.randrange(len(self.contents))
                rm_value = self.contents.pop(random_item_from_list)
            return removed
            return self.contents

def experiment(hat,expected_balls,num_balls_drawn,num_experiments):
    count = 0
    for num in range(num_experiments):
        matchedColors = 0
        matchedNums = 0
        hat_copy = copy.deepcopy(hat)
        draw_result = hat_copy.draw(num_balls_drawn)
        draw_result_dict = collections.Counter(draw_result)
        for k,v in expected_balls.items():
          if k in draw_result_dict:
              matchedColors += 1
              if draw_result_dict[k] - v >= 0:
                matchedNums += 1
        if matchedColors == matchedNums:
            count += 1
    return float(count/num_experiments)


 python
1683 3000
Probability: 0.561
..491 1000
FAIL: test_prob_experiment (test_module.UnitTests)
Traceback (most recent call last):
  File "/home/runner/boilerplate-probability-calculator/", line 26, in test_prob_experiment
    self.assertAlmostEqual(actual, expected, delta = 0.01, msg = 'Expected experiment method to return a different probability.')
AssertionError: 0.491 != 0.272 within 0.01 delta (0.21899999999999997 difference) : Expected experiment method to return a different probability.

Ran 3 tests in 0.132s

FAILED (failures=1)

Since the probability is high, you are obviously overcounting the experiments that pass. If I modify your experiment to print the good results:

        if matchedColors == matchedNums:
            count += 1
            print(f"good d: {draw_result} e: {expected_balls}")

I get some good “good” results and some like this:

good d: ['green', 'green', 'red', 'green'] e: {'blue': 2, 'green': 1}
good d: ['blue', 'red', 'blue', 'red'] e: {'blue': 2, 'green': 1}
good d: ['green', 'green', 'green', 'green'] e: {'blue': 2, 'green': 1}
good d: ['green', 'green', 'red', 'red'] e: {'blue': 2, 'green': 1}

which are not good. It looks like if one color is correct, your code says the trial is good.

This code does exactly that. Since the test is not in the loop, if the matched colors and numbers are equal after the loop, it passes. You need to test that all colors and numbers in the expected balls are contained in your draw. Since you already have both as dicts, you should be able to check the values from excpected_balls against draw_result_dict in one loop. Once I plugged in the correct test here, your code passes.

So, just fix the comparison between draw_result_dict and expected_balls.

Thank you so much for your help!
I wasn’t able to find a way to compare efficiently both dictionaries, so I found a workaround checking the number of matching colors:

        for k,v in expected_balls.items():
          if k in draw_result_dict:
              if draw_result_dict[k] - v >= 0:
        if len(matchedColors) > 1:
            count += 1

The test is passed, but I know this is not optimal because if the “expected_balls” parameter would have 3 key/value pairs, probably I would ended up with the same issue.

The good news is you pass the tests (which is the goal), so congrats. The bad news is this code would fail if the tests tested an experiment that expected only one type of ball (I tested it; it does. Not your problem since you implemented the specifications.). The other good news is you’ve found a bug in the test suite for this project so you can have a go at writing a test that covers only one ball expected and submit a patch if you like.

You’ve hit upon probably the main issue here since there are no specifications about how many types of balls will be available or expected so you need to iterate over all the expected balls, like you’ve done. I broke this logic into a separate function to compare the dicts and iterated as you have. At that point there are only three possibilities:

  1. A ball is in expected_balls but not in draw_result_dict. You could use the in operator (like you did), catch a raised KeyError, or even use the .get() method with a sensible default to check. Regardless, this is a failed experiment.
  2. The ball exists in both dicts, but an insufficient number were drawn. This is a failed experiment too.
  3. The ball exists in both dicts, and enough were drawn. This is a passing experiment.

The function I use returns False for the first two cases and True for the last so I can use it for my conditional that counts the passing experiments.

1 Like

This topic was automatically closed 182 days after the last reply. New replies are no longer allowed.