Home > Net >  Problem with my probability calculator in Python
Problem with my probability calculator in Python

Time:09-20

Essentially I am making a probability calculator using Python. It is meant to find the probability to finding a set number of balls after performing a set number of experiments (could be a large number), where in each experiment, you draw a certain number of balls randomly from a hat. I used object oriented programming to do this under the class Hat. A hat object can be created this way:

hat = Hat(blue=3,red=2,green=6)

The class should take a variable number of arguments that specify the number of balls of each color that are in the hat. Here the hat contains 3 blue balls, 2 red balls and 6 green balls.

Outside the class, there is a function called experiment that works out the probability of drawing certain type of balls (expected_balls) from a argument called 'hat' when you decide to draw a set number of balls (num_balls_drawn) after performing a certain number of experiments (num_experiments). The certain balls can be balls of different colors or styles. So a typical way/example of calling the experiment function is:

probability = experiment(hat=hat, expected_balls={"blue":2,"green":1}, num_balls_drawn=4, num_experiments=1000)

The probabilities produced each time the code is run should vary slightly. I was testing my code with this object and specific function call:

hat = Hat(blue=3,red=2,green=6)
probability = experiment(hat=hat, expected_balls={"blue":2,"green":1}, num_balls_drawn=4, num_experiments=1000)

While my probabilities varied slightly and produce probabilities between 0.31 and 0.39, the expected probability is actually 0.272 or values close to that probability (with a difference of 0.01). So it appears that I am far off. However, I can't quite work out what the problem is and how to fix it.

Any help will be appreciated! Thank you in advance.

THE CODE


import copy
import random

class Hat:
    
    def __init__(self,**ball):
        self.contents = list() #holds the balls
        ball_and_count = list() #list for colour of ball and its count 
        for key, value in ball.items():
            ball_and_count.append(f"{key}= {value}")
        #print(ball_and_count)
        for i in ball_and_count:
            equal_pos = i.find("=")
            ball_type = i[:equal_pos] #using string splicing to find ball type
            count = int(i[equal_pos 1:])#using string splicing to find the number of balls of that type
            c = 0
            while c < count:
                self.contents.append(ball_type)
                c = c   1
    def draw(self,num)
        self.num = num
        c = 0 #used in a while loop
        drawed_ball = list() #this is where all the balls that were drawed out from contents will stay
        try:
            while c < self.num:
                drawed_ball.append(self.contents.pop(random.randint(0,len(self.contents)-1)))
                c = c   1
            return drawed_ball
        except:
            return drawed_ball

     
def experiment(hat, expected_balls,num_balls_drawn, num_experiments):
    M = 0
    exp_done = 0
    while exp_done < num_experiments:
        drawn = 0
        drawn_balls = list()
        while drawn < num_balls_drawn:
            dc_contents = copy.deepcopy(hat.contents) # we are creating a deep copy of hat contents so that hat.contents stays the same
            drawn_balls.append(dc_contents.pop(random.randint(0,len(dc_contents)-1))) #append to the drawn_balls list
            v = 0
            for key, val in expected_balls.items():
                if key in drawn_balls:
                    k = drawn_balls.count(key) 
                    if k >= val:#here we are checking if for one ball type, we are drew the expected number of balls, then we increment the variable v
                        v = v   1
        
            if v == len(expected_balls):#we check if we have drawn the expected no. balls for all the balls and types, either we did or we did not, no in between, then we increment the variable M
                M = M   1            
            
            #incrementing the number of balls drawn  
            drawn = drawn   1
        exp_done = exp_done   1 #incrementing the number of experiments drawn
        
    N = num_experiments
    prob = M / N
    return prob

CodePudding user response:

There's more efficient ways to approach the problem, but following your original structure:

def experiment(hat, expected_balls, num_balls_drawn, num_experiments):
    exp_done = 0
    M = 0
    while exp_done < num_experiments:
        drawn = 0
        drawn_balls = list()
        dc_contents = copy.deepcopy(
            hat.contents)  # we are creating a deep copy of hat contents so that hat.contents stays the same
        while drawn < num_balls_drawn:
            drawn_balls.append(
                dc_contents.pop(random.randint(0, len(dc_contents) - 1)))  # append to the drawn_balls list
            for key, val in expected_balls.items():
                if drawn_balls.count(key) < val:
                    break
            else:
                M  = 1
                break

            drawn  = 1
        exp_done  = 1  # incrementing the number of experiments drawn

    N = num_experiments
    prob = M / N
    return prob

