Tell us what’s happening:
Hello, I have tried to solve the problem according to the instructions but my code doesn’t pass any test except the first one (“Creation of hat object should add correct contents.”). I read the browser console but got more confused lol
For the second test, I have confirmed that the list gets reduced until it reaches the specified draw number
For the the third test, I have checked that it returns all the contents list if the draw number is equal or bigger than the contents length
For the forth test, the code returns around 0.35 to 0.38, which is very similar to the output example.
Your code so far
import random
import copy
class Hat:
def __init__(self, **kwargs):
self.contents = []
for key,val in kwargs.items():
content = [key] * val
self.contents.extend(content)
def draw(self,n):
contents = copy.deepcopy(self.contents)
if n >= len(contents):
return contents
while n < (len(contents)):
contents.pop(random.randrange(len(contents)))
return contents
def experiment(hat, expected_balls, num_balls_drawn, num_experiments):
M = 0
n = num_experiments
while n > 0:
drawn_balls = hat.draw(num_balls_drawn)
all_balls_met = []
for color, number in expected_balls.items():
matched_balls = [ball for ball in drawn_balls if ball == color]
if len(matched_balls) >= number:
all_balls_met.append(True)
else:
all_balls_met.append(False)
if all(all_balls_met):
M += 1
n += -1
print("M/N: ", f"{M}/{num_experiments}")
probability = M/num_experiments
return probability
hat = Hat(black=6, red=4, green=3)
probability = experiment(hat=hat,
expected_balls={'red':2,'green':1},
num_balls_drawn=5,
num_experiments=2000)
print(probability)
Your browser information:
User Agent is: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36
Challenge Information:
Build a Probability Calculator Project - Build a Probability Calculator Project
Here are the errors I’m getting for every test:
Second test
Third test
Fourth test
Third test is straightforward, it tells you exactly how many balls should be remaining in the hat, 0 but you have 19.
If you draw all the balls from the hat, how many balls should remain in the hat?
For the 2nd test, try printing out the contents of the hat before returning it in your draw function.
print(contents)
return contents
You will see a problem.
For the second test I run this:
hat = Hat(black=6, red=4, green=3)
hat.draw(5)
I got this result in print:
['black', 'black', 'red', 'red', 'green']
It drew only 5, isn’t this the expected behaviour?
It’s not the # of balls that it draws. Add the print, run the tests.
So I was deepcopying the self.contents but I should instead modify the original self.contents, is that right?
I ran this code
hat = Hat(black=6, red=4, green=3)
for _ in range(5):
hat.draw(5)
I got this:
['black', 'black', 'black', 'black', 'red']
['black', 'black', 'black', 'black', 'black']
['black', 'black', 'red', 'red', 'green']
['black', 'red', 'red', 'red', 'green']
['black', 'black', 'black', 'red', 'green']
It returns a random list, so I don’t see the problem
while n < (len(contents)):
contents.pop(random.randrange(len(contents)))
print(contents)
return contents
What should remain in contents
at the end of the loop? If you are removing the contents one by one, until n < (len(contents)
, and then you print the contents, what should it print?
It should print a list with random elements with a length determined by the parameter n. I think that since I’m using the pop method, the first element always remains the same (as it did in the prev reply); I will poke on that rn and see if I’m right. brb
I shuffled the list and then popped it
random.shuffle(contents)
while n < (len(contents)):
#contents.pop(random.randrange(len(contents)))
contents.pop()
Output:
['green', 'black', 'red', 'black', 'black']
['black', 'black', 'black', 'red', 'black']
['red', 'green', 'green', 'black', 'black']
['red', 'black', 'green', 'black', 'green']
['black', 'red', 'green', 'black', 'black']
I got the fourth test passed… Not the one I was solving, but it’s a progress
1 Like
lol, yes progress is progress!
What I’m alluding to here:
- This loop will run forever since
n
never changes.
- If the hat contents is 10 balls and you draw 9 balls (remove them from contents) and then you
print
or return
contents, then contents
should be left with only 1 ball, correct?
Think of it physically: You need to remove 9 balls from the hat. The hat contents will now just be 1 ball. You will have 9 balls that you drew on the table or somewhere.
How does pop()
work?
It removes a ball from contents
and where does that ball go?
I still don’t get it ahhh
- The loop doesn’t run forever because the length of the contents is evaluated for every iteration and stops when it reaches the same value for n
- I imagined the draw as taking out n balls from the hat; in other words, if I specify hat.draw(5) then I expect the function to only give me 5 balls. Should I do something with all the balls I removed during the while loop?
-
You’re right, my mistake
-
contents.pop(random.randrange(len(contents)))
You remove a ball from contents
, now it has 1 ball less. Where did it go? You are returning the remaining balls in contents, not the balls that you removed.
This leads into the next problem:
if n >= len(contents):
return contents
If you remove all of the balls from the contents
of the hat, how many balls remain in the contents
? All of the balls or none of the balls?
Think of a physical hat. You have two containers, a hat and a table. You take a ball out of the hat and put it on the table.
You have a hat, but you don’t have a table to put the balls that have been drawn.
Maybe I’ve mixed up the metaphor:
self.contents
is the Hat container
contents
is new container copy
If you remove a ball from the copy, is it removed from the Hat?
Where did the removed ball go?
No. The loop would not run forever, n
would not change, but len(contents)
would decrease by 1 every time an item is removed from contents. The code indeed produces an equivalence of “random draw” of specified number of balls from the hat, but by opposite logic, not by taking out balls one by one as drawn balls, but by removing balls from a copy of the hat until meeting the specified number. Thus said, the problem is the content of the hat will not change after this operation. For example, if the hat has 10 balls, and 4 balls to be drawn, the code can return 4 randomly drawn balls, but the hat is still having 10 balls after that, so it leads to failure to pass test 2 and 3.
1 Like
Right, it was a little reverse of my implementation which confused me.
However, when you draw a ball from a hat, that is the ball that is “drawn” and presented, not the balls remaining in the hat, no?
If you have 2 names on paper in a hat and you draw 1, THAT is the one that you’ve drawn. Not the one still in the hat…
I guess it will still return a random selection in the end, and if that passes the test it’s fine.
What is the probability that a random draw of 4 balls will contain at least 1 red ball and 2 green balls?
This seems to refer to the balls taken out of the hat, not the balls remaining in the hat.
1 Like
I think I get it now, so I need to remove the balls from self.contents and move them to the contents list and return that list. Right?
1 Like
That’s one way to go. But if you want to maintain your original logic, ie. keeping the while loop of popping out elements, you have to remove the list of balls returned from the draw
method from self.contents
.
1 Like
Exactly. Could rename contents
to drawn_balls
in that case to be a bit more clear.
1 Like
Here is my new method:
drawn_balls = []
if n >= len(self.contents):
drawn_balls = copy.deepcopy(self.contents)
self.contents = []
return drawn_balls
random.shuffle(self.contents)
while n > 0:
drawn_balls.append(self.contents.pop())
n += -1
return drawn_balls
I got 3 tests approved now :