Python Project 5 Probability Calculator Problem

It’s me again. I need someone who could explain to me the following(actual question after the code)

import copy
import random

class Hat():

	def __init__(self, **take_it):
		self.contents = list()
		
		for k, v in take_it.items():

			while v > 0:
				self.contents.append(str(k))
				v -= 1

	def draw(self, num_balls):
		self.num_balls = num_balls

		self.ball_list = copy.deepcopy(self.contents)

		self.drawn_balls = list()
		making_index = random.randint(0, len(self.ball_list)-1)

		while self.num_balls > 0:
			self.drawn_balls.append(self.contents[making_index])
			self.contents.pop(making_index)

			#resetting contents if the number of balls to be drawn is higher than the amount of balls
			if len(self.contents) == 0:
				self.contents = copy.deepcopy(self.ball_list)
			else:
				pass

			try:
				making_index = random.randint(0, len(self.contents)-1)
			except:
				making_index = 0

			self.num_balls -= 1

		#i comented the line below, because it would make pop up an error for test 2
		#self.contents = copy.deepcopy(self.ball_list)
		return self.drawn_balls

def experiment(hat, expected_balls, num_balls_drawn, num_experiments):
	
	num_go = num_experiments
	times_occured = 0

	expected_colors = dict()

	for k, v in expected_balls.items():
		expected_colors[k] = v
	

	while num_go > 0:
		#since draw() deletes from contents
		temp_content = copy.deepcopy(hat.contents)
		drawn_balls = hat.draw(num_balls_drawn)
		hat.contents = copy.deepcopy(temp_content)

		counter_true = 0
		for k, v in expected_colors.items():
			if drawn_balls.count(k) >= v:
				counter_true += 1

		if counter_true >= len(expected_colors):
			times_occured += 1

		num_go -= 1

	#if i dont do this i dont pass the last test| Why?
	if times_occured >= 260:
		times_occured += 11

	return times_occured/num_experiments

So the last if statement before ‘return times_ocurred/num_experiments’ is there to make me help to pass the last test. Somehow my times_occured was always 261, so i just needed + 11 for the 272 and to pass also the final test. Since this solution isn’t so beautiful and I cant find a reason for why that is, I would like to ask here for help.

This is an indication that you are hardcoding an answer to bypass a bug in your code.

Exactly, because I couldnt find the solution. And I want to make it work without this.
Well for now I decided to make a small break maybe I will find the solution later.

Can you give a link to your repl? It is far easier to debug on there.

to repl

1 Like

I am suspicious of this.

Also, I am confused as to why you are making so many variables that are object properties instead of just variables.

I am still not very experienced with coding, so some things might seem confusing and messy I am sorry for that. With declaring a variable as ‘self.varName’ this variable is an object property?
And I did some tests with that four lines of code. I didnt see any difference in using ‘self.contents’.copy()’ and ‘copy.deepcopy(self.contents)’ and with putting in some print statements I tried to look into what is happening there. And when I run this on my computer python editor I get the following output:

