模拟非矩形数组

问题描述 投票:3回答:3

通常,您希望在链接列表上执行数组,而不符合矩形数组的要求。

作为一个例子,考虑一个六边形网格,这里用中灰色的单元(3,3)的1距离邻居和浅灰色的2距离邻居表示。 enter image description here假设我们想要一个数组,它为每个单元格包含该单元格的每个1和2距离邻居的索引。一个小问题是单元格具有不同数量的X距离邻居 - 网格边界上的单元格比靠近网格中心的单元格具有更少的邻居。

(出于性能原因,我们需要一组邻居索引---而不是从单元坐标到邻居索引的函数。)

我们可以通过跟踪每个单元有多少邻居来解决这个问题。假设您有一个大小为neighbors2的数组R x C x N x 2,其中R是网格行的数量,C是列,N是网格中任何单元格的2个距离邻居的最大数量。然后,通过保持大小为n_neighbors2的额外数组R x C,我们可以跟踪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

我们如何创建上述阵列neighbors2n_neighbors2?假设我们事先知道2距离邻居N的最大数量。然后可以修改generateNeighs以始终返回相同大小的列表,因为我们可以用(0,0)填充剩余的条目。我认为这留下了两个问题:

  • 我们需要一个函数来填充neighbors2,它不是每个单独的索引而是在一个切片上运行,在我们的例子中它应该一次填充一个单元格。
  • n_neighbors2应该同时作为neighbors2填充

欢迎使用repaaccelerate阵列解决方案。

haskell repa accelerate-haskell
3个回答
2
投票

这是你向右倾斜30度的照片:

enter image description here

如您所见,您的阵列实际上是完全矩形的。

邻域周边的指数很容易找到围绕所选中心单元的六个直线部分,例如, (想象一下n == 2是周边距离中心(i,j) == (3,3)的距离):

periphery n (i,j) = 
   --     2 (3,3)
  let 
    ((i1,j1):ps1) = reverse . take (n+1) . iterate (\(i,j)->(i,j+1)) $ (i-n,j) 
    --                                                                 ( 1, 3)
    ((i2,j2):ps2) = reverse . take (n+1) . iterate (\(i,j)->(i+1,j)) $ (i1,j1) 
    --                                                                 ( 1, 5)
    .....
    ps6           = .......                                          $ (i5,j5)
  in filter isValid (ps6 ++ ... ++ ps2 ++ ps1)

整个社区很简单

neighborhood n (i,j) = (i,j) : concat [ periphery k (i,j) | k <- [1..n] ]

对于每个单元/距离组合,只需动态生成邻域索引,并在O(1)时间内为每个索引对访问您的阵列。


2
投票

完整地写出@WillNess的答案,并将来自@leftroundabout的提议结合到1D向量中存储凹陷,我们得到:

import qualified Data.Array.Accelerate as A
import Data.Array.Accelerate (Acc, Array, DIM1, DIM2, DIM3, Z(..), (:.)(..), (!), fromList, use)

rows = 7
cols = 7

type Cell = (Int, Int)

(neighs, nNeighs) = generateNeighs

-- Return a vector of indices of cells at distance 'd' or less from the given cell
getNeighs :: Int -> Cell -> Acc (Array DIM1 Cell)
getNeighs d (r,c) = A.take n $ A.drop start neighs
  where
    start = nNeighs ! A.constant (Z :. r :. c :. 0)
    n = nNeighs ! A.constant (Z :. r :. c :. d)

generateNeighs :: (Acc (Array DIM1 Cell), Acc (Array DIM3 Int))
generateNeighs = (neighsArr, nNeighsArr)
  where
    idxs = concat [[(r, c) | c <- [0..cols-1]] | r <- [0..rows-1]]
    (neighsLi, nNeighsLi, n) = foldl inner ([], [], 0) idxs
    neighsArr = use $ fromList (Z :. n) neighsLi
    nNeighsArr = use $ fromList (Z :. rows :. cols :. 5) nNeighsLi
    inner (neighs', nNeighs', n') idx = (neighs' ++ cellNeighs, nNeighs'', n'')
      where
        (cellNeighs, cellNNeighs) = neighborhood idx
        n'' = n' + length cellNeighs
        nNeighs'' = nNeighs' ++ n' : cellNNeighs

