我正面临着为我的问题找到正确算法的困难时期 我有两个大的列表(或一组)数字,列表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中做到这一点的好方法 谢谢所有
答案 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)