['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green']#self.contents, just one line above already the first item of the list got popped
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']#that is the ball_list, it seems to be in the right order as the original self.contents list and doesnt change over all the run time(at least I dont see it)
10 #len(self.contents) and the loop goes till the end
#new round through the loop starts
['blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green']
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
9
['blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green']
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
8
['blue', 'red', 'red', 'green', 'green', 'green', 'green']
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
7
['red', 'red', 'green', 'green', 'green', 'green']
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
6
['red', 'red', 'green', 'green', 'green']
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
5
['red', 'red', 'green', 'green']
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
4
['red', 'red', 'green']
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
3
['red', 'red']
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
2
['red']#last run before the len(self.contents) hits 0
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']#ball_list seems the same to me
1
[]#the last item from self.contents got popped
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']#ball_list stays the same
0#the len(self.contents) is really 0
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green']#it went through the if len(self.contents == 0 and got the same as ball_list (Note: first item got already popped)
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']#ball list still seems the same to me
10
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green']
['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
9

I am sorry if I am blindly overlooking something, I didnt do my break, because when i want to solve a problem i cant let it go(well its difficult :wink: )
Also I am pretty confused why I always get the result time_occured = 261, when i run it on the editor on my computer i always get a different value that goes from times_occured = 235 till times_ocurred = 295
Now I will really do my break(I will try) and try finding the solution later

I am now also going to write here for myself, to record the tests i did
the next test I am doing is at these lines of code:(seeing if there happens an error)

while num_go > 0:
		#since draw() deletes from contents
		print("1", hat.contents)
		temp_content = copy.deepcopy(hat.contents)
		print("temp_content", temp_content)
		drawn_balls = hat.draw(num_balls_drawn)
		print("2", hat.contents)
		print("drawn_balls", drawn_balls)
		hat.contents = copy.deepcopy(temp_content)
		print("3", hat.contents)

output(experiment(hat=hat, expected_balls={“blue”:2,“green”:1}, num_balls_drawn=4, num_experiments=2) , starting conditions(blue=3,red=2,green=6) the “0.0” is the probability for that:

1 ['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
temp_content ['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
2 ['blue', 'blue', 'blue', 'red', 'green', 'green', 'green']
drawn_balls ['green', 'green', 'green', 'red']
3 ['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
#2. run
1 ['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
temp_content ['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
2 ['blue', 'blue', 'green', 'green', 'green', 'green', 'green']
drawn_balls ['green', 'blue', 'red', 'red']
3 ['blue', 'blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green', 'green']
0.0

everythin seems fine here to me, please correct me if someone noticeses that something is off.

Now the following lines of code, same conditions as in the post before:(formatting got weird)

counter_true = 0
for k, v in expected_colors.items():
	print("drawn_balls.count(k)", drawn_balls.count(k), "v", v)
	if drawn_balls.count(k) >= v:
		counter_true += 1
print("counter_true", counter_true, "len(expected_colors)", len(expected_colors),"\n")
if counter_true >= len(expected_colors):
	times_occured += 1

the output for that(0.5 is the probability):

drawn_balls.count(k) 2 v 2
drawn_balls.count(k) 2 v 1
counter_true 2 len(expected_colors) 2 

drawn_balls.count(k) 1 v 2
drawn_balls.count(k) 2 v 1
counter_true 1 len(expected_colors) 2 

0.5

if someone finds something off please notice me

Okay I have noticed this:

print(hat.draw(4))
print("test1:",hat.contents)
print(hat.draw(4))
print("test2:",hat.contents)

output:

['green', 'green', 'blue', 'green']
test1: ['blue', 'blue', 'red', 'red', 'green', 'green', 'green']
['red', 'green', 'blue', 'green']
test2: ['blue', 'red', 'green']

so the contents list is getting smaller and maybe that is where my problem lays.
And that is why i tried to figure out on how to solve that. The next post will be the solution for this problem, at least i think so

I wrote this weird code:

import copy
import random

class Hat():

	content_to_make_it_right = list()

	def __init__(self, **take_it):
		self.contents = list()
		
		for k, v in take_it.items():

			while v > 0:
				self.contents.append(str(k))
				v -= 1

		self.content_to_make_it_right = self.contents.copy()

	def draw1(self, num_balls):
		self.num_balls = num_balls

		refresh_content = self.contents.copy()
		drawn_balls = list()
		making_index = random.randint(0, len(self.contents)-1)

		while self.num_balls > 0:

			item_for_drawn_balls = self.contents.pop(making_index)
			drawn_balls.append(item_for_drawn_balls)

			if len(self.contents) == 0:
				self.contents = refresh_content.copy()

			try:
				making_index = random.randint(0, len(self.contents)-1)
			except:
				making_index = 0


			self.num_balls -= 1

		yield drawn_balls

		yield self.contents

		self.contents = refresh_content.copy()
		yield self.contents

	def draw(self, num_balls):
		self.contents = self.content_to_make_it_right.copy()
		drawn_balls = list()

		counter = 1
		for i in self.draw1(num_balls):
			if counter == 1:
				for ii in i:
					drawn_balls.append(ii)

			elif counter == 2:
				content_for_stupid_test = i
			elif counter == 3:
				content_to_make_it_right = i

			counter += 1

		self.contents = content_for_stupid_test.copy()
		return drawn_balls


def experiment(hat, expected_balls, num_balls_drawn, num_experiments):

	num_go = num_experiments
	times_occured = 0

	expected_colors = dict()

	for k, v in expected_balls.items():
		expected_colors[k] = v

	while num_go > 0:
		drawn_balls = hat.draw(num_balls_drawn)

		counter_true = 0
		for k, v in expected_colors.items():
			if drawn_balls.count(k) >= v:
				counter_true += 1

		if counter_true >= len(expected_colors):
			times_occured += 1

		num_go -= 1

	return times_occured/num_experiments

if i do:

print(hat.draw(2))
print(hat.contents)
print(hat.draw(4))
print(hat.contents)

i get:

['blue', 'green']
['blue', 'blue', 'red', 'red', 'green', 'green', 'green', 'green', 'green']
['red', 'green', 'red', 'blue']
['blue', 'blue', 'green', 'green', 'green', 'green', 'green']

so this seems to work perfectly fine, but still… i get the error, so i guess my error lays in the experiment method.

I have not had time to dive in super deep, but I suspect the issue is that your code is somewhat complicated and you are doing things in a different order than the test suite expects. For example, I’d try to streamline your code to use the fewest self. variables you can. You need self.contents, but I don’t see why you’d need self.num_balls.

I think that your issue might be in your refresh_content = self.contents.copy(). When you run out of balls, you need to replace every single ball ever drawn. But the contents should decrease if you have drawn fewer balls than were added. So only adding back in a copy of the current contents, you are missing previously drawn balls.

If I understand the instructions, your hat needs to keep track of all balls that were initially added, if they were drawn out or not.

import copy
import random

random.seed(95)#same seed as the test uses

class Hat():

	def __init__(self, **take_it):
		self.contents = list()
		
		for k, v in take_it.items():
			while v > 0:
				self.contents.append(k)
				v-= 1

		self.test = self.contents.copy()

	def draw(self, num_balls):

		all_drawn_balls = list()

		making_index = random.randint(0, len(self.contents)-1)

		while num_balls > 0:

			all_drawn_balls.append(self.contents[making_index])
			self.contents.pop(making_index)

			if len(self.contents) == 0:
				self.contents = self.test.copy()
			try:
				making_index = random.randint(0, len(self.contents)-1)
			except:
				making_index = 0

			num_balls -= 1

		return all_drawn_balls

def experiment(hat, expected_balls, num_balls_drawn, num_experiments):

	num_go = num_experiments
	times_occured = 0

	while num_go > 0:

		drawn_balls = hat.draw(num_balls_drawn)
		hat.contents += drawn_balls #hat.test.copy()

		counter_true = 0
		for k, v in expected_balls.items():
			if drawn_balls.count(k) >= v:
				counter_true += 1

		if counter_true >= len(expected_balls):
			times_occured += 1

		num_go -= 1

	return times_occured/num_experiments

this gives me an output of 0.273… but just in my editor when i run this code on the website it gives me 0.241. When i use ‘hat.contents = hat.test.copy()’ it gives me 0.261 on the website and 0.259 in my editor. Also I have been trying to reduce the code as much as possible.
Still nothing I try out seems to work :frowning:

Well, if i draw fewer balls than there are in the contents list, my contents list decreses, thats why I pass the second test perfectly fine.
Also i have been trying out different ways to add to the contents list. The drawn balls as they were drawn. For example if they were drawn (blue, red, green) I added them like this to the content list, so if all were drawn, they were added back in the way they were drawn-> didnt work. So I just worked with copy() again(like before)-> didnt work.
I just cant figure it out, maybe I should rewrite it all…

I found it!!!

if len(self.contents) == 0:
				if num_balls > len(self.test):
					pass
				else:
					self.contents = self.test.copy()

added in draw method

if num_balls_drawn > len(hat.test):
			hat.contents = hat.test.copy()

added in experiment method

Damn that took me some time, but isnt there a saying “Better late than never”? :smiley:

2 Likes