Python 从两个排序数组中取前10个元素的最小和组合
谷歌没有设法说出这个问题的名称,希望这个问题能对社区有所贡献 假设我们有两个排序的数字数组,如:Python 从两个排序数组中取前10个元素的最小和组合,python,algorithm,sorting,iteration,cartesian-product,Python,Algorithm,Sorting,Iteration,Cartesian Product,谷歌没有设法说出这个问题的名称,希望这个问题能对社区有所贡献 假设我们有两个排序的数字数组,如: 2 8 12 18 45 35 85 48 87 49 97 59 我们希望有效地从两个数组中提取前k 10个最小的数和组合。在我们的情况下,这将是: 2 + 8 = 10 2 + 18 = 20 12 + 8 = 20 12 + 18 = 30 2 + 35 = 37 12 + 35 = 47 2 + 48 = 50 2 + 49 = 51 45 + 8 = 53 12 +
2 8
12 18
45 35
85 48
87 49
97 59
我们希望有效地从两个数组中提取前k 10个最小的数和组合。在我们的情况下,这将是:
2 + 8 = 10
2 + 18 = 20
12 + 8 = 20
12 + 18 = 30
2 + 35 = 37
12 + 35 = 47
2 + 48 = 50
2 + 49 = 51
45 + 8 = 53
12 + 48 = 60
正确的方法是什么?我设计了一个由@sanyash改进的简单实现,但它没有利用数组被排序的事实,而且问题在线性时间内是可行的
def最小_产品K,arr1,arr2:
product_iter=itertools.product
itertools.islicearr1,k,
itertools.islicearr2,k,
产品分类=分类产品分类,关键字=总和
product_sliced=itertools.isliceproduct_sorted,k;
返回列表产品\u切片
打印产品10,
[ 2, 12, 45, 85, 87, 98],
[ 8, 18, 35, 48, 49, 59]
类似的问题:但它处理的是创建一个完整的结果数组,而在我的例子中,我只需要前几个值
另外,我添加了python标记,因为这是一个数学问题,但我很乐意使用任何语言的解决方案,或者只是一个解释,或者一个到wikipedia的链接…首先将两个数组截断为lenk。然后使用以前的实现。没关系^2困难:
import itertools
def smallest_product(k, arr1, arr2):
product_iter = itertools.product(
itertools.islice(arr1, k),
itertools.islice(arr2, k),
)
product_sorted = sorted(product_iter, key=sum)[:k]
return list(product_sorted)
print(smallest_product(
10,
[ 2, 12, 45, 85, 87, 98],
[ 8, 18, 35, 48, 49, 59])
)
首先将两个数组截断为lenk。然后使用以前的实现。没关系^2困难:
import itertools
def smallest_product(k, arr1, arr2):
product_iter = itertools.product(
itertools.islice(arr1, k),
itertools.islice(arr2, k),
)
product_sorted = sorted(product_iter, key=sum)[:k]
return list(product_sorted)
print(smallest_product(
10,
[ 2, 12, 45, 85, 87, 98],
[ 8, 18, 35, 48, 49, 59])
)
假设我们使用两个数组创建一个表:
for arr in [[i + j for j in arr2] for i in arr1]: print(arr)
我们得到如下输出:
[10, 20, 37, 50, 51, 61]
[20, 30, 47, 60, 61, 71]
[53, 63, 80, 93, 94, 104]
[93, 103, 120, 133, 134, 144]
[95, 105, 122, 135, 136, 146]
[106, 116, 133, 146, 147, 157]
注意,在该矩阵中,矩阵[i][j]==arr1[i]+arr2[j]。所以我们可以计算O1中矩阵任意位置的元素的值。请注意,这是一个排序矩阵,其中所有行和列都是单调递增的,我们试图找到其中的k个最小元素
在这个阶段,OKlogN堆方法变得相当简单。取第一行并将其转换为最小堆。每次弹出最小的元素并将其添加到结果中。每次弹出时,都会将下一行对应列中的元素添加到堆中。重复k次,得到最小的k和
这与实际情况并不完全相关,但是确实存在鞍形搜索的变体,可以让您在ON中而不是像上面的方法那样在OKlogN中找到排序矩阵中的第k个最小元素。可能有一种方法可以将所采用的方法修改为OK,但在这种情况下很可能是矫枉过正 假设我们使用两个数组创建一个表:
for arr in [[i + j for j in arr2] for i in arr1]: print(arr)
我们得到如下输出:
[10, 20, 37, 50, 51, 61]
[20, 30, 47, 60, 61, 71]
[53, 63, 80, 93, 94, 104]
[93, 103, 120, 133, 134, 144]
[95, 105, 122, 135, 136, 146]
[106, 116, 133, 146, 147, 157]
注意,在该矩阵中,矩阵[i][j]==arr1[i]+arr2[j]。所以我们可以计算O1中矩阵任意位置的元素的值。请注意,这是一个排序矩阵,其中所有行和列都是单调递增的,我们试图找到其中的k个最小元素
在这个阶段,OKlogN堆方法变得相当简单。取第一行并将其转换为最小堆。每次弹出最小的元素并将其添加到结果中。每次弹出时,都会将下一行对应列中的元素添加到堆中。重复k次,得到最小的k和
这与实际情况并不完全相关,但是确实存在鞍形搜索的变体,可以让您在ON中而不是像上面的方法那样在OKlogN中找到排序矩阵中的第k个最小元素。可能有一种方法可以将所采用的方法修改为OK,但在这种情况下很可能是矫枉过正 您可以使用堆:
import heapq
def smallest_product(k, a, b):
k = min(k, len(a) * len(b))
l = [(a[0] + b[0], 0, 0)]
heapq.heapify(l)
seen = set()
for _ in range(k):
s, i, j = heapq.heappop(l)
if i + 1 < len(a) and (i + 1, j) not in seen:
heapq.heappush(l, (a[i + 1] + b[j], i + 1, j))
seen.add((i + 1, j))
if j + 1 < len(b) and (i, j + 1) not in seen:
heapq.heappush(l, (a[i] + b[j + 1], i, j + 1))
seen.add((i, j + 1))
yield (a[i], b[j])
result = list(smallest_product(10, [ 2, 12, 45, 85, 87, 98], [ 8, 18, 35, 48, 49, 59]))
print(result)
上面的代码是中代码的Python翻译。该方法的时间复杂度为Ok*logk
k=11时的输出
您可以使用堆:
import heapq
def smallest_product(k, a, b):
k = min(k, len(a) * len(b))
l = [(a[0] + b[0], 0, 0)]
heapq.heapify(l)
seen = set()
for _ in range(k):
s, i, j = heapq.heappop(l)
if i + 1 < len(a) and (i + 1, j) not in seen:
heapq.heappush(l, (a[i + 1] + b[j], i + 1, j))
seen.add((i + 1, j))
if j + 1 < len(b) and (i, j + 1) not in seen:
heapq.heappush(l, (a[i] + b[j + 1], i, j + 1))
seen.add((i, j + 1))
yield (a[i], b[j])
result = list(smallest_product(10, [ 2, 12, 45, 85, 87, 98], [ 8, 18, 35, 48, 49, 59]))
print(result)
上面的代码是中代码的Python翻译。该方法的时间复杂度为Ok*logk
k=11时的输出
这个问题可以分三步解决 构造一个长度为m的已排序对的iterables列表,其中m=minlenlist1,k; 对iterables应用see了解更多信息,以获得单个iterable的已排序对,使用每对的总和作为键; 从iterable中获取前k个元素。 m路合并有不同的算法。下面是一个基于堆的实现。复杂性是Ok*logm
from itertools import islice
from heapq import merge
def smallest_pairs(k, list1, list2):
pairs = map(lambda x:((x, y) for y in list2), islice(list1, k))
return list(islice(merge(*pairs, key=sum), k))
print(smallest_pairs(10,
[ 2, 12, 45, 85, 87, 98],
[ 8, 18, 35, 48, 49, 59]))
这个问题可以分三步解决 构造一个长度为m的已排序对的iterables列表,其中m=minlenlist1,k; 对iterables应用see了解更多信息,以获得单个iterable的已排序对,使用每对的总和作为键; 从iterable中获取前k个元素。 m路合并有不同的算法。下面是一个基于堆的实现。复杂性是Ok*logm
from itertools import islice
from heapq import merge
def smallest_pairs(k, list1, list2):
pairs = map(lambda x:((x, y) for y in list2), islice(list1, k))
return list(islice(merge(*pairs, key=sum), k))
print(smallest_pairs(10,
[ 2, 12, 45, 85, 87, 98],
[ 8, 18, 35, 48, 49, 59]))
谢谢,这肯定是一次升级,不过我想知道是否有比Ok ^2更好的方法。。。我会更新我的问题。谢谢,这肯定会是一个升级,不过我想知道是否
下面是一种比Ok ^2更好的方法。。。我将更新我的问题。可能重复的可能重复的感谢您的解决方案!我无法从一瞥中了解原因,但出于某种原因,对于大于10的k值(如11),它会失败。索引器:列表索引超出范围heapq.heappush,a[i]+b[j+1],i,j+1@ArturKlesun现在看。谢谢你的解决方案!我无法从一瞥中了解原因,但出于某种原因,对于大于10的k值(如11),它会失败。索引器:列表索引超出范围heapq.heappush,a[i]+b[j+1],i,j+1@ArturKlesun现在看。