从一对列表中找出最小对的列表
给定两个整数列表,生成一个最短的配对列表,确保两个列表中的每个值都被包含在内。每对中的第一个值必须来自第一个列表,第二个值必须来自第二个列表,并且每对中的第一个值必须小于第二个值。
如果两个列表的长度不同,或者在每个列表的相同位置上存在相同的整数,简单的 zip
方法就无法使用。
def gen_min_pairs(uplist, downlist):
for pair in zip(uplist, downlist):
yield pair
这是我目前想到的解决方案:
def gen_min_pairs(uplist, downlist):
up_gen = iter(uplist)
down_gen = iter(downlist)
last_up = None
last_down = None
while True:
next_out = next(up_gen, last_up)
next_down = next(down_gen, last_down)
if (next_up == last_up and
next_down == last_down):
return
while not next_up < next_down:
next_down = next(down_gen, None)
if next_down is None:
return
yield next_up, next_down
last_up = next_up
last_down = next_down
这里还有一个简单的测试程序:
if __name__ == '__main__':
from pprint import pprint
datalist = [
{
'up': [1,7,8],
'down': [6,7,13]
},
{
'up': [1,13,15,16],
'down': [6,7,15]
}
]
for dates in datalist:
min_pairs = [pair for pair in
gen_min_pairs(dates['up'], dates['down'])]
pprint(min_pairs)
这个程序对第一组数据的输出是符合预期的,但对第二组数据就不行了。
预期输出:
[(1, 6), (7, 13), (8, 13)]
[(1, 6), (1, 7), (13, 15)]
实际输出:
[(1, 6), (7, 13), (8, 13)]
[(1, 6), (13, 15)]
我认为这个问题可以在只查看每个列表中的每个元素一次的情况下完成,所以复杂度是 O(len(up) + len(down))
。我觉得这取决于每个列表中独特元素的数量。
补充说明:我应该提到,我们可以期待这些列表是按从小到大的顺序排列的。
补充说明:uplist
和 downlist
只是随便起的名字,换成 A
和 B
可能会更清楚。
另外,这里还有一个更全面的测试程序:
from random import uniform, sample
from pprint import pprint
def random_sorted_sample(maxsize=6, pop=31):
size = int(round(uniform(1,maxsize)))
li = sample(xrange(1,pop), size)
return sorted(li)
if __name__ == '__main__':
A = random_sorted_sample()
B = random_sorted_sample()
min_pairs = list(gen_min_pairs(A, B))
pprint(A)
pprint(B)
pprint(min_pairs)
这个程序生成随机的真实输入,计算输出,并显示所有三个列表。以下是一个正确实现可能产生的结果示例:
[11, 13]
[1, 13, 28]
[(11, 13), (13, 28)]
[5, 15, 24, 25]
[3, 13, 21, 22]
[(5, 13), (15, 21), (15, 22)]
[3, 28]
[4, 6, 15, 16, 30]
[(3, 4), (3, 6), (3, 15), (3, 16), (28, 30)]
[2, 5, 20, 24, 26]
[8, 12, 16, 21, 23, 28]
[(2, 8), (5, 12), (5, 16), (20, 21), (20, 23), (24, 28), (26, 28)]
[3, 4, 5, 6, 7]
[1, 2]
[]
3 个回答
0
虽然这不是一个完整的答案(也就是说没有代码),你有没有试过看看numpy的“where”模块?
1
我想了很多办法来解决这个问题(可以查看编辑历史;-)),但没有一个能完全奏效,或者能在线性时间内完成。过了一段时间我才明白,但我之前遇到过类似的问题,所以我真的很想搞明白这个问题;-)
总之,最后的解决办法是我放弃了直接的方法,开始画图来分析匹配关系。我觉得你的第一个列表其实是在定义一些区间,而你要找的就是那些落在这些区间里的项目:
def intervals(seq):
seq = iter(seq)
current = next(seq)
for s in seq:
yield current,s
current = s
yield s, float("inf")
def gen_min_pairs( fst, snd):
snd = iter(snd)
s = next(snd)
for low, up in intervals(fst):
while True:
# does it fall in the current interval
if low < s <= up:
yield low, s
# try with the next
s = next(snd)
else:
# nothing in this interval, go to the next
break
1
在 Python 2.x 中,zip_longest 被称为 izip_longest。
import itertools
def MinPairs(up,down):
if not (up or down):
return []
up=list(itertools.takewhile(lambda x:x<down[-1],up))
if not up:
return []
down=list(itertools.dropwhile(lambda x:x<up[0],down))
if not down:
return []
for i in range(min(len(up),len(down))):
if up[i]>=down[i]:
up.insert(i,up[i-1])
return tuple(itertools.zip_longest(up,down,fillvalue=(up,down)[len(up)>len(down)][-1]))