 # Probability Calculator Project Help

I think I did the project correctly even though the test for the experiment function fails.

In Replit I always get 0.293, which fails due to their expected outcome of 0.272. When I run the exact code in Jupyter Notebook, sometimes I get the number very close to 0.272, sometimes an exact match. I get a range of numbers from approximately 0.260-0.294. Which to me makes sense, since drawing at random will not yield the same results everytime(like the project ReadMe notes). I’m open to suggestions for anything I may have missed or improvements to code.

Code:

``````import copy
import random
# Consider using the modules imported above.

class Hat:

def __init__(self,**kwargs):
self.kwargs = kwargs
self.contents = []

for key,value in self.kwargs.items():
for i in range(0,value):
self.contents.append(key)

def draw(self, num_balls):

self.content_copy = self.contents.copy()

if num_balls > len(self.contents):
return(self.contents)

else:
draws = []

for x in range(num_balls):
draws.append(self.contents.pop(random.randrange(len(self.contents))))

return(draws)

def experiment(hat, expected_balls, num_balls_drawn, num_experiments):

#track the number of times an experiment resulted in having all the expected balls drawn
count = 0

for x in range(0,num_experiments):

#set up empty dictionary to track balls that are drawn
drawn = {}

placeholder = copy.deepcopy(expected_balls)

draw = hat.draw(num_balls_drawn)

hat.contents = hat.content_copy

#iterate through the drawn balls list and store the number of times drawn in drawn dictionary
for y in draw:
if y in drawn:
drawn[y] += 1
else:
drawn[y] = 1

#iterate through the keys of drawn, check if the value in drawn is equal to or greater than the amount in placeholder
for i in list(placeholder.keys()):

#if the amount drawn for the color is
if i in drawn:
if drawn[i] >= placeholder[i]:
placeholder[i] = True
else:
placeholder[i] = False

else:
continue

if set(list(placeholder.values())) == {True}:
count += 1

else:
continue

return(count/num_experiments)
``````

Tests are using specific `seed` number, to ensure random values from `random` module are repeatable during testing and therefore it’s possible to test if result is as expected. This means, with the seed number used in testing function returned `0.293`, while expected answer is `0.272`.

I haven’t looked enough at the code, to be sure where is the problem, but often there are discrepancies between specification and how `draw` method is handling some edge cases (ie. when number of balls to draw is higher than number of balls in hat).

Something funny is going on with your copy. I would copy the hat and draw from the copy.

The placeholder is to keep track of how the drawn balls compare to expected_balls, without changing the original contents of expected_balls.

I set the hat.contents to the hat.content_copy after the draw to restore the contents to the original state, for the next experiment. Before I added this code, I noticed the contents were different in each experiment, due to the draw function removing the object drawn from the contents list.

You are misusing the copy. Like I said, you need to copy the entire hat on each test and draw from the copy.

I believe I accounted for the edge case in my draw method. It simply returns all the contents if the number of balls to draw is greater than the number of items in contents.

Your logic is pretty complex here - much more than it needs to be.

For a test, the instructions say

1. copy the hat
2. draw from the copied hat
3. check if the draw has the expected balls

You should not copy the expected balls.

You should not modify the internal contents of the hat from outside of the hat object.

Some issues I spotted:

• There’s no need to assign `kwargs` as an instance attribute when it’s never used outside of `__init__()`
• There’s no need to specify 0 as the start for `range()`, it’s the default
• As stated above, change+restore would be better accomplished by making a (deep) copy of `hat`
• When not actually using the iteration variable, replace it with an underscore `_` to make that clear
• `dict.keys()` can be iterated over directly, no need to convert to a list first
``````if set(list(placeholder.values())) == {True}:
``````

This line is probably the source of your test failure. My guess is that because `0 == False` and `1 == True` in Python, something like `{1, True}` is being evaluated as equal to `{True}`, causing a false positive.

Thank you all for the suggestions! You have truly been helpful and the project is now officially done. I have incorporated some of the changes you have suggested.

-copied the entire hat object and drew from copied hat
-used “_” when not using iteration variable
`if set(list(placeholder.values())) == {True}:`
was setting off false positives when I ran a debug. great catch.

I ended up replacing the True with “Yes” and False with “No” instead. That took care of the false positives.

Well done! For reference, here’s my solution:

``````class Hat:
def __init__(self, **kwargs):
self.contents = []

for ball_type, num in kwargs.items():
self.contents.extend([ball_type] * num)

def draw(self, num_balls_drawn):
if num_balls_drawn > len(self.contents):
return self.contents[:]
else:
return [
self.contents.pop(random.randrange(len(self.contents)))
for _ in range(num_balls_drawn)
]

def experiment(hat, expected_balls, num_balls_drawn, num_experiments):
num_matches = 0

for _ in range(num_experiments):
drawn_balls = copy.deepcopy(hat).draw(num_balls_drawn)
match = all(
drawn_balls.count(ball_type) >= num_expected
for ball_type, num_expected in expected_balls.items()
)
if match:
num_matches += 1

return num_matches/num_experiments
``````

wow that match = all() really saves alot of time and memory. very good to note going forward.
Thanks