Problems that had to be fixed:

  • you were resetting the hat after every ball drawn, the deepcopy needs to be outside the loop
  • your check routine counted a success as soon as each required type was drawn

Runnable:

import copy
import random

class Hat:
    def __init__(self,**ball):
        self.contents = list() #holds the balls
        ball_and_count = list() #list for colour of ball and its count
        for key, value in ball.items():
            ball_and_count.append(f"{key}= {value}")
        #print(ball_and_count)
        for i in ball_and_count:
            equal_pos = i.find("=")
            ball_type = i[:equal_pos] #using string splicing to find ball type
            count = int(i[equal_pos 1:])#using string splicing to find the number of balls of that type
            c = 0
            while c < count:
                self.contents.append(ball_type)
                c = c   1

    def draw(self,num):
        self.num = num
        c = 0 #used in a while loop
        drawed_ball = list() #this is where all the balls that were drawed out from contents will stay
        try:
            while c < self.num:
                drawed_ball.append(self.contents.pop(random.randint(0,len(self.contents)-1)))
                c = c   1
            return drawed_ball
        except:
            return drawed_ball


def experiment(hat, expected_balls, num_balls_drawn, num_experiments):
    exp_done = 0
    M = 0
    while exp_done < num_experiments:
        drawn = 0
        drawn_balls = list()
        dc_contents = copy.deepcopy(
            hat.contents)  # we are creating a deep copy of hat contents so that hat.contents stays the same
        while drawn < num_balls_drawn:
            drawn_balls.append(
                dc_contents.pop(random.randint(0, len(dc_contents) - 1)))  # append to the drawn_balls list
            for key, val in expected_balls.items():
                if drawn_balls.count(key) < val:
                    break
            else:
                M  = 1
                break

            drawn  = 1
        exp_done  = 1  # incrementing the number of experiments drawn

    N = num_experiments
    prob = M / N
    return prob


hat = Hat(blue=3,red=2,green=6)
probability = experiment(hat=hat, expected_balls={"blue":2,"green":1}, num_balls_drawn=4, num_experiments=1000)
print(probability)

This prints values like 0.288 - how you'd be getting values like 8.0 is a mystery to me, but I'm fairly certain this isn't actually the code you're running. (and thus you weren't running the solution given with your code - that still has a typo and won't run on def draw(self,num) anyway)

CodePudding user response:

I finally fixed it! I'm relieved. I just simply made some adjustments on my draw method in the Hat class. Then I used the draw method in the experiment function, as well as the changes that you (Grismar) mentioned. Thank you so so much!

import copy
import random

class Hat:
    
    def __init__(self,**ball):
        self.contents = list() #holds the balls
        ball_and_count = list() #list for colour of ball and its count 
        for key, value in ball.items():
            ball_and_count.append(f"{key}= {value}")
        #print(ball_and_count)
        for i in ball_and_count:
            equal_pos = i.find("=")
            ball_type = i[:equal_pos] #using string splicing to find ball type
            count = int(i[equal_pos 1:])#using string splicing to find the number of balls of that type
            c = 0
            while c < count:
                self.contents.append(ball_type)
                c = c   1
     
    def draw(self,num):
        self.num = num
        c = 0 #used in a while loop
        drawed_ball = list() #this is where all the balls that were drawed out from contents will stay
        if self.num <= len(self.contents):
            while c < self.num:
                drawed_ball.append(self.contents.pop(random.randint(0,len(self.contents)-1)))
                c = c   1
            return drawed_ball
        else:
            drawed_ball = self.contents
            return drawed_ball
        
def experiment(hat, expected_balls, num_balls_drawn, num_experiments):
    exp_done = 0
    M = 0
    while exp_done < num_experiments:
        dc_contents = copy.deepcopy(hat)  # we are creating a deep copy of hat contents so that hat.contents stays the same
        drawn_balls = dc_contents.draw(num_balls_drawn)
        
        for key,val in expected_balls.items():
            if drawn_balls.count(key) < val:
                break
        
        else:
            M  = 1
        
        exp_done  = 1  # incrementing the number of experiments drawn

    N = num_experiments
    prob = M / N
    return prob

  • Related