Я хотел бы изучить использование дерева kd . По сути, вы строите двоичное дерево, где на каждом уровне вы разделяете плоскость на точку. Вы чередуетесь, разделяете ли вы в направлении x или y.
Если вы используете нижнюю левую координату в качестве ключа, то единственными прямоугольниками, которые могут содержаться в данном узле, являются прямоугольники в правом поддереве.
Редактировать: на самом деле это может работать не совсем так. Вполне возможно, что прямоугольник может содержаться прямоугольником в его левом поддереве.
Во время обеденного перерыва я быстро внедрил дерево kd. Он работает в сопоставимое время с вашей первой функцией, но, кажется, для достижения лучших результатов. Я не проверял его на корректность (хотя я использую тот же код теста и сжатия, который вы используете), но на 100000 случайных прямоугольников (при значениях x, y от (0,0) до (99,99) ) подход дерева kd сжал это до 47539 ритов, в то время как подход отсортированного списка уменьшил его до 68393. Дерево kd было немного медленнее, особенно при меньших входах (для 100 ритов это заняло вдвое больше, для 100 000 это было только на 4% медленнее) ). Вот мой код:
type 'a kdtree = Empty | Node of 'a kdtree * 'a * 'a kdtree
let rect_comp d a b =
if d mod 2 = 0 then
a.x0 < b.x0
else
a.y0 < b.y0
let kd_of_list l =
let rec kdl n = function [] -> Empty
| h::t ->
let right, left = List.partition (rect_comp n h) t in
Node (kdl (n+1) left, h, kdl (n+1) right)
in
kdl 0 l
let rec kd_compress boxes =
let rec compress elt rest = function [] -> elt::rest
| h::t when _test_if_compressable elt h -> let b = _calc_new elt h in compress b rest t
| h::t when h = elt -> compress elt rest t
| h::t -> h::(compress elt rest t)
in
match boxes with Empty -> []
| Node (l, m, r) ->
let left = kd_compress l in
let right = kd_compress r in
(compress m left right)
Существует некоторая очевидная возможность для улучшения (во-первых, я делю дерево kd, используя первый элемент вместо медианного элемента)