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