【问题标题】:Partition neighbor points given a euclidean distance range在给定欧式距离范围的情况下划分相邻点
【发布时间】:2011-05-20 16:36:14
【问题描述】:

给定两个点 P、Q 和一个 delta,我定义了等价关系 ~=,其中 P ~= Q if EuclideanDistance(P,Q) S 个 n 个点,在示例中 S = (A, B, C, D, E, F) 和 n = 6(事实点实际上段的端点可以忽略不计),是否有一种算法在平均情况下的复杂度优于 O(n^2) 来找到集合的一个分区(子集的代表元素不重要)?

到目前为止,试图找到这个问题的理论定义是不成功的:k-means clustering、最近邻搜索和其他在我看来是不同的问题。图片显示了我需要在我的应用程序中执行的操作。

有什么提示吗?谢谢

编辑:虽然实际问题(给定某种不变量的点附近的集群)在平均情况下应该比 O(n^2) 更好地解决,但我的问题有一个严重的缺陷问题定义:=~ 不是等价关系,因为它不尊重传递性这一简单事实。我认为这是这个问题不容易解决并且需要先进技术的主要原因。将很快发布我的实际解决方案:当近点都满足 =~ 定义时应该工作。当两极分开的点不尊重这种关系但它们与聚集点的重心有关时,可能会失败。它适用于我的输入数据空间,可能不适用于您的。有谁知道这个问题的完整正式解决方案(有解决方案)?

【问题讨论】:

  • 会不会有冲突,即AFdelta 内是否有点GH
  • 示例中没有,但可能有。在某些条件下,建议的解决方案应该有效。不幸的是,这个问题比我最初想象的要复杂得多。本质上,我需要对 near 点进行聚类/分类/索引。如何正式写一个概括非形式概念“近点”的条件?不容易做到,除非做到这一点,否则我想不出更好的解决方案,即使知道了,我也没有时间解决这个问题。同时,您可能会喜欢此页面中的免费代码。 :)

标签: algorithm cluster-analysis partitioning spatial euclidean-distance


【解决方案1】:

重述问题的一种方法如下:给定一组n 2D 点,对于每个点p,找到以p 为中心的直径为delta 的圆所包含的一组点.

一个简单的线性搜索给出了你提到的O(n^2)算法。

在我看来这是最好的在最坏的情况下。当集合中的所有点都包含在直径 delta 的圆内时,每个 n 查询都必须返回 O(n) 点,从而给出 O(n^2) 的整体复杂性。

但是,人们应该能够在更合理的数据集上做得更好。 看看this(特别是关于空间分区的部分)和KD-trees。在合理的情况下,后者应该给你一个 sub-O(n^2) 算法。

可能有一种不同的方式来看待问题,一种更复杂的方式;我无法想到任何事情。

【讨论】:

  • 嗯...好吧,也许我明白了。 1)我需要用适当的启发式构造一个 kd-tree 2)我需要遍历树寻找邻居。您确定在平均情况下,遍历 kd-tree 我将能够在 O(logN) 的增量中获得 all 一个点的邻居吗?谢谢
  • 我在实现 KdTree 方面取得了进展。完成后会将您的答案设置为解决方案。同时我喜欢你的回答:)
  • 虽然我编写了一个适用于我的案例的解决方案,但我发现我对问题的形式化是错误的(=~ 不是不是等价关系)。即使使用我以前的幼稚算法也是如此。但是,现在我在平均情况下有 O(NlogN) 算法,好多了:)。感谢您的提示。
【解决方案2】:

Quadtree 绝对是个问题。

您也可以尝试对每个坐标进行排序并使用这两个列表(排序为n*log(n),并且您可以只检查满足dx <= delta && dy <= delta 的点。此外,您可以将它们放在一个有两个级别的排序列表中指针数量:一个用于解析 OX,另一个用于 OY。

【讨论】:

  • 四叉树似乎用于空间索引(如果我做对了,应该是我问题的理论定义)。我之前尝试过第二种方法,但没有成功。在这两种情况下,我似乎都缺乏知识背景和/或伪代码来自己在现实世界的应用程序中编写 100% 工作解决方案。现在将按照 aix 的建议尝试 kd-tree 和 nns,无论如何,谢谢! :)
