Files
2024-05-16 14:27:36 -05:00

9.8 KiB
Raw Blame History

id, title, challengeType, forumTopicId, dashedName
id title challengeType forumTopicId dashedName
5e44414f903586ffb414c950 概率计算器 23 462364 probability-calculator

--description--

假设有一顶帽子,里面有 5 个蓝球、4 个红球和 2 个绿球。 随机抽取的 4 个球中至少包含 1 个红球和 2 个绿球的概率是多少? 虽然可以使用高等数学来计算概率,但更简单的方法是编写一个程序来执行大量实验来估计近似概率。

对于这个项目,你将编写一个程序来确定从帽子中随机抽取某些球的大致概率。

First, create a Hat class in main.py. 该类应该采用可变数量的参数来指定帽子中每种颜色的球数。 例如,可以通过以下任何一种方式创建类对象:

hat1 = Hat(yellow=3, blue=2, green=6)
hat2 = Hat(red=5, orange=4)
hat3 = Hat(red=5, orange=4, black=1, blue=0, pink=2, striped=9)

一顶帽子总是至少有一个球。 创建时传递给 hat 对象的参数应转换为 contents 实例变量。 contents 应该是一个字符串列表,其中包含帽子中每个球的一个项目。 列表中的每一项都应该是一个颜色名称,代表该颜色的单个球。 例如,如果你的帽子是 {"red": 2, "blue": 1}contents 应该是 ["red", "red", "blue"]

Hat 类应该有一个 draw 方法,该方法接受一个参数,该参数指示要从帽子中抽取的球数。 此方法应该从 contents 中随机删除球,并将这些球作为字符串列表返回。 在抽取过程中球不应回到帽子中,类似于没有放回的黑盒实验。 如果要抽的球数量超过可用数量,则返回所有球。

Next, create an experiment function in main.py (not inside the Hat class). 此函数应接受以下参数:

  • hat:一个包含球的帽子对象,应该在函数内复制。
  • expected_balls:一个对象,指示尝试从帽子中抽取的确切球组以进行实验。 例如,要确定从帽子中抽取 2 个蓝球和 1 个红球的概率,将 expected_balls 设置为 {"blue":2, "red":1}
  • num_balls_drawn:每次实验中从帽子中抽出的球数。
  • num_experiments:要执行的实验数量。 (进行的实验越多,近似概率就越准确。)

experiment 函数应该返回一个概率。

例如,如果你想确定当你从一个包含 6 个黑球、4 个红球和 3 个绿球的帽子中抽出 5 个球时,至少得到 2 个红球和 1 个绿球的概率, 你将进行 N 次实验,记录其中你至少得到 2 个红球和 1 个绿球的次数 M,并估计概率为 M/N。 每个实验都包括从一个装有指定球的帽子开始,抽出几个球,并检查你是否抽到了你试图抽出的球。

以下是基于上面的示例调用 experiment 函数的方法,其中包含 2000 个实验:

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)

The output would be something like this:

0.356

由于这是基于随机抽取的,因此每次运行代码时概率会略有不同。

Hint: Consider using the modules that are already imported at the top. Do not initialize random seed within the file.

--hints--

Creation of hat object should add correct contents.

({
  test: () => {
    pyodide.FS.writeFile("/home/pyodide/probability_calculator.py", code);
    pyodide.FS.writeFile(
      "/home/pyodide/test_module.py",
      `
import unittest
import probability_calculator
from importlib import reload

reload(probability_calculator)

probability_calculator.random.seed(95)
class UnitTests(unittest.TestCase):
    maxDiff = None
    def test_hat_class_contents(self):
        hat = probability_calculator.Hat(red=3,blue=2)
        actual = hat.contents
        expected = ["red","red","red","blue","blue"]
        self.assertEqual(actual, expected, 'Expected creation of hat object to add correct contents.')
        `
    );
    const testCode = `
from unittest import main
import test_module
from importlib import reload

reload(test_module)
t = main(module='test_module', exit=False)
t.result.wasSuccessful()
`;
    const out = runPython(testCode);
    assert(out);
  },
});

The draw method in hat class should reduce number of items in contents.