neighborhood :: Cell -> ([Cell], [Int])
neighborhood (r,c) = (neighs, nNeighs)
  where
    neighsO = [ periphery d (r,c) | d <- [1..4] ]
    neighs = (r,c) : concat neighsO
    nNeighs = tail $ scanl (+) 1 $ map length neighsO

periphery d (r,c) =
  -- The set of d-distance neighbors form a hexagon shape. Traverse each of
  -- the sides of this hexagon and gather up the cell indices.
  let 
    ps1 = take d . iterate (\(r,c)->(r,c+1))   $ (r-d,c)
    ps2 = take d . iterate (\(r,c)->(r+1,c))   $ (r-d,c+d)
    ps3 = take d . iterate (\(r,c)->(r+1,c-1)) $ (r,c+d)
    ps4 = take d . iterate (\(r,c)->(r,c-1))   $ (r+d,c)
    ps5 = take d . iterate (\(r,c)->(r-1,c))   $ (r+d,c-d)
    ps6 = take d . iterate (\(r,c)->(r-1,c+1)) $ (r,c-d)
  in filter isValid (ps6 ++ ps5 ++ ps4 ++ ps3 ++ ps2 ++ ps1)


isValid :: Cell -> Bool
isValid (r, c)
  | r < 0 || r >= rows = False
  | c < 0 || c >= cols = False
  | otherwise = True

0
投票

这可以通过使用permute函数一次填充1个单元的邻居。

import Data.Bits (shift)
import Data.Array.Accelerate as A
import qualified Prelude as P
import Prelude hiding ((++), (==))

rows = 7
cols = 7
channels = 70

type Cell = (Int, Int)

(neighs, nNeighs) = fillNeighs

getNeighs :: Cell -> Acc (Array DIM1 Cell)
getNeighs (r, c) = A.take (nNeighs ! sh1) $ slice neighs sh2
  where
    sh1 = constant (Z :. r :. c)
    sh2 = constant (Z :. r :. c :. All)

fillNeighs :: (Acc (Array DIM3 Cell), Acc (Array DIM2 Int))
fillNeighs = (neighs2, nNeighs2)
  where
    sh = constant (Z :. rows :. cols :. 18) :: Exp DIM3
    neighZeros = fill sh (lift (0 :: Int, 0 :: Int)) :: Acc (Array DIM3 Cell)
    -- nNeighZeros = fill (constant (Z :. rows :. cols)) 0 :: Acc (Array DIM2 Int)
    (neighs2, nNeighs2li) = foldr inner (neighZeros, []) indices
    nNeighs2 = use $ fromList (Z :. rows :. cols) nNeighs2li
    -- Generate indices by varying column fastest. This assures that fromList, which fills
    -- the array in row-major order, gets nNeighs in the correct order.
    indices = foldr (\r acc -> foldr (\c acc2 -> (r, c):acc2 ) acc [0..cols-1]) [] [0..rows-1]
    inner :: Cell
      -> (Acc (Array DIM3 Cell), [Int])
      -> (Acc (Array DIM3 Cell), [Int])
    inner cell (neighs, nNeighs) = (newNeighs, n : nNeighs)
      where
        (newNeighs, n) = fillCell cell neighs


-- Given an cell and a 3D array to contain cell neighbors,
-- fill in the neighbors for the given cell
-- and return the number of neighbors filled in
fillCell :: Cell -> Acc (Array DIM3 Cell) -> (Acc (Array DIM3 Cell), Int)
fillCell (r, c) arr = (permute const arr indcomb neighs2arr, nNeighs)
  where
    (ra, ca) = (lift r, lift c) :: (Exp Int, Exp Int)
    neighs2li = generateNeighs 2 (r, c)
    nNeighs = P.length neighs2li
    neighs2arr = use $ fromList (Z :. nNeighs) neighs2li
    -- Traverse the 3rd dimension of the given cell
    indcomb :: Exp DIM1 -> Exp DIM3
    indcomb nsh = index3 ra ca (unindex1 nsh)


generateNeighs :: Int -> Cell -> [Cell]
generateNeighs d cell1 = [ (row2, col2)
                            | row2 <- [0..rows]
                            , col2 <- [0..cols]
                            , hexDistance cell1 (row2, col2) P.== d]


-- Manhattan distance between two cells in an hexagonal grid with an axial coordinate system
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
© www.soinside.com 2019 - 2024. All rights reserved.