【发布时间】:2018-09-16 12:13:38
【问题描述】:
通常您希望数组的性能优于链表,但又不符合矩形数组的要求。
以六边形网格为例,此处以中灰色显示单元格 (3, 3) 的 1 距离邻居和浅灰色的 2 距离邻居。 假设我们想要一个数组,其中包含每个单元格的每个 1 距离和 2 距离邻居的索引。一个小问题是单元格具有不同数量的 X 距离邻居 - 网格边界上的单元格比靠近网格中心的单元格具有更少的邻居。
(出于性能原因,我们想要一个相邻索引数组 --- 而不是从单元坐标到相邻索引的函数。)
我们可以通过跟踪每个单元格有多少邻居来解决这个问题。假设你有一个数组
neighbors2 的大小为 R x C x N x 2,其中 R 是网格行数,C 是列数,N 是网格中任何单元格的最大 2 距离邻居数。
然后,通过保留一个大小为R x C 的附加数组n_neighbors2,我们可以跟踪neighbors2 中的哪些索引被填充,哪些只是零填充。例如,要检索单元格 (2, 5) 的 2 距离邻居,我们只需像这样对数组进行索引:
someNeigh = neighbors2[2, 5, 0..n_neighbors2[2, 5], ..]
someNeigh 将是一个n_neighbors2[2, 5] x 2 索引数组(或视图),其中someNeigh[0, 0] 产生第一个邻居的行,someNeigh[0, 1] 产生第一个邻居的列,依此类推。
注意位置上的元素
neighbors2[2, 5, n_neighbors2[2, 5]+1.., ..]
无关紧要;这个空间只是填充以保持矩阵矩形。
如果我们有一个函数可以找到任何单元格的 d 距离邻居:
import Data.Bits (shift)
rows, cols = (7, 7)
type Cell = (Int, Int)
generateNeighs :: Int -> Cell -> [Cell]
generateNeighs d cell1 = [ (row2, col2)
| row2 <- [0..rows-1]
, col2 <- [0..cols-1]
, hexDistance cell1 (row2, col2) == d]
hexDistance :: Cell -> Cell -> Int
hexDistance (r1, c1) (r2, c2) = shift (abs rd + abs (rd + cd) + abs cd) (-1)
where
rd = r1 - r2
cd = c1 - c2
我们如何创建上述数组neighbors2 和n_neighbors2?假设我们事先知道最大 2 距离邻居 N。然后可以修改generateNeighs 以始终返回相同大小的列表,因为我们可以用 (0, 0) 填充剩余的条目。在我看来,这留下了两个问题:
- 我们需要一个函数来填充
neighbors2,它不是对每个单独的索引进行操作,而是对一个切片进行操作,在我们的例子中,它应该一次填充一个单元格。 -
n_neighbors2应同时填充为neighbors2
欢迎使用 repa 或 accelerate 数组的解决方案。
【问题讨论】:
-
抱歉,我没有仔细阅读您的问题,但是仅查看您的图像,数组是完美的矩形,您只是有一些特殊的邻居关系。查看您的图像skewed 30 degrees to the right 更容易看出如何根据所选中心图块周围的 6 条线段定义这种关系。如果您已经知道这一切并在问题中写到(我跳过了),我们深表歉意。
-
@WillNess 问题不是将网格存储为一个数组,而是为每个单元格存储其邻居的“列表”。这不会是矩形的,因为单元格有不同数量的邻居。
-
我几乎可以肯定,除非网格很小,否则使用相邻索引数组将比动态计算它们慢。 CPU 缓存是一种非常有限且极其宝贵的资源;使用其中的一些来保存可以在几个廉价指令中计算的值听起来并不好。
-
我建议您考虑替代 monolithic-array-with-some-dummy-entries 方法。这是一个非常 Matlab 要做的事情。我会考虑将所有单元格的所有邻居排列在一个 1D 未装箱向量中,然后为每个单元格存储该向量的偏移量和邻居的数量。以纯函数方式构建会更好,如果邻居的数量变化很大,它也需要更少的总内存。
-
@davidcox 在将网格存储在矩形阵列中时会导致空间浪费。请参阅redblobgames.com/grids/hexagons,获取有关六边形网格的优秀资源。
标签: haskell repa accelerate-haskell