({
  test: () => {
    pyodide.FS.writeFile("/home/pyodide/probability_calculator.py", code);
    pyodide.FS.writeFile(
      "/home/pyodide/test_module.py",
      `
import unittest
import probability_calculator
from importlib import reload

reload(probability_calculator)

probability_calculator.random.seed(95)

class UnitTests(unittest.TestCase):
    maxDiff = None
    def test_hat_draw(self):
        hat = probability_calculator.Hat(red=5,blue=2)
        actual = hat.draw(2)
        expected = ['blue', 'red']
        self.assertEqual(actual, expected, 'Expected hat draw to return two random items from hat contents.')
        actual = len(hat.contents)
        expected = 5
        self.assertEqual(actual, expected, 'Expected hat draw to reduce number of items in contents.')
        `
    );
    const testCode = `
from unittest import main
import test_module
from importlib import reload

reload(test_module)
t = main(module='test_module', exit=False)
t.result.wasSuccessful()
`;
    const out = runPython(testCode);
    assert(out);
  },
});

The draw method should behave correctly when the number of balls to extract is bigger than the number of balls in the hat.

({
  test: () => {
    pyodide.FS.writeFile("/home/pyodide/probability_calculator.py", code);
    pyodide.FS.writeFile(
      "/home/pyodide/test_module.py",
      `
import unittest
import probability_calculator
from importlib import reload

reload(probability_calculator)

probability_calculator.random.seed(95)
class UnitTests(unittest.TestCase):
    maxDiff = None
    def test_hat_draw_2(self):
        hat = probability_calculator.Hat(yellow=5,red=1,green=3,blue=9,test=1)
        actual = hat.draw(20)
        expected = ['yellow', 'yellow', 'yellow', 'yellow', 'yellow', 'red', 'green', 'green', 'green', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'blue', 'test']
        self.assertEqual(actual, expected, 'Expected hat draw to return all items from hat contents.')
        actual = len(hat.contents)
        expected = 0
        self.assertEqual(actual, expected, 'Expected hat draw to leave no items in contents.')
        `
    );
    const testCode = `
from unittest import main
import test_module
from importlib import reload

reload(test_module)
t = main(module='test_module', exit=False)
t.result.wasSuccessful()
`;
    const out = runPython(testCode);
    assert(out);
  },
});

The experiment method should return a different probability.

({
  test: () => {
    pyodide.FS.writeFile("/home/pyodide/probability_calculator.py", code);
    pyodide.FS.writeFile(
      "/home/pyodide/test_module.py",
      `
import unittest
import probability_calculator
from importlib import reload

reload(probability_calculator)

probability_calculator.random.seed(95)
class UnitTests(unittest.TestCase):
    maxDiff = None
    def test_prob_experiment(self):
        hat = probability_calculator.Hat(blue=3,red=2,green=6)
        probability = probability_calculator.experiment(hat=hat, expected_balls={"blue":2,"green":1}, num_balls_drawn=4, num_experiments=1000)
        actual = probability
        expected = 0.272
        self.assertAlmostEqual(actual, expected, delta = 0.01, msg = 'Expected experiment method to return a different probability.')
        hat = probability_calculator.Hat(yellow=5,red=1,green=3,blue=9,test=1)
        probability = probability_calculator.experiment(hat=hat, expected_balls={"yellow":2,"blue":3,"test":1}, num_balls_drawn=20, num_experiments=100)
        actual = probability
        expected = 1.0
        self.assertAlmostEqual(actual, expected, delta = 0.01, msg = 'Expected experiment method to return a different probability.')
        `
    );
    const testCode = `
from unittest import main
import test_module
from importlib import reload

reload(test_module)
t = main(module='test_module', exit=False)
t.result.wasSuccessful()
`;
    const out = runPython(testCode);
    assert(out);
  },
});

--seed--

--seed-contents--

import copy
import random

class Hat:
    pass

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

--solutions--

import copy
import random

class Hat:
    def __init__(self, **hat):
        self.hat = hat
        contents = []
        for i in hat:
            for j in range(hat[i]):
                contents.append(i)           
        self.contents = contents


    def draw(self, number): 
        drawn = []
        if number >= len(self.contents):
            drawn.extend(self.contents)
            self.contents = []
        else:
            for i in range(number):
                drawn.append(
                    self.contents.pop(random.randrange(len(self.contents)))
                )                
        return drawn

def experiment(hat, expected_balls, num_balls_drawn, num_experiments):    
    expected_balls_list = []
    drawn_list = []
    success = 0
    for i in expected_balls:
        for j in range(expected_balls[i]):
            expected_balls_list.append(i)
    for j in range(num_experiments):
        hat_copy = copy.deepcopy(hat)
        drawn_list.append(hat_copy.draw(num_balls_drawn))        
        exp_ball_list_copy = expected_balls_list[:]
        for k in range(len(drawn_list[j])):
            try:
                ind = exp_ball_list_copy.index(drawn_list[j][k])
                exp_ball_list_copy.pop(ind)
            except:
                continue
        if len(exp_ball_list_copy) == 0:
            success += 1        
    probability = success/num_experiments    
    return probability