Build a Probability Calculator Project - Build a Probability Calculator Project

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
image

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 :thinking:

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
image

1 Like

lol, yes progress is progress!

What I’m alluding to here:

  1. This loop will run forever since n never changes.
  2. 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

  1. 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
  2. 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?
  1. You’re right, my mistake

  2. 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 :

image