算法:从列表中选择点

时间:2014-05-01 07:44:26

标签: python algorithm python-2.7

我正面临着为我的问题找到正确算法的困难时期 我有两个大的列表(或一组)数字,列表A和列表B.list A已经排序了实数 并且列表B已经对实数进行了排序,我想创建一个包含列表B中所选点的列表C,从列表B中选择以这种方式工作,从列表中的每个元素(数字)AI必须搜索最接近的数字( abs(A.element-B.element)=最小值)。 我会举一些例子,因为很难解释。 例1:

A= [20.0, 19.6, 19.2]
B= [22.454, 22.224, 21, 20.1, 19.76, 19.72, 19]

C= [20.1, 19.72, 19]

条件:
列表C不能有重复点,例如,如果列表A中的两个元素共享列表B中的相同点(因为它是列表A中两个点的最近点) 然后列表A中的一个点必须从列表B中选择第二个最近点 例2:

A= [20.0, 19.6, 19.2]
B= [22.454, 22.224, 21, 20.3, 19.9, 19, 18]

C= [20.3, 19.9, 19]
# I it must not choose  C= [19.9, 19, 18] because its not an optimum solution

注释: 列表B总是比列表A大4倍以上,所以如果A有10个元素B将至少有40个元素

所以让我快速解释一下,列表C必须排序从列表B中选择的不重复的元素,从列表B中选择的这些点必须与列表A中的每个元素最接近。

我希望我的解释和我的英语足够好:) 请帮我找到一个在python 2.7中做到这一点的好方法 谢谢所有

6 个答案:

答案 0 :(得分:1)

使用bisect模块:

import bisect

def solve(A, B):

    A.reverse()
    B.reverse()
    C, indices = [], []

    for x in A:

        i = bisect.bisect_left(B, x)
        if i == 0:
            C.append(B[0])
            indices.append(0)
        elif i == len(B):
            C.append(B[-1])
            indices.append(len(B)-1)
        else:
            minn = min((i-1, i, i+1), key=lambda y:abs(x-B[y]))
            C.append(B[minn])
            indices.append(minn)
    seen = set()
    for i, x in enumerate(C):
        if x in seen:
            C[i] = B[indices[i]+1]
            seen.add(B[indices[i]+1])
        else:
            seen.add(x)

    C.reverse()
    return C

A= [20.0, 19.6, 19.2]
B= [22.454, 22.224, 21, 20.1, 19.76, 19.72, 19]
assert solve(A, B) == [20.1, 19.72, 19]

A= [20.0, 19.6, 19.2]
B= [22.454, 22.224, 21, 20.3, 19.9, 19, 18]
assert solve(A, B) == [20.3, 19.9, 19]

A = [20,17,14,11]
B = [22,21,20,19,18,17,16]
assert solve(A, B) == [20, 18, 17, 16]

答案 1 :(得分:1)

使用二进制搜索

def binary(arr, val):
    assert len(arr) > 0
    if len(arr) == 1:
        return 0
    l2 = len(arr) // 2
    if abs(arr[l2-1] - val) > abs(arr[l2] - val):
        return l2 + binary(arr[l2:], val)
    else:
        return binary(arr[0:l2], val)

def closest_points(A, B):
    C = []
    for val in A:
        idx = binary(B, val)
        C.append(B[idx])
        B.pop(idx)
    return C

A = [20.0, 19.6, 19.2]
B = [22.454, 22.224, 21, 20.1, 19.76, 19.72, 19]
A.reverse()
B.reverse()
C = closest_points(A, B)
C.reverse()
assert C == [20.1, 19.72, 19]

A = [20.0, 19.6, 19.2]
B = [22.454, 22.224, 21, 20.3, 19.9, 19, 18]
A.reverse()
B.reverse()
C = closest_points(A, B)
C.reverse()
assert C == [20.3, 19.9, 19]

复杂性为len(A)* log(len(B))


