持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第32天,点击查看活动详情
随机漫游和有关数据的更多信息可视化
这本文是关于使用计算来解决问题的。到目前为止,我们将注意力集中在可以通过确定性程序解决的问题上。如果程序在相同的输入上运行,则它产生相同的输出,则程序是确定性的。这样的计算非常有用,但显然不足以解决某些类型的问题。我们生活的世界的许多方面只能准确地建模为随机过程.100如果一个过程的下一个状态可以依赖于某个随机元素,那么它就是随机的。随机过程的结果通常是不确定的。因此,我们很少能对随机过程的作用做出明确的陈述。相反,我们对他们可能做什么做出概率陈述。本文的其余大部分内容都涉及构建有助于理解不确定情况的程序。其中许多程序将是仿真模型。
模拟模拟真实系统的活动。例如,图 10-11 中的代码模拟一个人进行一系列抵押贷款付款。将该代码视为称为模拟模型的实验设备,它提供有关正在建模的系统可能行为的有用信息。除其他事项外,模拟被广泛用于预测物理系统的未来状态(例如,50年后地球的温度),并代替过于昂贵,耗时或危险的现实世界实验(例如,税法变化的影响)。
请务必始终记住,仿真模型与所有模型一样,只是现实的近似值。我们永远不可能确保实际系统的行为方式与模型预测的方式相同。事实上,我们通常可以非常确信实际系统的行为不会完全按照模型的预测。例如,并非每个借款人都会按时支付所有抵押贷款。一个常被引用的真理是“所有模型都是错误的,但有些模型是有用的。101
随机游走
1827年,苏格兰植物学家罗伯特·布朗(Robert Brown)观察到悬浮在水中的花粉颗粒似乎是随机漂浮的。他对后来被称为布朗运动的东西没有合理的解释,也没有试图用数学方法对其进行建模.1°2 1900年,路易斯·巴舍利埃(Louis Bachelier)的博士论文《推测理论》(The Theory of Speculation)首次提出了这种现象的清晰数学模型。然而,由于这篇论文涉及当时不光彩的金融市场理解问题,它在很大程度上被受人尊敬的学者所忽视。五年后,年轻的阿尔伯特·爱因斯坦(Albert Einstein)将这种随机思维带到了物理学世界,其数学模型与Bachelier的数学模型几乎相同,并描述了如何用它来确认原子的存在.103出于某种原因,人们似乎认为理解物理学比赚钱更重要。 世界开始关注。时代当然不同了。布朗运动是随机游走的一个例子。随机游走被广泛用于模拟物理过程(例如,扩散),生物过程(例如,DNA从异质双链体中置换RNA的动力学)和社会过程(例如,股票市场的运动)。
在本章中,我们出于三个原因研究随机游走:
·随机游走本质上很有趣并且被广泛使用。它们为我们提供了一个很好的例子,说明如何使用抽象数据类型和继承来构建程序,特别是模拟模型。
它们提供了一个机会来介绍 Python 的更多功能,并演示一些额外的技术生产地块。
酒鬼散步
让我们看一个实际涉及步行的随机游走。一个醉酒的农夫站在田野中间,每一秒农夫就朝一个随机的方向迈出一步。她(或他)在 1000 秒内与原点的预期距离是多少?如果她走了很多步,她是有可能离原点越来越远,还是更有可能一遍又一遍地徘徊回到原点,最终离她开始的地方不远?让我们写一个模拟来找出答案。在开始设计程序之前,尝试对程序要建模的情况产生一些直觉总是一个好主意。让我们首先使用笛卡尔坐标勾勒出一个简单的情况模型。假设农民站在田野里,草被神秘地剪成一张方格纸。进一步假设农民采取的每个步骤的长度为一,并且平行于 x 轴或 y 轴。
图 16-1 左侧的图片描绘了一个站在田地中间的农民1°4。笑脸表示农民在一步后可能去过的所有地方。请注意,在一步之后,她总是离她开始的地方正好一个单位。让我们假设她在第一步时从她的初始位置向东徘徊。在她迈出第二步后,她离她最初的位置有多远?
看看右边图片中的笑脸,我们看到,以 0.25 的概率,她会远离 o 个单位,概率为 o.25,她会远离 2 个单位,而概率为 o.5,她会远离 ✓2 个单位.105 所以,平均而言,她走两步后会比走一步后更远。第三步呢?如果第二步是顶部或底部的笑脸,第三步将使农民一半时间更接近原点,一半时间更远。如果第二步是左笑脸(原点),第三步将远离原点。如果第二步是向右的笑脸,第三步将接近原点四分之一的时间,更远的四分之三时间。
似乎醉汉走的步数越多,与原点的预期距离就越大。我们可以继续详尽地列举各种可能性,也许可以对这个距离如何随着步数的增长产生一种很好的直觉。但是,它变得越来越乏味,因此编写一个程序来为我们执行此操作似乎是一个更好的主意。
让我们从考虑一些数据抽象开始设计过程,这些数据抽象可能对构建此模拟以及其他类型的随机游走的模拟有用。像往常一样,我们应该尝试发明与我们试图建模的情况中出现的事物类型相对应的类型。三种明显的类型是位置、字段和醉酒。当我们查看提供这些类型的类时,值得考虑每个类对它们允许我们构建的模拟模型类型可能意味着什么。
让我们从位置开始,图 16-2。这是一个简单的类,但它确实体现了两个重要的决策。它告诉我们,模拟最多涉及两个维度。这与上图一致。此外,由于为 delta_x 和 delta_y 提供的值可以是浮点数而不是整数,因此此类中没有关于醉汉可能移动的方向集的内置假设。这是非正式模型的推广,其中每个步骤的长度为一,并且平行于 x 轴或 y 轴。类字段(图 16-2)也非常简单,但它也体现了值得注意的决策。它只是维护醉汉到位置的映射。它对位置没有限制,因此大概字段的大小是无限的。它允许在随机位置将多个醉汉添加到字段中。它没有说明醉汉移动的模式,也没有禁止多个醉汉占据同一位置或穿过其他醉汉占据的空间。
Eigure 16-3中的醉酒和Usual_drunk课程定义了醉汉在田野中徘徊的方式。特别是,step_choices在Usual_drunk中的值引入了以下限制:
每个步骤的长度为 1,平行于 X 轴或 Y 轴。由于函数 random.choice 返回它所传递的序列的随机选择成员,因此每种步骤的可能性相同,并且不受前面步骤的影响。稍后我们将研究具有不同行为的醉酒子类。
下一步是使用这些类来构建回答原始问题的模拟。图 16-4 包含此模拟中使用的三个函数。
函数步行模拟一次num_steps步。该函数sim_walks调用步行来模拟每个 num_steps 步的试验步行数。函数醉酒测试调用模拟步行来模拟不同长度的步行。
参数 d_class 的 sim_walks 属于类类型,在代码的第一行中用于创建相应子类的醉酒。稍后,当从 Field.move_drunk 调用 drunk.take_step 时,将自动选择相应子类中的方法。
该函数drunk_test还有一个类型为 class 的参数 d_class。它被使用了两次,一次在调用模拟步行中,一次在第一个打印语句中。在 print 语句中,内置类attribute_name_is用于获取具有类名的字符串。当我们执行 drunk_test((10, 100, 1000, 10000), 100, Usual_drunk) 时,它打印
这是令人惊讶的,因为我们之前开发的直觉是平均距离应该随着步数的增加而增长。这可能意味着我们的直觉是错误的,也可能意味着我们的模拟有问题,或者两者兼而有之。
此时要做的第一件事是对我们已经认为知道答案的值运行模拟,并确保模拟生成的结果与预期结果匹配。让我们尝试零步(与原点的平均、最小和最大距离都应为 o)和一步(与原点的平均、最小和最大距离都应为 1)的步行。
当我们运行 drunk_test((0,1)、100、Usual_drunk) 时,我们得到了高度可疑的结果
零步的平均距离到底怎么可能超过8?我们的模拟中必须至少有一个错误。经过一番调查,问题很明显。在sim_walks中,函数调用walk(f, Homer, num_trials)应该是walk(f, Homer, num_steps)。
这里的寓意很重要:在查看模拟结果时,始终要带上一些怀疑态度。首先询问结果是否通过气味测试(即是否合理)。并且始终对参数进行模拟 test106,您对结果应该是什么有很强的直觉。
当模拟的校正版本在我们的两个简单案例上运行时,它会产生完全预期的答案:
当在更长的步行中运行时,它打印Usual_drunk 10步的步行:
正如预期的那样,与原点的平均距离随着步数的增加而增加。
现在让我们看一下与原点的平均距离图,图 16-5。为了了解距离的增长速度,我们在图上放置了一条线,显示步数的平方根(并将步数增加到 100,000)。