17. 首先我们获取曼哈顿距离:如何在一个网格里测量距离

来源:互联网 发布:开源crm软件排名 编辑:程序博客网 时间:2024/06/05 21:57

17.First We Take Manhattan: How to measure distance on a grid

We saw very early on in this blog how to make an actor aim for a target location. One way to use this is for enemies chasing you in games, or things like commanding units in strategy games. But what if the world is based on a small number of squares (like a chess-board), where movement is only allowed horizontally and vertically, not diagonally?

我们在比较早前的帖子里了解了如何让一个角色朝向目标位置。它的用途之一是在游戏中让敌人追逐玩家,或者在策略游戏中指挥单位行动。但是如果世界是基于一系列小方格(类似棋盘)的,其中的物体只能水平和垂直运动而不能斜线运动,那又将如何?

Distance

距离

How far away is the wombat from the leaf (in terms of grid squares)?

袋熊离树叶有多远(在方格情况下)?

If you’ve been reading this blog for a while, your answer is hopefully to reach for Pythagoras, and perhaps come up with\sqrt{6^2 + 3^2} = \sqrt{45} = 6.71 (\text{to 2dp}). This is true, but traditionally in Greenfoot, wombats are only allowed to move horizontally or vertically into adjacent squares. The diagonal distance is not helpful in this situation, because it doesn’t tell you how far the wombat has to move to reach the leaf.

如果你已经关注这个博客有一段时间了,你的答案很可能是使用勾股定理,即\sqrt{6^2 + 3^2} = \sqrt{45} = 6.71 (\text{to 2dp})。这是对的,但是通常在Greenfoot中,袋熊只允许水平或垂直地向相邻的方格移动。在这种情况下斜线距离没有用处,因为它不能告诉你袋熊需要移动多远距离来够到树叶。

Shortest Route

最短路径

To measure how far the wombat is from the leaf, let’s first find the shortest path (least squares) that the wombat has to move to reach the leaf. Here’s a likely candidate path:

为了测量袋熊离树叶有多远,让我们首先找到袋熊移动够到树叶的最短路线(最少的方格数)。这儿是一个合适的参考路线:

If you count the arrows, you’ll find the wombat has to move 9 squares. To know how to calculate this distance automatically, here’s the key insight. How long is this alternative route for the wombat to take to the leaf?

如果你计算箭头数量,你会发现袋熊需要移动9个方格。为了知道如何自动计算这个距离,这儿有一个关键的直觉。以下的这条袋熊移动到树叶的迂回路线有多长?

If you count the arrows, you’ll find it’s the same distance: 9 squares! If you look at the two routes, you’ll see that they actually feature the same number of vertical moves and horizontal moves as each other. It’s just that the latter route rearranges them to do clump all the vertical moves together and all the horizontal moves together. So having seen that the latter route is the same distance, we just need to calculate the distance for that. And this becomes easy: it’s the number of vertical moves necessary (which is the vertical distance between them) plus the number of horizontal moves necessary (which is the horizontal distance between them). So the distance in our case is6 + 3 = 9, and in general is:

如果你数一下箭头数,你会发现距离是一样的:9个方格!如果你观察这两条路线,你会发现它们实际上拥有相同数目的垂直步数和水平步数。只不过将后一条路线重新排列,从而将其所有垂直箭头放在一起而将所有水平箭头放在一起。于是已经知道后一条线路的距离是一样的,我们只需要去计算该距离即可。这就变得容易了:它就是垂直的必要步数(即它们的垂直距离)加上水平的必要步数(即它们间的水平距离)。因此在我们的例子中是6+3=9,而一般形式如下:

\text{manhattanDistance}(xA, yA, xB, yB) = \text{abs}(xA - xB) + \text{abs}(yA - yB)

The abs function stands for absolute, which makes the given number positive (i.e. -3 becomes 3, 4 stays as 4, 0 stays as 0). This distance that we’ve described is called the Manhattan distance, so-called because Manhattan is laid out in US-style city blocks. Since you can’t “cut across” city blocks in Manhattan, the distance you have to walk between two destinations is not the straight-line distance, it’s the Manhattan distance.

