棘手的中位数问题

Gre*_*und 11 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)
Run Code Online (Sandbox Code Playgroud)

编辑

更清楚的解释.

函数foo定义为

foo(point_a,point_b) = max(abs(point_a.x - point_b.x),abs(point_a.y - point_b.y))
Run Code Online (Sandbox Code Playgroud)

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

输入:

12 -14
-3 3
-14 7
-14 -3
2 -12
-1 -6
Run Code Online (Sandbox Code Playgroud)

产量

-1 -6
Run Code Online (Sandbox Code Playgroud)

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

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

Kar*_*ath 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
Run Code Online (Sandbox Code Playgroud)

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

对此的解决方案是(已排序)列表的中间元素或两个中间元素(包括这两个元素)之间的任何数字(如果列表中有偶数个元素).

更新:我的nth算法似乎非常慢,可能有更好的方法来重写它,sort优于<100000元素,所以如果你做速度比较,只需添加sort(lix); sort(liy);

def nth(sample, n):
    return sample[n]
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)