在某个时间点,您可能听说过中奖彩票的机会非常渺茫。与所有与概率相关的事情一样,多次试验可能会导致结果对你有利。现在,如果你参加很多次彩票,你中奖的机会会更大一些,这取决于你参加了多少次彩票。这仍然不能保证你最终会中奖,但是是均匀分布的,并根据大数定律(在本例中意味着大量彩票),我们可以得出相对更有可能的可能性。
重要的是要了解,每个新彩票都是独立于其他彩票的,并且相同的彩票“彩票号码”可以赢得许多不同的彩票(遵循大数定律)。您也可能会运气不好,无论您尝试了多少次,每次都选错了号码。您现在有两个选择:
理论上(和数学上),这两种情况发生的可能性相同。然而,场景 2 会给你带来轻微的优势。随着次数接近无穷大,最终每一个数字都会被选中。问题是,对于场景 1,您需要尝试更多次,希望您当时选择的数字与获胜的数字相匹配。在场景 2 中,您确信随着试验趋于无限,您的数字将在某个时刻“获胜”。对于本博文,我们将使用场景 2。
那么,在我告诉你答案之前,你认为你能回答这个问题吗?
“如果您周围的所有彩票都有正好 100 万人的老虎机,并且您为每个玩过的人都选择了同一张彩票 [x],那么您需要玩多少张彩票才能最终成为中奖者?” (欢迎评论您最初的答案)
答案是...
大约1440万次。
这篇博文的其余部分将介绍我如何得出该值、如何进行模拟以及一些注意事项。从这里开始事情会变得更加技术化。
100万人的彩票号码范围为1 - 1,000,000(或0 - 999,999)。玩家每次抽奖只能选择该范围内的号码,中奖彩票也只能来自该范围。本质上,我们可以说我们将拥有一组 100 万个数字。
考虑到用户可以选择该范围内的任何数字,我们需要满足集合中的每个项目至少被击中一次的条件。这是因为,如果每个号码至少被叫过一次,那么它将涵盖玩家可能选择的任何可能的票号。这也意味着我们不关心每个数字运行了多少次,从而使“集合”成为用于我们的模拟的理想 Python 数据结构。我们将从一个空集开始,并在每次迭代时用随机生成的数字填充它,直到该集包含指定范围内的每个数字。由于 Python 集合不重复数字,因此我们不必担心确保唯一性。
def calculate_lottery_chances(lottery_players_count): number_set = set() count = 0 while len(number_set) < lottery_players_count: gen_number = random.randint(1, lottery_players_count) number_set.add(gen_number) count += 1 return count
对于 1,000,000 人的彩票,函数调用如下:calculate_lottery_chances(1000000),它将返回中奖之前尝试彩票的次数。以这种方式排列代码使其具有很强的可扩展性。
简单来说,问题的根源就是“变异”。第一次运行该函数时,我得到了“1310 万”次作为我的值。我重新运行了一下,得到了大约 1390 万的结果。我这样做了更多次,得到的答案差异很大——在某个时候,我得到了 1500 万个。很明显,我需要这样做并找到平均值。按照到目前为止的现有模式,我认为随着平均迭代次数趋于无穷大,我将更接近于得到一个可靠的答案。需要一些可以做到这一点并且快速完成的东西,这促使我编写了这个函数:
def average_over_n_times(function, function_arg, n): """ This returns the average of the returned value of a function when it is called n times, with its (one) arg """ total = 0 for x in range(0, n): total += function(function_arg) return round(total/n)
随后,所有内容都会被修补为:
num_of_trials = average_over_n_times(calculate_lottery_chances, lottery_players_count, n)
其中“n”表示对结果进行平均的次数。然而,这带来了另一个问题,我们将在下一节中讨论。
n 的值越大,结果越接近“平均情况”。然而,考虑到仍然没有绝对性或确定性,执行这一系列任务太多次就会失去生产力。我这么说有以下原因:
牢记这些,我用以下值测试了“n”:10、20、30、50、100、1000 和 5000 次。
此时,您可能想知道为什么博文标题中没有提到“PyTorch”一词。好吧,虽然我提到用不同的值测试 n,但它与我用于所有测试的代码并不相同。
这些都是计算量很大的实验,我的 CPU 跟我说了一句话。我之前分享的代码片段是在一个具有零外部包依赖性的文件中编写的,并且该文件在 bash shell 中运行,并在前面添加了 time 命令来跟踪执行时间。以下是仅使用 CPU 时的执行时间:
n | Time (min and sec) |
---|---|
10 | 1m34.494s |
20 | 3m2.591s |
30 | 5m19.903s |
50 | 10m58.844s |
100 | 14m56.157s |
在 1000 时,我无法再让程序运行了。我不确定是不是中途断了没能停止执行,但我在4小时57分钟后取消了。我认为有几个因素影响了这一点,我将在“注意事项”部分中讨论这些因素。不管怎样,我的风扇噪音很刺耳,我知道我可能已经把笔记本电脑功率适中的 CPU 推得有点太多了。我拒绝接受失败,在思考如何至少运行 4 位迭代时,我想起了一位使用 PyTorch 的朋友告诉我的话:
“GPU 在计算密集型方面通常比 CPU 更高效”
PyTorch 使用 GPU,使其成为完成这项工作的完美工具。
PyTorch 将用于我们目的的计算,因此重构现有的calculate_lottery_chances() 代码意味着更改依赖于CPU 的数值运算并切换到合适的PyTorch 数据结构。简而言之:
calculate_lottery_chances 的重构如下:
def calculate_lottery_chances(lottery_players_count): number_set = set() count = 0 while len(number_set) < lottery_players_count: gen_number = random.randint(1, lottery_players_count) number_set.add(gen_number) count += 1 return count
我将设备设置为“xpu”,因为我的计算机使用 PyTorch 支持的 Intel Graphics GPU。
为了确保在执行过程中使用我的 GPU,我在运行之前打开了 Windows 任务管理器并导航到“性能”部分。运行时,我发现 GPU 资源使用量出现明显峰值。
对于上下文,这是之前和之后的对比:
之前:
请注意 GPU 使用率为 1%
之后:
请注意,GPU 使用率为 49%
对于不同 n 值的运行时间,GPU 的速度要快几倍。它在不到一分钟的时间内始终运行低于 100 的 n 值,并且能够计算 5000(五千!)
的 n 值这是使用 GPU 的运行时表:
n | Time (min and sec) |
---|---|
10 | 0m13.920s |
20 | 0m18.797s |
30 | 0m24.749s |
50 | 0m34.076s |
100 | 1m12.726s |
1000 | 16m9.831s |
为了直观地了解本实验中 GPU 和 CPU 操作之间的性能差距有多大,可以考虑以下数据可视化:
x 轴的上限为 100,因为我无法再从 CPU 获得实际的“及时”输出,因此没有空间与 GPU 进行比较。使用 1000 - 5000 范围内的数字进行实验,结果往往是“1440 万次”。这就是我之前得到答案的方式。
这个实验做出了假设并依赖于某些做事方式。此外,我对 PyTorch 的经验不足可能意味着可能有更有效的方法。以下是一些需要考虑的因素可能影响了我的发现的准确性或执行时间:
最后,我想指出,这是我第一次使用 PyTorch 做任何事,它的性能给我留下了深刻的印象。
当我陷入困境时,我没想到会看到性能上的如此提升。我了解了张量背后的想法以及一些有关计算更复杂的任务背后的支持机制的知识。您可以随意使用、复制或修改代码片段。
感谢您对我的包容,希望您阅读愉快。
下次再见,
干杯。 ?
以上是一次彩票探索如何让我了解 PyTorch 的强大功能的详细内容。更多信息请关注PHP中文网其他相关文章!