简体   繁体   English

计算二维图中偶数/奇数的总数

[英]Counting the total number of even/odd numbers in a 2D graph

  • I have a graph with dimensions M x N .我有一个尺寸为M x N的图表。
  • I also have two points [ X , Y ]我也有两点 [ X , Y ]
  • And the range r和范围r

We can only move horizontally or vertically by 1 space (we always have to move).我们只能水平或垂直移动 1 个空格(我们总是必须移动)。 The goal is to find the total number of possibilities where we can be.我们的目标是找到我们可以达到的可能性总数。

In this example we have M=5 , N=4 , [ X=2 , Y=1 ] and r=3 .在这个例子中,我们有M=5N=4 , [ X=2Y=1 ] 和r=3

在此处输入图像描述

We can see, that r is an odd number, so we're searching for odd numbers.我们可以看到, r是一个奇数,所以我们正在寻找奇数。 The result is obviously (after summing all the odd numbers) 12结果显然是(将所有奇数相加后) 12

I first tried brute force but it was too slow ( O(n^2) ).我首先尝试了蛮力,但它太慢了( O(n^2) )。

So I tried it more mathematically.所以我在数学上尝试了更多。 I tried the Von Neumann Neighborhood algorithm, but I got stuck there totally.我尝试了冯诺依曼邻域算法,但我完全被困在那里。 Finally, I'm trying it by calculating the vertical and horizontal counts in the row corresponding to x and y (count in x is 3 , count in y is 3 ).最后,我尝试通过计算对应于xy的行中的垂直和水平计数( x中的计数为3y中的计数为3 )。

在此处输入图像描述

My next step is to find the corners and using Taxicab geometry calculate how many r s it would take to go there.我的下一步是找到拐角并使用出租车几何计算到达 go 需要多少r

Then I divide the image into 4 parts (quadrants).然后我将图像分成 4 个部分(象限)。 When the path from [X, Y] to the corner of a given quadrant is less than r , just divide the number of squares by 2. When the path is larger, I create r_ , which is determined by the difference of path to corner with rr_ = taxicab - r .当从[X, Y]到给定象限角的路径小于r时,只需将正方形数除以 2。当路径较大时,我创建r_ ,由路径到角的差异决定与rr_ = taxicab - r Then just subtract r_ from the rectangle's contents and divide by 2 again.然后只需从矩形的内容中减去r_并再次除以 2。

在此处输入图像描述

As we can see, the result also comes out correctly, 3 + 3 + 1 + 1 + 2 + 2 = 12 .正如我们所看到的,结果也是正确的, 3 + 3 + 1 + 1 + 2 + 2 = 12

BUT

Here I ask:在这里我问:

在此处输入图像描述

  1. We are still working with even and odd numbers so that rounding errors often occur after dividing an odd number by 2. (for example for M=2, N=4, X=0, Y=0, r=4 - see picture. There are 8 fields - 3 blank. But 5/2 is 2.5 => why take 3 and not 2? I tried adding different rules, but it varied from example to example.)我们仍在处理偶数奇数,因此在将奇数除以 2 后经常会出现舍入错误。(例如,对于M=2, N=4, X=0, Y=0, r=4 - 参见图片。有 8 个字段 - 3 个空白。但 5/2 是 2.5 => 为什么取 3 而不是 2?我尝试添加不同的规则,但它因示例而异。)

  2. Is there a more efficient algorithm for such a calculation that would be similarly fast and less error prone?对于这样的计算,是否有更有效的算法,同样快速且不易出错?

We can count the squares which lie outside of our grid, and subtract those from the total number of odd/even squares in range (regardless of in/out of bounds).我们可以计算位于我们网格之外的方格,然后从范围内的奇数/偶数方格总数中减去这些方格(不管边界内/外)。

This is actually easier to do than we may think.这实际上比我们想象的要容易。 First, we want to be able to count the number of odd/even squares in range, with no regards for boundaries.首先,我们希望能够计算范围内奇数/偶数方格的数量,而不考虑边界。 Using the formula for the sum of natural numbers up to N - S = N * (N + 1) / 2 we can derive a couple of simple equations for this:使用直到N - S = N * (N + 1) / 2的自然数之和的公式,我们可以为此推导出几个简单的方程:

def count_all(size):
    if size % 2:  # odd size
        return (size + 1) ** 2
    return size * (size + 2) + 1

It may be a good exercise to try deriving these yourself, or at least verifying with a few examples that they are in fact correct.尝试自己推导这些可能是一个很好的练习,或者至少用一些例子来验证它们实际上是正确的。

