Help with prob calc

Hey guys, I have some problems with my code. I’m getting traceback with about 0.01 to 0.06 difference in test_module. Can some1 check code and maybe spot where I did mistake? Would be grateful . Here is my code:

import random


class Hat:

    def __init__(self, **kwargs):
        self.balls = dict()
        for key, value in kwargs.items():
            self.balls[key] = value
        self.ball_list = list()
        self.contents = list()
        for key, value in self.balls.items():
            self.ball_count = value * (key,)
            self.ball_list.append(self.ball_count)
        for x in self.ball_list:
            for y in x:
                self.contents.append(y)


    def draw(self, draw_number):
        self.draw_number = draw_number
        self.drawn_balls = list()
        count = len(self.contents)
        for i in range(self.draw_number):
            self.ball = random.randint(0, len(self.contents)-1)
            self.drawn_balls.append(self.contents[self.ball])
            del self.contents[self.ball]
            count -= 1
            if count == 0:
                for x in self.ball_list:
                    for y in x:
                        self.contents.append(y)
                        count = len(self.contents)
                        continue
        return self.drawn_balls


def experiment(hat, expected_balls, num_balls_drawn, num_experiments):
    count_matching_balls = 0
    balls_drawn = dict()
    count = num_experiments
    while count > 0:
        Hat.draw(hat, num_balls_drawn)
        for x in hat.drawn_balls:
            if x in balls_drawn.keys():
                balls_drawn[x] += 1
            else:
                balls_drawn[x] = 1
        if check_balls(expected_balls, balls_drawn) is None:
            count_matching_balls += 1
        balls_drawn.clear()
        count -= 1
    if count_matching_balls == 0:
        percentage = 0
    else:
        percentage = count_matching_balls/num_experiments
    print(percentage)


def check_balls(a, b):
    results = list()
    final_result = list()
    for x in a.keys():
        if x in b.keys():
            match = a[x] - b[x]
            if match > 0:
                results.append("False")
            else:
                results.append("True")
        else:
            return False
    for x in results:
        if x == "False":
            final_result.append("False")
    for x in final_result:
        if x == "False":
            return False

also here is pic of traceback:

There are two things. First:

This kills the testing from the console. You have to return the value or the tests will fail.

Second is in draw(), here:

The test you attached the error for is placing 19 balls in the hat and drawing out 20, and making sure that matches a subset of the balls in the hat so that the probability must be 1. When you try to rebuild your hat at the end of a draw (pulling 20 from 19 here), count will get to 0 and trigger the resulting code, which leads to the single test item not getting pulled from the hat a small amount of the time, and hence, test failure. You can see it in action if you modify experiment() like this:

        if check_balls(expected_balls, balls_drawn) is None:
            count_matching_balls += 1
        else:
            print(hat.drawn_balls)
            print(balls_drawn)
            print(expected_balls)

You should see around 7 sets of lines with test absent from the drawn balls set.

2 Likes

Hey, I rewrote my draw method it passed my tests and I’m done now :smiley: . Just want to ask you to check second part “elif”. I’m not sure that It is correct. If number of balls that will be drawn from hat is lower than number of items in hat. For example:
hat : yellow = 2, blue = 3
number of balls to be drawn = 2
number of experiments = 3
When my method pulls 2 times, number of remaining balls in hat is 1, do I have to return that ball to picked balls and then rebuild Hat, or just delete that 1 ball and rebuild. Btw thanks for your help mate!
here is my new code that passed tests and my second code for you to understand my question.

Final code that passed test:

    def draw(self, draw_number):
        self.draw_number = draw_number
        self.drawn_balls = list()
        count = len(self.contents)
        if count < draw_number:
            self.drawn_balls.extend(self.contents)
            self.contents.clear()
            for x in self.ball_list:
                for y in x:
                    self.contents.append(y)
                    continue
        elif count > draw_number:
            for i in range(self.draw_number):   # 20
                self.ball = random.randint(0, len(self.contents)-1)
                self.drawn_balls.append(self.contents[self.ball])
                del self.contents[self.ball]
                count -= 1
                if count < draw_number:
                    self.contents.clear()
                    for x in self.ball_list:
                        for y in x:
                            self.contents.append(y)
                            count = len(self.contents)
                            continue
        return self.drawn_balls

code that I think is correct:

    def draw(self, draw_number):
        self.draw_number = draw_number
        self.drawn_balls = list()
        count = len(self.contents)
        if count < draw_number:
            self.drawn_balls.extend(self.contents)
            self.contents.clear()
            for x in self.ball_list:
                for y in x:
                    self.contents.append(y)
                    continue
        elif count > draw_number:
            for i in range(self.draw_number):   # 20
                self.ball = random.randint(0, len(self.contents)-1)
                self.drawn_balls.append(self.contents[self.ball])
                del self.contents[self.ball]
                count -= 1
                if count == 0:
                    self.contents.clear()
                    for x in self.ball_list:
                        for y in x:
                            self.contents.append(y)
                            count = len(self.contents)
                            continue
        return self.drawn_balls

Well, the important thing is passing the test. If your customer finds a problem now, they need to write a new test to make the code fail, which should be more billable hours for you. :wink:

But in general, changing the hat during an experiment can lead to errors that are more subtle than the ones that are tested now. I believe the typical solution in this situation would return all the balls if more were requested than available, so that the user of the draw() method was responsible for interpretation of results for their experiment. The workaround to depleting the hat was the hint in the project to use the copy module and just start with a new hat on each experiment.

1 Like

Nevermind I’m stupid haha, just figured put that hat is reseted after 2 balls are pulled…

Yes haha! Thats is what I was thinking about :stuck_out_tongue: thanks a lot mate for help!