按单元格与中心单元格的距离对单元格进行排序

发布于 2024-10-04 10:33:41 字数 2680 浏览 2 评论 0原文

有人问我,当时看来是一个天真的问题:

我们如何根据与预定义/预先计算的中心单元的距离对 2D 数组中的单元进行排序。

下面的表格显示了特定单元格与预定义中心单元格的距离(它们的值为 0)。 n 值意味着距离中心有 n 个单元格:

+----+----+----+----+----+----+----+----+
| 4  | 4  | 3  | 3  | 3  | 3  | 4  | 4  |
+----+----+----+----+----+----+----+----+
| 4  | 3  | 2  | 2  | 2  | 2  | 3  | 4  |
+----+----+----+----+----+----+----+----+
| 3  | 2  | 1  | 1  | 1  | 1  | 2  | 3  |
+----+----+----+----+----+----+----+----+
| 3  | 2  | 1  | 0  | 0  | 1  | 2  | 3  |
+----+----+----+----+----+----+----+----+
| 3  | 2  | 1  | 0  | 0  | 1  | 2  | 3  |
+----+----+----+----+----+----+----+----+
| 3  | 2  | 1  | 1  | 1  | 1  | 2  | 3  |
+----+----+----+----+----+----+----+----+
| 4  | 3  | 2  | 2  | 2  | 2  | 3  | 4  |
+----+----+----+----+----+----+----+----+
| 4  | 4  | 3  | 3  | 3  | 3  | 4  | 4  |
+----+----+----+----+----+----+----+----+

我通过计算欧几里德空间中 ( x1, y1 ) 和 ( x2, y2 ) 之间的直线距离并使用旧式“装饰排序”对它们进行排序来解决这个问题-取消装饰”方法。

这就是我最终得到的结果:

import math
boardMaxRow = 8
boardMaxCol = 8
thatAbsurdLargeValue = ( 1 + boardMaxRow + boardMaxCol )
centerCells = ( ( 3, 3 ), ( 3, 4 ), ( 4, 3 ), ( 4, 4 ) )
cellsOrderedFromTheCenter = {}

for row in xrange( boardMaxRow ):
    for col in xrange( boardMaxCol ):
        minDistanceFromCenter = thatAbsurdLargeValue
        for ( centerX, centerY ) in centerCells:
            # straight line distance between ( x1, y1 ) and ( x2, y2 ) in an Euclidean space
            distanceFromCenter = int( 0.5 + math.sqrt( ( row - centerX ) ** 2 + ( col - centerY ) ** 2 ) )
            minDistanceFromCenter = min( minDistanceFromCenter, distanceFromCenter )
        cellsOrderedFromTheCenter[ ( row, col ) ] = minDistanceFromCenter

board = [ keyValue for keyValue in cellsOrderedFromTheCenter.items() ]

import operator

# sort the board in ascending order of distance from the center
board.sort( key = operator.itemgetter( 1 ) )
boardWithCellsOrderedFromTheCenter = [ key for ( key , Value ) in board ]
print boardWithCellsOrderedFromTheCenter

输出:

[(3, 3), (4, 4), (4, 3), (3, 4), (5, 4), (2, 5), (2, 2), (5, 3), (3, 2), (4, 5), (5, 5), (2, 3), (4, 2), (3, 5), (5, 2), (2, 4), (1, 3), (6, 4), (5, 6), (2, 6), (5, 1), (1, 2), (6, 3), (1, 5), (3, 6), (4, 1), (1, 4), (2, 1), (6, 5), (4, 6), (3, 1), (6, 2), (7, 3), (4, 7), (3, 0), (1, 6), (3, 7), (0, 3), (7, 2), (4, 0), (2, 0), (5, 7), (1, 1), (2, 7), (6, 6), (5, 0), (0, 4), (7, 5), (6, 1), (0, 2), (7, 4), (0, 5), (0, 7), (6, 7), (7, 6), (7, 7), (0, 0), (7, 1), (6, 0), (1, 0), (0, 1), (7, 0), (0, 6), (1, 7)]

对于这样一个微不足道的问题,我对其中的代码量感到惊讶。

我的问题是:我可以让它更快和/或更短(使用更少的临时文件/函数调用)吗?

I had someone ask me, what at that time seemed an innocent enough question:

How do we ordering cells in a 2D array by their distance from predefined/precomputed center cell(s).