实际上它确实适用于您的示例,但它不符合您的要求(就像其他基于二进制搜索的解决方案:()在没有探索复杂性的情况下,属性方法是使用Iterative closest point算法

答案 2 :(得分:0)

这可以被认为是linear programming assignment problem(或最大的二分匹配问题),其中您必须以尽可能低的成本将一些来源分配到目的地。

List A is your list of sources.  
List B is your list of destinations.  
Since list B is larger, you have to create dummy sources in List A to make the problem balanced.
The cost of assigning element i from list A to element j of List B = abs(A[i] - B[j])

现在可以使用Hungarian Algorithm解决几乎O(n ^ 3)的复杂性。

答案 3 :(得分:0)

这是我想到的伪代码:

b := 0
For each element a in array A:
    while b < length(B) and a < B[b]:
        b++
    if b == length(B):
        Push B[-1] into C
    else if b == 0:
        Push B[0] into C
    else:
        if abs(B[b] - a) < abs(B[b - 1] - a):
            Push B[b]
        else:
            Push B[b - 1]
            b++

答案 4 :(得分:0)

这是一个简单的动态编程解决方案, 子状态被定义为dp [i] [j]保持listA的元素的最佳分配的值从0到包括i,此时我们具有从索引0到包括索引j的listB的值。

现在对于每个元素,有两个选项,我们要么将listA [i]与listB [j]匹配,要么我们不要。所以dp关系是

  

dp [i] [j] = min(dp [i-1] [j-1] + abs(listA [i] -listB [j]),dp [i] [j-1]); < / p>

使用memoization以自上而下方式实现此动态编程解决方案的工作Java代码显示here

public class Main {     
    static double[] a = {20.0, 19.6, 19.2};
    static double[] b = {22.454, 22.224, 21, 20.3, 19.9, 19, 18};  // declare the 2 arrays
    static boolean[][] takes; // tells about the dp state.
    static double INF = 1000.0;
    static double[][] dp;         // gives the answer for the above defined dp states

    public static void main(String[] args){
        dp = new double[a.length][b.length];
        takes = new boolean[a.length][b.length];

        for(int i=0; i<dp.length; i++)
            Arrays.fill(dp[i], Double.MAX_VALUE);
        System.out.println(maxMatching(a.length-1, b.length-1));
        printSolution(a.length-1, b.length-1);
    }

    static void printSolution(int i, int j){
        if(i==-1 || j==-1)return;
        if(takes[i][j]){
            System.out.println(a[i] + " " + b[j]);
            printSolution(i-1, j-1);
        }else{
            printSolution(i, j-1);
        }
    }

    static double maxMatching(int i, int j){
        //if there are no numbers left to be matched
        if(i==-1)                        return 0;
        //If there are numbers to match but no number to which we can match
        if(j==-1)                        return INF;
        //If we have already solved this problem 
        if(dp[i][j] != Double.MAX_VALUE) return dp[i][j];

        double val1 = maxMatching(i, j-1); // ith number of listAis not matched with jth number of listB
        double val2 = Math.abs(a[i] - b[j]) + maxMatching(i-1, j-1);
        // When ith number is matched with the jth number

        // take the minimum of the above two.
        if(Double.compare(val1, val2)>0){
            dp[i][j] = val2;
            takes[i][j] = true;
        }else{
            dp[i][j] = val1;
        }
        return dp[i][j];
    }
}

运行时分析:代码在O(NM)时间内工作,其中N是list1的长度,M是list2的长度。这是因为每个子问题都存在O(NM)子问题和O(1)时间。

编辑:此解决方案与其他两个O(nlogn)解决方案相反,将适用于所有测试用例并始终找到正确的答案。我认为甚至不存在保证O(nlogn)解决方案正确解决问题的所有版本。

答案 5 :(得分:0)

我这样做:

A = [20.0, 19.6, 19.2]
B = [22.454, 22.224, 21, 20.3, 19.9, 19, 18]
C = []

def lowDiff(var, B):
    prev, low = (0,0)
    for i in B:
        num = abs(i - var)
        if num < prev:
            low = i

        prev = num

    return low

for i in A:
    C.append(lowDiff(i, [x for x in B if x not in C])])

C.sort(reverse=True)