棘手的中位数问题

时间:2011-08-14 20:24:36

标签: python algorithm

给定n个点,选择给定列表中的一个点,使得到这一点的距离总和最小,与所有其他点相比。

以下列方式测量距离。

对于点(x,y),所有8个相邻点的距离为1。

(x+1,y)(x+1,y+1),(x+1,y-1),(x,y+1),(x,y-1),(x-1,y)(x-1,y+1),(x-1,y-1)

修改

更清楚的解释。

函数foo定义为

foo(point_a,point_b) = max(abs(point_a.x - point_b.x),abs(point_a.y - point_b.y))

找到一个点x,使得sum(list_of_points中的y的[foo(x,y)]最小。

实施例

输入:

12 -14
-3 3
-14 7
-14 -3
2 -12
-1 -6

输出

-1 -6

例如: (4,5)和6,7之间的距离是2.

这可以通过检查每对的总和在O(n ^ 2)时间内完成。 有没有更好的算法呢?

2 个答案:

答案 0 :(得分:5)

更新:有时候无法找到最佳状态,我会把它留在这里,直到找到问题为止。

这是O(n):nth是O(n)(预期,而不是最差),迭代列表是O(n)。如果你需要严格的O()然后选择带有排序的中间元素,那么它将是O(n * log(n))。

注意:很容易修改它以返回所有最佳点。

import sys

def nth(sample, n):
    pivot = sample[0]
    below = [s for s in sample if s < pivot]
    above = [s for s in sample if s > pivot]
    i, j = len(below), len(sample)-len(above)
    if n < i:      return nth(below, n)
    elif n >= j:   return nth(above, n-j)
    else:          return pivot

def getbest(li):
    ''' li is a list of tuples (x,y) '''
    l = len(li)
    lix = [x[0] for x in li]
    liy = [x[1] for x in li]

    mid_x1 = nth(lix, l/2) if l%2==1 else nth(lix, l/2-1)
    mid_x2 = nth(lix, l/2)
    mid_y1 = nth(liy, l/2) if l%2==1 else nth(liy, l/2-1)
    mid_y2 = nth(liy, l/2)

    mindist = sys.maxint
    minp = None
    for p in li:
        dist = 0 if mid_x1 <= p[0] <= mid_x2 else min(abs(p[0]-mid_x1), abs(p[0]-mid_x2))
        dist += 0 if mid_y1 <= p[1] <= mid_y2 else min(abs(p[1]-mid_y1), abs(p[1]-mid_y2))
        if dist < mindist:
            minp, mindist = p, dist
    return minp

它基于一维问题的解决方案 - 对于一个数字列表,找到一个总和距离最小的数字。

如果列表中有偶数个元素,则解决方法是(已排序)列表的中间元素或两个中间元素(包括这两个元素)之间的任何数字。

更新:我的nth算法似乎非常慢,可能有更好的方法来重写它,sort优于&lt; sort(lix); sort(liy);。 100000个元素,因此如果您进行速度比较,只需添加def nth(sample, n): return sample[n]

import random
def example(length):
    l = []
    for x in range(length):
        l.append((random.randint(-100, 100), random.randint(-100,100)))
    return l

def bruteforce(li):
    bestsum = sys.maxint
    bestp = None
    for p in li:
        sum = 0
        for p1 in li:
            sum += max(abs(p[0]-p1[0]), abs(p[1]-p1[1]))
        if sum < bestsum:
            bestp, bestsum = p, sum
    return bestp

对于那些想要测试他的解决方案的人,这是我使用的。只需运行一个循环,生成输入并将您的解决方案与bruteforce的输出进行比较。

{{1}}

答案 1 :(得分:1)

我可以想象一个比O(n ^ 2)好的方案,至少在通常的情况下。

从输入点构建quadtree。对于树中的每个节点,计算该节点内各点的数量和平均位置。然后,对于每个点,您可以使用四叉树在不到O(n)的时间内计算到所有其他点的距离。如果你计算从点p到远处四叉树节点v的距离,并且v不与p的45度对角线重叠,那么从p到v中所有点的总距离很容易计算(对于v它们在水平方向上比垂直方向与p分开,它只是v.num_points * |p.x - v.average.x|,类似地使用y坐标,如果v主要是垂直分离的)。如果v与45度对角线中的一个重叠,则递归其组件。

那应该击败O(n ^ 2),至少当你找到一个平衡的四叉树来代表你的分数时。