Here is a table showing how far the particular cell is from predefined center cells ( they have values of 0 in them ). A value of n means it's n cells away from the center:

+----+----+----+----+----+----+----+----+
| 4  | 4  | 3  | 3  | 3  | 3  | 4  | 4  |
+----+----+----+----+----+----+----+----+
| 4  | 3  | 2  | 2  | 2  | 2  | 3  | 4  |
+----+----+----+----+----+----+----+----+
| 3  | 2  | 1  | 1  | 1  | 1  | 2  | 3  |
+----+----+----+----+----+----+----+----+
| 3  | 2  | 1  | 0  | 0  | 1  | 2  | 3  |
+----+----+----+----+----+----+----+----+
| 3  | 2  | 1  | 0  | 0  | 1  | 2  | 3  |
+----+----+----+----+----+----+----+----+
| 3  | 2  | 1  | 1  | 1  | 1  | 2  | 3  |
+----+----+----+----+----+----+----+----+
| 4  | 3  | 2  | 2  | 2  | 2  | 3  | 4  |
+----+----+----+----+----+----+----+----+
| 4  | 4  | 3  | 3  | 3  | 3  | 4  | 4  |
+----+----+----+----+----+----+----+----+

I solved the problem by computing the straight line distance between ( x1, y1 ) and ( x2, y2 ) in an Euclidean space and sorting them using the old school "Decorate-Sort-Undecorate" method.

This is what I ended up with:

import math
boardMaxRow = 8
boardMaxCol = 8
thatAbsurdLargeValue = ( 1 + boardMaxRow + boardMaxCol )
centerCells = ( ( 3, 3 ), ( 3, 4 ), ( 4, 3 ), ( 4, 4 ) )
cellsOrderedFromTheCenter = {}

for row in xrange( boardMaxRow ):
    for col in xrange( boardMaxCol ):
        minDistanceFromCenter = thatAbsurdLargeValue
        for ( centerX, centerY ) in centerCells:
            # straight line distance between ( x1, y1 ) and ( x2, y2 ) in an Euclidean space
            distanceFromCenter = int( 0.5 + math.sqrt( ( row - centerX ) ** 2 + ( col - centerY ) ** 2 ) )
            minDistanceFromCenter = min( minDistanceFromCenter, distanceFromCenter )
        cellsOrderedFromTheCenter[ ( row, col ) ] = minDistanceFromCenter

board = [ keyValue for keyValue in cellsOrderedFromTheCenter.items() ]

import operator

# sort the board in ascending order of distance from the center
board.sort( key = operator.itemgetter( 1 ) )
boardWithCellsOrderedFromTheCenter = [ key for ( key , Value ) in board ]
print boardWithCellsOrderedFromTheCenter

The output:

[(3, 3), (4, 4), (4, 3), (3, 4), (5, 4), (2, 5), (2, 2), (5, 3), (3, 2), (4, 5), (5, 5), (2, 3), (4, 2), (3, 5), (5, 2), (2, 4), (1, 3), (6, 4), (5, 6), (2, 6), (5, 1), (1, 2), (6, 3), (1, 5), (3, 6), (4, 1), (1, 4), (2, 1), (6, 5), (4, 6), (3, 1), (6, 2), (7, 3), (4, 7), (3, 0), (1, 6), (3, 7), (0, 3), (7, 2), (4, 0), (2, 0), (5, 7), (1, 1), (2, 7), (6, 6), (5, 0), (0, 4), (7, 5), (6, 1), (0, 2), (7, 4), (0, 5), (0, 7), (6, 7), (7, 6), (7, 7), (0, 0), (7, 1), (6, 0), (1, 0), (0, 1), (7, 0), (0, 6), (1, 7)]

I am amazed at how much code I got in there, for such a trivial problem.

My question is : can I make it faster and/or shorter ( use less temporaries/function calls )?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

余生一个溪 2024-10-11 10:33:41

为了使其更短(并使用稍微不同的度量):

