我在这个谜题中找到了方向,需要将特定的最近邻居归为一类。
我的输入数据是:
myList :: Map (Int, Int) Int
myList =
fromList
[((-2,-2),0),((-2,-1),0),((-2,0),2),((-2,1),0),((-2,2),0)
,((-1,-2),1),((-1,-1),3),((-1,0),0),((-1,1),0),((-1,2),1)
,((0,-2),0),((0,-1),0),((0,0),0),((0,1),0),((0,2),0)
,((1,-2),0),((1,-1),0),((1,0),0),((1,1),2),((1,2),1)
,((2,-2),0),((2,-1),2),((2,0),0),((2,1),0),((2,2),0)]
以下是此5 x 5网格(棕色土地,蓝色水域)的数据表示形式:
我使用(Int,Int)
作为XY
坐标,因为列表的生成方式(因此顺序)是在作为原点的笛卡尔坐标网格(0,0)
上的螺旋形。剩下的Int
是人口0
的人口,1..9
是水,Map
是土地。
由于我的4
的排序,我一直在努力寻找一种遍历数据并返回[ [(-1 , 2)]
, [(1, 2),(1,1)]
, [(-2, -0),(-1,-1),(-1,-2)]
, [(2, -1)]]
分组土地的方法,这些土地由于彼此之间的相互连接而被分组(包括对角线) ),因此我正在寻找类似波纹管的结果:
rlang
我已经研究并尝试了各种算法,例如BFS,Flood Fill,但是我的输入数据从不符合结构要求,或者我对主题的理解使我无法将其转换为使用坐标。
有没有一种方法可以直接在数据上运行算法,还是应该在寻找另一个方向?
很抱歉,到目前为止,我还没有代码示例,但是我什至无法创建任何对远程有用的东西。
答案 0 :(得分:2)
我建议使用联合查找数据结构。循环所有位置;如果它是陆地,则将其标记为等同于它也是NE的NE,N,NW或W的任何位置。 (当您访问其他土地时,它会自动被标记为与该土地上存在E,SW,S或SE的土地等价。集合D = {NE,N,NW,W}的关键属性是镜像D中的所有方向以获得M,则M∪D包含每个方向;具有此属性的任何其他D集也可以正常工作。)在此过程结束时,数据结构返回的等效类将与您连接土地块。
如果n是职位总数,则此过程为O(n * log n); log n组件来自Map
查找,以确定邻居是陆地还是水域。
如果可以的话,应该考虑使Map
稀疏-仅存储对应于land的键-值对,并跳过水键-逐步到O(m * log m),其中m是土地总数,而不是阵地总数。如果您不能(例如,因为必须记住水位和不存在的位置之间的差异),则可以考虑切换到数组作为后备存储以逐步达到O(n * an),其中a是逆阿克曼函数,因此整个shebang基本上会尽可能地接近O(n),而实际上不必为O(n)。
当同时选择O(m * log m)或O(n * a n)时,O(m * log m)还是O(n * a n)是更可取的,这是对您认为代表典型用例的某些数据集进行经验探索的问题。
答案 1 :(得分:0)
我最终使用了Chris Penner通过FP松弛通道使用的此解决方案,它使用联合查找算法(我在代码中添加了注释以提供一些帮助):
-- | Take Map of land coordinates and return list of grouped land items forming islands
-- | Using Union find algorythm
findIslands :: M.Map Coordinate Coordinate -> IO [[Coordinate]]
findIslands land = do
-- create fresh point map
pointMap <- traverse U.fresh land
-- traverse each point checking for neighbours
void . flip M.traverseWithKey pointMap $ \(x, y) point ->
for_ (catMaybes (flip M.lookup pointMap <$> [(x + 1, y), (x, y + 1),(x +1, y +1), (x - 1, y + 1)]))
$ \neighbourPoint ->
U.union point neighbourPoint
-- traverse ppintMap and representative and their descriptors
withUnionKey :: (M.Map Coordinate Coordinate) <- for pointMap (U.repr >=> U.descriptor)
-- swap cordinates arround
let unionKeyToCoord :: [(Coordinate, Coordinate)] = (swap <$> M.toList withUnionKey)
-- combine coordinates to create islands
results :: M.Map Coordinate [Coordinate] = M.fromListWith (<>) (fmap (:[]) <$> unionKeyToCoord)
-- return just the elements from the Map
return (M.elems results)
convertTolandGrid :: [Coordinate] -> M.Map Coordinate Coordinate
convertTolandGrid = M.fromList . fmap (id &&& id)