From 9caccda16491253668c7a928cf7e7d631c3a6424 Mon Sep 17 00:00:00 2001 From: Andreas Kloeckner <inform@tiker.net> Date: Sun, 5 Jun 2016 19:48:28 -0500 Subject: [PATCH] Spatial binary tree: prevent infinite recursion by probabilistic retention --- pytools/spatial_btree.py | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/pytools/spatial_btree.py b/pytools/spatial_btree.py index 00adc20..dba6ec8 100644 --- a/pytools/spatial_btree.py +++ b/pytools/spatial_btree.py @@ -85,9 +85,22 @@ class SpatialBinaryTreeBucket: """ def insert_into_subdivision(element, bbox): - for bucket in self.all_buckets: - if do_boxes_intersect((bucket.bottom_left, bucket.top_right), bbox): - bucket.insert(element, bbox) + bucket_matches = [ + ibucket + for ibucket, bucket in enumerate(self.all_buckets) + if do_boxes_intersect((bucket.bottom_left, bucket.top_right), bbox)] + + from random import uniform + if len(bucket_matches) > len(self.all_buckets) // 2: + # Would go into more than half of all buckets--keep it here + self.elements.append((element, bbox)) + elif len(bucket_matches) > 1 and uniform(0, 1) > 0.95: + # Would go into more than one bucket and therefore may recurse + # indefinitely. Keep it here with a low probability. + self.elements.append((element, bbox)) + else: + for ibucket_match in bucket_matches: + self.all_buckets[ibucket_match].insert(element, bbox) if self.buckets is None: # No subdivisions yet. @@ -99,14 +112,13 @@ class SpatialBinaryTreeBucket: self.all_buckets, max_elements_per_box=self.max_elements_per_box) + old_elements = self.elements + self.elements = [] + # Move all elements from the full bucket into the new finer ones - for el, el_bbox in self.elements: + for el, el_bbox in old_elements: insert_into_subdivision(el, el_bbox) - # Free up some memory. Elements are now stored in the - # subdivision, so we don't need them here any more. - del self.elements - insert_into_subdivision(element, bbox) else: # Simple: @@ -128,10 +140,10 @@ class SpatialBinaryTreeBucket: for result in bucket.generate_matches(point): yield result - else: - # We don't. Perform linear search. - for el, bbox in self.elements: - yield el + + # Perform linear search. + for el, bbox in self.elements: + yield el def visualize(self, file): file.write("%f %f\n" % (self.bottom_left[0], self.bottom_left[1])) -- GitLab