【解决方案3】:

对于每个点,计算到原点的距离 D(n),这是一个 O(n) 操作。

使用 O(n^2) 算法查找匹配项,其中 D(a-b) 跳过 D(a)-D(b) > delta。

平均而言,由于跳过了(希望很大的)数字,结果必须优于 O(n^2)。

【讨论】:

  • 我认为这是一个 Θ(n^2) 算法(Theta)。我的意思是这将始终有 n^2 次迭代,只节省一小部分恒定时间。还是我弄错了?无论如何,我很快就会推出 C# kd-tree 实现。
  • 好吧,试试看。我敢打赌,将会有 许多 被淘汰,从而显着减少 O(n^2) 分量
【解决方案4】:

这是一个 C# KdTree 实现,应该解决“在 delta 内查找点 P 的所有邻居”。它大量使用函数式编程技术(是的,我喜欢 Python)。它已经经过测试,但我对理解_TreeFindNearest() 仍有疑问。解决问题的代码(或伪代码)在另一个答案中发布了“在平均情况下,将一组 n 点给定一个 ~= 关系比 O(n^2) 更好”。

/*
Stripped C# 2.0 port of ``kdtree'', a library for working with kd-trees.
Copyright (C) 2007-2009 John Tsiombikas <nuclear@siggraph.org>
Copyright (C) 2010 Francesco Pretto <ceztko@gmail.com>

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice,
   this list of conditions and the following disclaimer in the documentation
   and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
   derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
OF SUCH DAMAGE.
*/

using System;
using System.Collections.Generic;
using System.Text;

namespace ITR.Data.NET
{
    public class KdTree<T>
    {
        #region Fields

        private Node _Root;
        private int _Count;
        private int _Dimension;
        private CoordinateGetter<T>[] _GetCoordinate;

        #endregion // Fields

        #region Constructors

        public KdTree(params CoordinateGetter<T>[] coordinateGetters)
        {
            _Dimension = coordinateGetters.Length;
            _GetCoordinate = coordinateGetters;
        }

        #endregion // Constructors

        #region Public methods

        public void Insert(T location)
        {
            _TreeInsert(ref _Root, 0, location);
            _Count++;
        }

        public void InsertAll(IEnumerable<T> locations)
        {
            foreach (T location in locations)
                Insert(location);
        }

        public IEnumerable<T> FindNeighborsRange(T location, double range)
        {
            return _TreeFindNeighborsRange(_Root, 0, location, range);
        }

        #endregion // Public methods

        #region Tree traversal

        private void _TreeInsert(ref Node current, int currentPlane, T location)
        {
            if (current == null)
            {
                current = new Node(location);
                return;
            }

            int nextPlane = (currentPlane + 1) % _Dimension;

            if (_GetCoordinate[currentPlane](location) <
                    _GetCoordinate[currentPlane](current.Location))
                _TreeInsert(ref current._Left, nextPlane, location);
            else
                _TreeInsert(ref current._Right, nextPlane, location);
        }

        private IEnumerable<T> _TreeFindNeighborsRange(Node current, int currentPlane,
            T referenceLocation, double range)
        {
            if (current == null)
                yield break;

            double squaredDistance = 0;
            for (int it = 0; it < _Dimension; it++)
            {
                double referenceCoordinate = _GetCoordinate[it](referenceLocation);
                double currentCoordinate = _GetCoordinate[it](current.Location);
                squaredDistance +=
                    (referenceCoordinate - currentCoordinate)
                    * (referenceCoordinate - currentCoordinate);
            }

            if (squaredDistance <= range * range)
                yield return current.Location;

            double coordinateRelativeDistance =
                _GetCoordinate[currentPlane](referenceLocation)
                    - _GetCoordinate[currentPlane](current.Location);
            Direction nextDirection = coordinateRelativeDistance <= 0.0
                ? Direction.LEFT : Direction.RIGHT;
            int nextPlane = (currentPlane + 1) % _Dimension;
            IEnumerable<T> subTreeNeighbors =
                _TreeFindNeighborsRange(current[nextDirection], nextPlane,
                    referenceLocation, range);
            foreach (T location in subTreeNeighbors)
                yield return location;

            if (Math.Abs(coordinateRelativeDistance) <= range)
            {
                subTreeNeighbors =
                    _TreeFindNeighborsRange(current.GetOtherChild(nextDirection),
                        nextPlane, referenceLocation, range);
                foreach (T location in subTreeNeighbors)
                    yield return location;
            }
        }