Moving on- we can eliminate the points that fall out of bounds from above by "shrinking" our radius.继续前进——我们可以通过“缩小”我们的半径来消除从上方超出边界的点。 This is very visual, so let me give you a diagram.这是非常直观的,所以让我给你一个图表。 Imagine only the points where the range is some fixed number, say range=13 , and our center is something in the lower-right quadrant, say (17, 5) .仅想象范围是某个固定数字的点,例如range=13 ,而我们的中心位于右下象限,例如(17, 5) If we plot these points, connecting them with lines, it creates a diamond:如果我们 plot 这些点,用线连接它们,它会创建一个菱形:

 |    /\
 |   /  \
 |  /    \
-+-----------
 | /      \
 | \      /
 |  \    /
 |   \  /
 |    \/

If we're only concerned with counting the points above the axis, we could equivalently just count the points above the axis of a smaller diamond that's shifted upwards accordingly.如果我们只关心计算轴上方的点,我们可以等效地只计算相应向上移动的较小钻石的轴上方的点。 Example:例子:

 |    /\
 |   /  \
 |  /    \
-+-----------
 |  \    /
 |   \  /
 |    \/

Now, this is very convenient to work with, because exactly half of the diamond is above, half is below.现在,使用起来非常方便,因为钻石的一半在上面,一半在下面。 We do half to be careful though- there are points that fall on the axis, and both need to either be considered in bounds or out of bounds equivalently, but we can easily account for that.尽管我们做了一半要小心——有些点落在轴上,两者都需要同等地考虑在界内或界外,但我们可以很容易地解释这一点。

Using this insight, we can count the number of points that fall out of bounds across an axis by shrinking the range and shifting the center point, and counting points on half of this new plot.使用这种洞察力,我们可以通过缩小范围和移动中心点来计算超出轴范围的点数,并计算这个新 plot 一半上的点数。 The counting code:计数代码:

def count_side(size):
    if size % 2:
        return (size + 1) // 2
    return size // 2 + 1

def count_half(size):
    if size < 0:
        return 0
    return count_all(size) // 2 + count_side(size)

Note that we have to be careful for even ranges, since we need to count the center (range 0) exactly once.请注意,我们必须注意偶数范围,因为我们需要只计算中心(范围 0)一次。

We aren't done yet though- if we just subtract out the number of points that are out of bounds above and then to the left independently, we're overcount the number of points to remove, since we count points in the top-left quadrant twice.我们还没有完成——如果我们只是减去超出边界的点数,然后独立地向左移动,我们就会多算要删除的点数,因为我们计算的是左上角的点象限两次。 To handle this, we use the same trick.为了解决这个问题,我们使用相同的技巧。 We'll shrink + shift the diamond across the x-axis first, and then we'll do it again on this new diamond, but across the y-axis.我们将首先在 x 轴上缩小 + 移动钻石,然后我们将在这个新钻石上再次执行此操作,但在 y 轴上。 Note that this new diamond will end up centered on the origin.请注意,这颗新钻石最终将以原点为中心。 I'd recommend that you pause a moment at this point to visualize this and convince yourself that this is fine, and will in fact give us a new diamond for any specific range, containing 4x the number of points which fall in the top-left quadrant.我建议您在这一点上暂停片刻以想象这一点并说服自己这很好,实际上会为我们提供任何特定范围的新菱形,包含 4 倍落在左上角的点数象限。

Using this, we count the number of points in the top-left quadrant, re-add them to the total, and obtain our overall total.使用它,我们计算左上象限中的点数,将它们重新添加到总数中,并获得我们的总总数。 The entire solution below:整个解决方案如下:

def count_all(size):
    if size % 2:
        return (size + 1) ** 2
    return size * (size + 2) + 1

def count_side(size):
    if size % 2:
        return (size + 1) // 2
    return size // 2 + 1

def count_half(size):
    if size < 0:
        return 0
    return count_all(size) // 2 + count_side(size)

def count_quarter(size):
    if size < 0:
        return 0
    return count_all(size) // 4 + count_side(size)

def count_inside(x, y, s):
    total = count_all(s)
    
    out_left = count_half(s - x - 1)
    out_top  = count_half(s - y - 1)
    out_top_left = count_quarter(s - x - y - 2)
    
    inside = total - out_left - out_top + out_top_left
    return inside

A couple of examples:几个例子:

>>> print(count_inside(2, 1, 3))
12
>>> print(count_inside(2, 1, 4))
16
>>> print(count_inside(2, 1, 5))
21
>>> print(count_inside(3, 2, 8))
47

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM