If you're seeing this message, it means we're having trouble loading external resources on our website.

如果你被网页过滤器挡住,请确保域名*.kastatic.org*.kasandbox.org 没有被阻止.

主要内容

随机游走

在我们讨论复杂的矢量和基于物理的运动的之前,先想一想简单地在屏幕上移动意味着什么。 让我们从最著名和最简单的运动模拟之一 —— 随机游走开始。
想象你站在平衡木的中间。 每十秒钟,你抛一个硬币。 正面,向前迈出一步。 反面,退后一步。 这是一个随机游走——由一系列随机步骤定义的路径。 掉下平衡木并落到地板上,你可以通过将相同的硬币翻转两次来执行二维随机游走,结果如下:
翻转 1翻转 2结果
朝上朝上前进。
朝上朝下往右走。
朝下朝上往左走。
朝下朝下后退。
是的,这似乎是一个特别简单的算法。 尽管如此,随机游走可以用来模拟现实世界中发生的现象,从天然气中的分子运动到在赌场度过一天的赌徒的行为。 至于我们,我们通过研究一个有三个目标的随机游走来开始这个课题。

随机游走对象

让我们首先通过构建一个Walker对象来回顾一下面向对象编程(OOP)。这只是一个粗略的复习。如果您以前从未使用过OOP,那么应该先阅读面向对象的JavaScript部分。
JavaScript中的 对象 是一种数据类型,它通过其原型附加了属性和功能。我们正在设计一个Walker对象,它既可以跟踪数据(它存在于屏幕上的位置),也可以执行某些操作(例如绘制自己或移动一步)。
为了创建Walker的实例,我们需要定义一个Walker对象。这个对象就是切曲奇饼的模型,每个新的 Walker实例就是一个曲奇饼。
让我们从定义Walker对象类型开始。 Walker只需要两个数据 —— 一个数字用于其x坐标,一个用于其y坐标。我们将在构造函数中设置它们,将它们设置为画布的中心。
var Walker = function() {
    this.x = width/2;
    this.y = height/2;
};
除了跟踪它的x和y之外,我们的Walker对象还将有可以调用它的方法。第一种方法是允许对象将自身显示为黑点。请记住,我们通过将方法附加到对象的prototype来向JavaScript中的对象添加方法。
Walker.prototype.display = function() {
    stroke(0, 0, 0);
    point(this.x, this.y);
};
第二种方法将指示Walker对象采取行动。现在,这是让事情变得更有趣的地方。还记得我们采取随机步骤的地板吗?那么,现在我们可以以相同的方法使用我们的画布。有四个可能的步骤。可以通过递增 xx++)来模拟往右边移动的步骤;通过递减 xx--)向左移动;向前移动一个像素(y++);向上移动一个像素(y--)。我们如何从这四种选择中挑选?早些时候我们说可以翻转两个硬币。然而,在ProcessingJS中,当我们想从一个选项列表中随机选择时,可以使用 random()来选择一个随机数。
Walker.prototype.walk = function() {
    var choice = floor(random(4));
};
上面的代码行选择0到4之间的随机浮点数,并使用floor()将结果转换为整数,结果为0,1,2或3 。从技术上讲,最高的数字永远不会是4.0,而是3.999999999(与小数位数一样多9);因为floor()返回最小或相等的最接近的整数,我们得到的最高结果是3 。接下来,我们根据选择的随机数采取适当的步骤(左,右,上或下)。
Walker.prototype.walk = function() {
    var choice = floor(random(4));
    if (choice === 0) {
        this.x++;
    } else if (choice === 1) {
        this.x--;
    } else if (choice === 2) {
        this.y++;
    } else {
        this.y--;
    } 
};
现在我们已经编写了这个类,是时候在程序中创建一个实际的Walker对象了。假设我们正在寻找单个随机游走的模型,通过使用new运算符调用构造函数来声明,并初始化一个类型为Walker的全局变量。
var w = new Walker();
接下来,为了让walker真正做某事,我们定义draw()函数,并告诉walker每次调用时都移动一步并绘制自己:
draw = function() {
    w.walk();
    w.display();
};
由于我们在draw函数中不调用background(),可以在画布上看到随机游走的踪迹:

改进随机游走

我们可以对随机游走进行一些改进。首先,这个游走者的步骤选择限于四个选项 - 向上,向下,向左和向右。但窗口中的任何给定像素都有八个可能的邻居,第九种可能性是留在同一个地方。
Nature of Code 图
图I.1
要实现一个可以移动到任何相邻像素(或保持放置)的 Walker对象,我们可以选择0到8之间的数字(一共九种选择)。但是,编写代码的更有效方法是简单地从x轴移动的方向的三个可能步骤(-1,0或1)和沿y轴方向的三个可能步骤(-1,0或1)中进行选择。
Walker.prototype.walk = function() {
  var stepx = floor(random(3))-1;
  var stepy = floor(random(3))-1;
  this.x += stepx;
  this.y += stepy;
};
更进一步,我们可以使用十进制代替xy并根据-1和1之间的任意随机值移动 —— 如果我们编程环境可以显示“2.2”和“2.4”之间的差异:
Walker.prototype.walk = function() {
  var stepx = random(-1, 1);
  var stepy = random(-1, 1);
  this.x += stepx;
  this.y += stepy;
};
所有这些关于“传统”随机游走的变化都有一个共同点:在任何时刻,Walker 选择在给定方向上迈出一步(或根本不动)的概率等于 Walker将做出任何其他给定选择的概率。换句话说,如果有四个可能的步骤,那么Walker将采取任何给定步骤的概率为1/4(或25%)。有九个可能的步骤,它是1/9(或11.1%)的机会。
方便的是,这符合random()函数的工作原理。它的随机数发生器产生所谓的“均匀”数字分布。我们可以使用一个程序来测试这个分布,该程序在每次拾取随机数时计算,并将其绘制为矩形的高度:
运行几分钟后,这些杆的高度是否相同?可能不。我们的样本量(即我们选择的随机数的数量)相当小,并且偶尔存在一些差异,其中某些数字被更频繁地选取。随着时间的推移,使用一个好的随机数生成器,这类情况只会偶然出现。
我们从random()函数得到的随机数并不是真正随机的;因此它们被称为“伪随机”。它们是模拟随机性的数学函数的结果。这个函数随着时间的推移会产生一个模式,但是这个时间段太长了,对我们来说,它和纯随机性一样好!
在下一节中,我们将讨论不同的方法,我们可以创建有“倾向”向某些方向的步行者(Walker)。在深入研究之前,有一个挑战等待你来面对!

“自然模拟”系列课程是由 Daniel Shiffman 的 "编程的本质" 衍生而来,基于 知识共享 著名-非商用性 3.0 本地化许可协议

想加入讨论吗?

尚无帖子。
你会英语吗?单击此处查看更多可汗学院英文版的讨论.