        #endregion // Tree traversal

        #region Node class

        public class Node
        {
            #region Fields

            private T _Location;
            internal Node _Left;
            internal Node _Right;

            #endregion // Fields

            #region Constructors

            internal Node(T nodeValue)
            {
                _Location = nodeValue;
                _Left = null;
                _Right = null;
            }

            #endregion // Contructors

            #region Children Indexers

            public Node this[Direction direction]
            {
                get { return direction == Direction.LEFT ? _Left : Right; }
            }

            public Node GetOtherChild(Direction direction)
            {
                return direction == Direction.LEFT ? _Right : _Left;
            }

            #endregion // Children Indexers

            #region Properties

            public T Location
            {
                get { return _Location; }
            }

            public Node Left
            {
                get { return _Left; }
            }

            public Node Right
            {
                get { return _Right; }
            }

            #endregion // Properties
        }

        #endregion // Node class

        #region Properties

        public int Count
        {
            get { return _Count; }
            set { _Count = value; }
        }

        public Node Root
        {
            get { return _Root; }
            set { _Root = value; }
        }

        #endregion // Properties
    }

    #region Enums, delegates

    public enum Direction
    {
        LEFT = 0,
        RIGHT
    }

    public delegate double CoordinateGetter<T>(T location);

    #endregion // Enums, delegates
}

【讨论】:

    【解决方案5】:

    以下 C# 方法与 KdTree 类、Join()(枚举作为参数传递的所有集合)和 Shuffled()(返回传递集合的打乱版本)方法一起解决了我的问题。当referenceVectorsvectorsToRelocate 相同的向量时,可能会有一些有缺陷的情况(阅读问题中的编辑),就像我在我的问题中所做的那样。

    public static Dictionary<Vector2D, Vector2D> FindRelocationMap(
        IEnumerable<Vector2D> referenceVectors,
        IEnumerable<Vector2D> vectorsToRelocate)
    {
        Dictionary<Vector2D, Vector2D> ret = new Dictionary<Vector2D, Vector2D>();
    
        // Preliminary filling
        IEnumerable<Vector2D> allVectors =
            Utils.Join(referenceVectors, vectorsToRelocate);
        foreach (Vector2D vector in allVectors)
            ret[vector] = vector;
    
        KdTree<Vector2D> kdTree = new KdTree<Vector2D>(
            delegate(Vector2D vector) { return vector.X; },
            delegate(Vector2D vector) { return vector.Y; });
        kdTree.InsertAll(Utils.Shuffled(ret.Keys));
    
        HashSet<Vector2D> relocatedVectors = new HashSet<Vector2D>();
        foreach (Vector2D vector in referenceVectors)
        {
            if (relocatedVectors.Contains(vector))
                continue;
    
            relocatedVectors.Add(vector);
    
            IEnumerable<Vector2D> neighbors =
                kdTree.FindNeighborsRange(vector, Tolerances.EUCLID_DIST_TOLERANCE);
    
            foreach (Vector2D neighbor in neighbors)
            {
                ret[neighbor] = vector;
                relocatedVectors.Add(neighbor);
            }
        }
    
        return ret;
    }
    

    【讨论】:

      猜你喜欢
      • 2021-07-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-11-12
      • 2011-05-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多