【发布时间】:2011-10-05 05:03:32
【问题描述】:
我在 Mathematica 中实现了quadtree。我是使用像 Mathematica 这样的函数式编程语言编码的新手,我想知道是否可以通过更好地使用模式来改进它或使其更紧凑。
(我知道我也许可以通过修剪未使用的节点来优化树,并且可能会有更好的数据结构,如 k-d 树用于空间分解。)
此外,我仍然对每次添加新点时都复制整个树/表达式的想法感到不舒服。但我的理解是,将表达式作为一个整体进行操作而不修改部分是函数式编程方式。我将不胜感激有关这方面的任何澄清。
MV
代码
ClearAll[qtMakeNode, qtInsert, insideBox, qtDraw, splitBox, isLeaf, qtbb, qtpt];
(* create a quadtree node *)
qtMakeNode[{{xmin_,ymin_}, {xmax_, ymax_}}] :=
{{}, {}, {}, {}, qtbb[{xmin, ymin}, {xmax, ymax}], {}}
(* is pt inside box? *)
insideBox[pt_, bb_] := If[(pt[[1]] <= bb[[2, 1]]) && (pt[[1]] >= bb[[1, 1]]) &&
(pt[[2]] <= bb[[2, 2]]) && (pt[[2]] >= bb[[1, 2]]),
True, False]
(* split bounding box into 4 children *)
splitBox[{{xmin_,ymin_}, {xmax_, ymax_}}] := {
{{xmin, (ymin+ymax)/2}, {(xmin+xmax)/2, ymax}},
{{xmin, ymin},{(xmin+xmax)/2,(ymin+ymax)/2}},
{{(xmin+xmax)/2, ymin},{xmax, (ymin+ymax)/2}},
{{(xmin+xmax)/2, (ymin+ymax)/2},{xmax, ymax}}
}
(* is node a leaf? *)
isLeaf[qt_] := If[ And @@((# == {})& /@ Join[qt[[1;;4]], {List @@ qt[[6]]}]),True, False]
(*--- insert methods ---*)
(* qtInsert #1 - return input if pt is out of bounds *)
qtInsert[qtree_, pt_] /; !insideBox[pt, List @@ qtree[[5]]]:= qtree
(* qtInsert #2 - if leaf, just add pt to node *)
qtInsert[qtree_, pt_] /; isLeaf[qtree] :=
{qtree[[1]],qtree[[2]],qtree[[3]],qtree[[4]],qtree[[5]], qtpt @@ pt}
(* qtInsert #3 - recursively insert pt *)
qtInsert[qtree_, pt_] :=
Module[{cNodes, currPt},
cNodes = qtree[[1;;4]];
(* child nodes not created? *)
If[And @@ ((# == {})& /@ cNodes),
(* compute child node bounds *)
(* create child nodes with above bounds*)
cNodes = qtMakeNode[#]& /@ splitBox[List @@ qtree[[5]]];
];
(* move curr node pt (if not empty) into child *)
currPt = List @@ qtree[[6]];
If[currPt != {},
cNodes = qtInsert[#, currPt]& /@ cNodes;
];
(* insert new pt into child *)
cNodes = qtInsert[#, pt]& /@ cNodes;
(* return new quadtree *)
{cNodes[[1]],cNodes[[2]], cNodes[[3]], cNodes[[4]], qtree[[5]], {}}
]
(* draw quadtree *)
qtDraw[qt_] := Module[{pts, bboxes},
pts = Cases[qt, _qtpt, Infinity] /. qtpt :> List;
bboxes = Cases[qt, _qtbb, Infinity] /. qtbb :> List;
Graphics[{
EdgeForm[Black],Hue[0.2], Map[Disk[#, 0.01]&, pts],
Hue[0.7],EdgeForm[Red], FaceForm[],(Rectangle @@ #) & /@ bboxes
},
Frame->True
]
]
用法
Clear[qt];
len = 50;
pts = RandomReal[{0, 2}, {len, 2}];
qt = qtMakeNode[{{0.0, 0.0}, {2.0, 2.0}}];
Do[qt = qtInsert[qt, pts[[i]]], {i, 1, len}]
qtDraw[qt]
输出
【问题讨论】:
-
我没有时间玩你的代码,但作为回应,'......我仍然不习惯每次有新点时都复制整个树/表达式的想法已添加...'---您是否正在寻找一种方法来为您的函数添加内存?如果是这样,请搜索“记住其值的函数”。如果不是您要找的东西,请忽略,我将在今天晚些时候使用它。看起来不错。 :)
-
不,我的意思是,当我添加一个点时,不是将节点插入到现有的树中,实际上,我们正在生成一棵全新的树,丢弃旧的。我只是想知道这对内存管理的影响,当这样的表达式真的很大时。很难摆脱 C++ 的思维模式! ;-) 谢谢。
-
如果我理解正确,您希望通过引用传递,因为您不喜欢
qtInsert中的Return行从(可能)巨大的列表中创建一个新的嵌套列表老树和几个新节点。 Mathematica 中有一种方法可以用Attributes[qtInsert] = HoldAll;做这样的事情,但缺点是qtInsert的所有参数都必须是变量,而不是文字值。 -
谢谢@bbtrb - 我需要阅读这个。
标签: wolfram-mathematica quadtree