如何找到最近的旋转

时间:2014-08-16 08:35:48

标签: algorithm

考虑从[0,T]开始按递增顺序给出的点Y.我们要将这些点视为位于圆周T上。现在考虑点X也来自[0,T)并且也位于圆周T上。

我们说X和Y之间的距离是X中每个点与Y中最近点之间的绝对距离之和,回想两者都被认为是在一个圆圈中。将此距离写为Delta(X,Y)。

我试图找到一种快速确定X旋转的方法,使这个距离尽可能小。

我的代码是用于测试一些数据

#!/usr/bin/python
import random
import numpy as np
from bisect import bisect_left

def simul(rate, T):
    time = np.random.exponential(rate)
    times = [0]
    newtime = times[-1]+time
    while (newtime < T):
        times.append(newtime)
        newtime = newtime+np.random.exponential(rate)
    return times[1:]

对于每个点,我使用此函数来查找其最近的邻居。

def takeClosest(myList, myNumber, T):
    """
    Assumes myList is sorted. Returns closest value to myNumber in a circle of circumference T.

    If two numbers are equally close, return the smallest number.
    """
    pos = bisect_left(myList, myNumber)
    before = myList[pos - 1]
    after = myList[pos%len(myList)]
    if after - myNumber < myNumber - before:
       return after
    else:
       return before

所以两个圆圈之间的距离是:

def circle_dist(timesY, timesX):
    dist = 0
    for t in timesX:
        closest_number = takeClosest(timesY, t, T)
        dist += np.abs(closest_number - t)
    return dist

所以我们只做一些数据

#First make some data

T = 5000

timesX = simul(1, T)
timesY = simul(10, T)

最后通过偏移旋转圆圈时间X我们可以

timesX = [(t + offset)%T for t in timesX] 

在实践中,我的timesX和timesY各有约20,000点。

  

给定timesX和timesY,我怎样才能快速找到(大约)timesX给出的旋转   距离时间的最小距离是什么?

3 个答案:

答案 0 :(得分:2)

单个点和一组点之间沿圆的距离是旋转的分段线性函数。该函数的关键点是集合本身的点(零距离)和集合的相邻点之间的中间点(局部最大距离)。这种函数的线性系数是±1。

此类函数的总和再次是分段线性的,但现在具有二次数临界点。实际上所有这些函数都是相同的,除了沿参数轴移动。和的线性系数是整数。

要找到它的最小值,必须在所有关键点计算其值。

我没有办法大幅减少所需的工作量,但无论如何,1,600,000,000点并不是什么大问题,特别是如果你可以在多个处理器之间分配工作。

要计算两个此类函数的总和,请将求和表示为关键点的序列以及每个临界点左侧和右侧的相关系数。然后在添加系数时合并两个点序列。

答案 1 :(得分:2)

您可以使用扫描线算法解决(原始)问题。诀窍是使用正确的&#34;离散化&#34;。想象一下,将你的圆圈切成两条:

X: x....x....x..........x................x.........x...x
Y: .....x..........x.....x..x.x...........x.............

现在计算score = 5+0++1+1+5+9+6

关键的观察是,如果我们非常轻微地转动X(右说),一些点将会改善,一些点会变得更糟。我们可以将其称为差异&#34;。在上面的例子中,差异将是1 - 1 - 1 + 1 + 1 - 1 + 1,因为第一个点与其右边的某个东西相匹配,第二个点与它下面的东西或左边的东西相匹配等。

当然,随着我们更多地移动X,差异将会改变。然而,只有匹配变化的次数不会超过|X||Y|,但可能会更少。

因此,所提出的算法用于计算下一个差异变化的初始分数和时间(X位置)。转到下一个位置并再次计算得分。继续,直到你到达起始位置。

答案 2 :(得分:0)

这可能是iterative closest point(ICP)算法的一个很好的例子:

它重复匹配每个点与其最近邻居并移动所有点,使得均方距离最小化。 (请注意,这相当于最小化平方距离的。)

import pylab as pl

T = 10.0
X = pl.array([3, 5.5, 6])
Y = pl.array([1, 1.5, 2, 4])

pl.clf()
pl.subplot(1, 2, 1, polar=True)
pl.plot(X / T * 2 * pl.pi, pl.ones(X.shape), 'r.', ms=10, mew=3)
pl.plot(Y / T * 2 * pl.pi, pl.ones(Y.shape), 'b+', ms=10, mew=3)

circDist = lambda X, Y: (Y - X + T / 2) % T - T / 2

while True:
    D = circDist(pl.reshape(X, (-1, 1)), pl.reshape(Y, (1, -1)))
    closestY = pl.argmin(D**2, axis = 1)
    distance = circDist(X, Y[closestY])
    shift = pl.mean(distance)
    if pl.absolute(shift) < 1e-3:
        break
    X = (X + shift) % T

pl.subplot(1, 2, 2, polar=True)
pl.plot(X / T * 2 * pl.pi, pl.ones(X.shape), 'r.', ms=10, mew=3)
pl.plot(Y / T * 2 * pl.pi, pl.ones(Y.shape), 'b+', ms=10, mew=3)

建议的解决方案的重要特性是:

  • ICP是一种迭代算法。因此,它取决于初始近似解。此外,它不会总是收敛到全球最优。这主要取决于您的数据和初始解决方案。如果有疑问,请尝试使用不同的起始配置评估ICP,并选择最常见的结果。
  • 当前实现执行定向匹配:它会查找Y中与X中每个点相对的最近点。交换XY时,它可能会产生不同的匹配。
  • 计算X中的点与Y中的点之间的所有成对距离对于大点云(例如20,000点,如您所示)可能是难以处理的。因此,行D = circDist(...)可能会被更有效的方法取代,例如没有评估所有可能的对。
  • 所有点都有助于最终轮换。如果有任何异常值,它们可能会显着扭曲shift。这可以通过median这样的强大平均值来解决,也可以通过排除大distance的点来克服。