>>> rows, cols, centerx, centery = 6, 6, 2.5, 2.5
>>> [p[1:] for p in sorted((((x - centerx) ** 2 + (y - centery) ** 2, x, y)
...                         for x in xrange(rows) for y in xrange(cols)))]
[(2, 2), (2, 3), (3, 2), (3, 3), (1, 2), (1, 3), 
 (2, 1), (2, 4), (3, 1), (3, 4), (4, 2), (4, 3), 
 (1, 1), (1, 4), (4, 1), (4, 4), (0, 2), (0, 3),
 (2, 0), (2, 5), (3, 0), (3, 5), (5, 2), (5, 3), 
 (0, 1), (0, 4), (1, 0), (1, 5), (4, 0), (4, 5),
 (5, 1), (5, 4), (0, 0), (0, 5), (5, 0), (5, 5)]

为了使其更快:

  • 不要取平方根(如上面我的代码中所示):按距离的平方排序与按距离排序一样好,并且取平方根相对较慢没有必要。
  • 利用 8 向对称性:对一个八分圆进行排序并复制 8 次。

在评论中,PoorLuzer 问道:“我也不明白你为什么要初始化 centerx, centery = 2.5, 2.5。”我希望这个图能够清楚地表明:

每个轴上坐标从 0 到 5 的 6x6 网格的中心位于 2.5, 2.5

PoorLuzer 还想知道我们的指标为何不同,因为我们都使用欧几里德距离公式。嗯,我的度量标准是从每个正方形的中心到整个网格中心的距离。例如,对于这 8 个单元格,距中心的距离为 √2.5 = 约 1.58:

8 个单元格到中心的距离为 sqrt( 2.5)

而 PoorLuzer 则采用欧几里得距离到四个中心正方形中最近的一个(并将其四舍五入为整数)。对于相同的 8 个单元格,PoorLuzer 指定距离为 1:

相同的 8 个单元格的距离在 PoorLuzer 的度量中为 1

To make it shorter (and using a slightly different metric):

>>> rows, cols, centerx, centery = 6, 6, 2.5, 2.5
>>> [p[1:] for p in sorted((((x - centerx) ** 2 + (y - centery) ** 2, x, y)
...                         for x in xrange(rows) for y in xrange(cols)))]
[(2, 2), (2, 3), (3, 2), (3, 3), (1, 2), (1, 3), 
 (2, 1), (2, 4), (3, 1), (3, 4), (4, 2), (4, 3), 
 (1, 1), (1, 4), (4, 1), (4, 4), (0, 2), (0, 3),
 (2, 0), (2, 5), (3, 0), (3, 5), (5, 2), (5, 3), 
 (0, 1), (0, 4), (1, 0), (1, 5), (4, 0), (4, 5),
 (5, 1), (5, 4), (0, 0), (0, 5), (5, 0), (5, 5)]

To make it faster:

  • Don't take square roots (as in my code above): sorting by the square of the distance is just as good as sorting by the distance, and taking square roots is relatively slow unnecessary.
  • Exploit the 8-way symmetry: sort one octant and copy it out 8 times.

In the comments, PoorLuzer asks, "I also did not understand why you init centerx, centery = 2.5, 2.5." I hope this figure makes it clear:

the centre of a 6x6 grid with coordinates running from 0 to 5 on each axis is at 2.5,2.5

PoorLuzer also wonders how come our metrics differ, given that we are both using the Euclidean distance formula. Well, my metric takes the distance from the centre of each square to the centre of the whole grid. For example, for these 8 cells the distance from the centre is √2.5 = about 1.58:

distance to centre for eight cells is sqrt(2.5)

Whereas PoorLuzer is taking the Euclidean distance to the closest of the four centre squares (and rounding it to an integer). For the same 8 cells PoorLuzer assigns a distance of 1:

distance for same eight cells is 1 in PoorLuzer's metric

守不住的情 2024-10-11 10:33:41

更短很容易:

coordinates = [(x,y) for y in range(boardMaxRow) 
                     for x in range(boardMaxCol)]

def dist(A,B):
    a,b = A
    c,d = B
    # real euklidian distance without rounding
    return (a-c)**2+(b-d)**2 

print list(sorted(coordinates, 
    key=lambda x: min(dist(x,c) for c in centerCells)))

Shorter is easy:

coordinates = [(x,y) for y in range(boardMaxRow) 
                     for x in range(boardMaxCol)]

def dist(A,B):
    a,b = A
    c,d = B
    # real euklidian distance without rounding
    return (a-c)**2+(b-d)**2 

print list(sorted(coordinates, 
    key=lambda x: min(dist(x,c) for c in centerCells)))
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文