abs函数代表绝对值,它使得给定的数字是正数(例如-3变为3,4保持不变,0仍为0)。我们上面讨论的这种距离被称为曼哈顿距离,这样叫是因为曼哈顿是以美国风格的城市街区布局的。由于在曼哈顿中我们不能“抄近路穿过”城市街区,因而在两个目标间的步行距离不是直线距离,这就是曼哈顿距离。

First We Take Manhattan

首先我们获得曼哈顿距离

From the second route diagram above, it is quite easy to formulate an algorithm to help the wombat to the leaf.

从上面的第二个路线图可以看到,描述一个算法让袋熊到达树叶是非常容易的。

if (getX() < leaf.getX())    setLocation(getX() + 1, getY());else if (getX() > leaf.getX())    setLocation(getX() - 1, getY());/* if we get here, we must have the same X coordinate as the leaf */else if (getY() < leaf.getY())    setLocation(getX(), getY() + 1);else if (getY() > leaf.getY())    setLocation(getX(), getY() -1);else    Greenfoot.stop();

We can actually simplify this code by co-opting a common mathematical method, signum. This is short for sign (of) number: if the number is negative it returns -1, if the number is zero it returns zero, and if the number is positive it returns 1. If you apply this to a distance, you find out which way to head!

我们实际上可以通过指派一个公共的数学方signum来简化以上代码。这是数字符号的简写:如果数字是负数它返回-1,如果数字为零返回零,如果数字是正数返回1.如果使用它来计算距离,你便可以找到前进的路线!。

int xDir = (int)Math.signum(leaf.getX() - getX());int yDir = (int)Math.signum(leaf.getY() - getY());if (xDir != 0)    setLocation(getX() + xDir, getY());else if (yDir != 0)    setLocation(getX(), getY() + yDir);else    Greenfoot.stop();

And Then We Take Berlin

接着我们获得柏林距离

The above routing code is fine, and we’ve seen that this simple route is as short as any other. But it still looks a bit funny when we run it; the wombat walks in two straight lines, but doesn’t look much like it’s heading towards the leaf:

上面计算路线的代码是很好的,同时我们已经知道这条简单的路线和其他路线是一样短的。但是当我们运行的时候它看起来仍旧有一些可笑;袋熊在两条直线上行走,而看起来不太像是朝着树叶行走:

One way to fix this is to re-introduce Pythagoras to help us. At each step, the wombat may have two choices to make (while still using the shortest route): move down or move right. We make the choice between the two by choosing the direction that will reduce the straight-line diagonal distance (as measured by Pythagoras) the most. This makes the wombat take what looks more like the shortest route:

一种改进办法是重新引入勾股定理来帮助我们。在每一步中,袋熊可能作出两种选择(同时仍然使用最短路线):向下或向右。我们在两者间作出选择的方式是,选择能够最大地减少斜向直线距离的方向(通过勾股定理测算)。

Here’s the code:

代码如下:

        int xDir = (int)Math.signum(leaf.getX() - getX());        int yDir = (int)Math.signum(leaf.getY() - getY());        if (xDir == 0 && yDir == 0) //We're there            Greenfoot.stop();        else if (xDir == 0) // Move in yDir            setLocation(getX(), getY() + yDir);        else if (yDir == 0) // Move in xDir            setLocation(getX() + xDir, getY());        else // A choice        {            double xDirDist = Math.hypot(leaf.getX() - (getX() + xDir), leaf.getY() - getY());            double yDirDist = Math.hypot(leaf.getX() - getX(), leaf.getY() - (getY() + yDir));            if (xDirDist < yDirDist)                setLocation(getX() + xDir, getY());            else                setLocation(getX(), getY() + yDir);        }

That concludes our first look at Manhattan distance, which will come in handy again in the future when we deal with grid-based scenarios.

以上结束了我们对曼哈顿距离的初次讨论,在今后我们处理基于网格的游戏剧本时迟早会用到它的。