我在以下列表中有范围:
ranges = [(1, 50), (49, 70), (75, 85), (84, 88), (87, 92)]
我想找到可以由这些(当它们相互重叠时)构造的最长范围。
预期输出:
[(1, 70), (75, 92)]
我有一个解决方案,但是它太复杂了,我确信必须有一个更简单的解决方案来解决这个问题
我的解决方案:
def overlap(x, y):
return range(max(x[0], y[0]), min(x[-1], y[-1]) + 1)
ranges = [(1, 50), (49, 70), (75, 85), (84, 88), (87, 92)]
beg, end = min([x[0] for x in ranges]), 0
for i in ranges:
if i[0] == beg:
end = i[1]
while beg:
for _ in ranges:
for i in ranges:
if i[1] > end and overlap(i, [beg, end]):
end = i[1]
print(beg, end)
try:
beg = min([x[0] for x in ranges if x[0] > end])
for i in ranges:
if i[0] == beg:
end = i[1]
except ValueError:
beg = None
输出:
1 70
75 92
答案 0 :(得分:9)
我认为您可以按范围的开头对输入进行排序,然后遍历它们。在每个项目上,它要么添加到当前范围(如果开始小于当前范围的结尾),要么我们得出当前范围并开始累积一个新范围:
def overlaps(ranges):
ranges = sorted(ranges) # If our inputs are garunteed sorted, we can skip this
it = iter(ranges)
try:
curr_start, curr_stop = next(it)
# overlaps = False # If we want to exclude output ranges not produced by overlapping input ranges
except StopIteration:
return
for start, stop in it:
if curr_start <= start <= curr_stop: # Assumes intervals are closed
curr_stop = max(curr_stop, stop)
# overlaps = True
else:
# if overlaps:
yield curr_start, curr_stop
curr_start, curr_stop = start, stop
# overlaps = False
# if overlaps:
yield curr_start, curr_stop
print(list(overlaps([(1, 50), (49, 70), (75, 85), (84, 88), (87, 92)])))
# [(1, 70), (75, 92)]
print(list(overlaps([(20, 30), (5, 10), (1, 7), (12, 21)])))
# [(1, 10), (12, 30)]
答案 1 :(得分:6)
您可以使用zip分组每个范围对的所有开始值和结束值。如果起始值低于上一个终止值,则存在重叠,因此请删除该起始值和终止值。我们使用int跟踪每个低和高列表中的哪个索引,我们希望低索引始终比高索引高一个。
#split the numbers in to the low and high part of each range
#and set the index position for each of them
ranges = [(1, 50), (49, 70), (75, 85), (84, 88), (87, 92)]
low, high = [list(nums) for nums in zip(*ranges)]
l, h = 1, 0
#Iterate over the ranges and remove when there is an overlap if no over lap move the pointers
while l < len(low) and h < len(high):
if low[l] < high[h]:
del low[l]
del high[h]
else:
l +=1
h +=1
#zip the low and high back into ranges
new_ranges = list(zip(low, high))
print(new_ranges)
输出
[(1, 70), (75, 92)]
答案 2 :(得分:4)
可以使用functools.reduce
:
from functools import reduce
ranges = [(1, 50), (45, 47), (49, 70), (75, 85), (84, 88), (87, 92)]
reducer = (
lambda acc, el: acc[:-1:] + [(min(*acc[-1], *el), max(*acc[-1], *el))]
if acc[-1][1] > el[0]
else acc + [el]
)
print(reduce(reducer, ranges[1::], [ranges[0]]))
礼物:
[(1, 70), (75, 92)]
很难说出来,但是它使用reduce
来浏览范围。如果范围中的最后一个元组与下一个提供的元组重叠(if acc[-1][1] > el[0]
),它将从两者的(min, max)
创建一个新范围,然后将这个新的组合范围替换为后面的范围({{1 }}),否则只需将新范围添加到末尾(acc[:-1:] + [(min, max)]
)。
编辑:查看其他答案后,进行更新以比较两个范围的最小值/最大值,而不仅仅是第一和最后一个
答案 3 :(得分:2)
您可以使用Counter
包中的collections
容器,然后对使用Counter
获得的itertools
个对象的组合执行设置操作。
类似的东西:
ranges = [(1, 50), (49, 70), (75, 85), (84, 88), (87, 92)]
import collections, itertools
import numpy as np
out = []
for range in ranges:
data = np.arange(range[0], range[1]+1)
out.append(collections.Counter(data))
for x,y in list(itertools.combinations(out, 2)): # combinations of two
if x & y: # if they overlap
print(x | y) # get their union
将使您获得所需的东西:
Counter({1: 1, 2: 1, 3: 1, 4: 1, 5: 1, 6: 1, 7: 1, 8: 1, 9: 1, 10: 1, 11: 1, 12: 1, 13: 1, 14: 1, 15: 1, 16: 1, 17: 1, 18: 1, 19: 1, 20: 1, 21: 1, 22: 1, 23: 1, 24: 1, 25: 1, 26: 1, 27: 1, 28: 1, 29: 1, 30: 1, 31: 1, 32: 1, 33: 1, 34: 1, 35: 1, 36: 1, 37: 1, 38: 1, 39: 1, 40: 1, 41: 1, 42: 1, 43: 1, 44: 1, 45: 1, 46: 1, 47: 1, 48: 1, 49: 1, 50: 1, 51: 1, 52: 1, 53: 1, 54: 1, 55: 1, 56: 1, 57: 1, 58: 1, 59: 1, 60: 1, 61: 1, 62: 1, 63: 1, 64: 1, 65: 1, 66: 1, 67: 1, 68: 1, 69: 1, 70: 1})
Counter({75: 1, 76: 1, 77: 1, 78: 1, 79: 1, 80: 1, 81: 1, 82: 1, 83: 1, 84: 1, 85: 1, 86: 1, 87: 1, 88: 1})
Counter({84: 1, 85: 1, 86: 1, 87: 1, 88: 1, 89: 1, 90: 1, 91: 1, 92: 1})
如果继续对多个图层执行此操作,则将获得所需的超集。您可以找到更多有关Counter
的使用方法的here。
答案 4 :(得分:2)
使用一个集合来消除重复项,并使用一个经过排序的列表进行迭代,下面的方法应该起作用。
ranges = [(1, 50), (49, 70), (75, 85), (84, 88), (87, 92)]
all_nums = sorted(list(set(x for r in ranges for x in range(r[0], r[1]))))
i = all_nums[0]
print(i, end=' ')
while i < all_nums[-1]:
if i not in all_nums:
print(i)
i = all_nums[all_nums.index(i-1) + 1]
print(i, end = ' ')
i += 1
print(i+1)
ranges = [(1, 50), (49, 70), (75, 85), (84, 88), (87, 92)]
1 70
75 92
ranges = [(1, 50), (55, 70), (75, 82), (84, 88), (87, 92)]
1 50
55 70
75 82
84 92
答案 5 :(得分:2)
问题:找到范围内最长的重叠范围
ranges1 = [(1, 50), (49, 70), (75, 85), (84, 88), (87, 92)]
ranges2 = [(1, 50), (40,45), (49, 70)]
def get_overlapping(ranges):
result = []
start = 0
end = ranges[0][1]
for i, node in enumerate(ranges[1:], 1):
if end > node[0]:
if end < node[1]:
end = node[1]
continue
result.append((start, i - 1))
end = node[1]
start = i
else:
result.append((start, i))
return result
用法:
for _range in [ranges1, ranges2]:
result = get_overlapping(_range)
for o in result:
start, end = _range[o[0]], _range[o[1]]
print(start[0], end[1])
print()
输出:
1 70
75 92
1 70
答案 6 :(得分:2)
我建议您仅对范围进行一次迭代,但是将当前正在扩展的范围保留在内存中,如下所示:
Checkbox
哪个输出:
import React from "react";
import ReactDOM from "react-dom";
import { createMuiTheme, ThemeProvider } from "@material-ui/core/styles";
import Checkbox from "@material-ui/core/Checkbox";
const theme = createMuiTheme({
overrides: {
MuiCheckbox: {
colorSecondary: {
color: "green",
"&:hover": {
color: "blue"
},
"&$checked": {
color: "purple",
"&:hover": {
color: "lightblue"
},
"&.Mui-focusVisible": {
color: "red"
}
},
"&.Mui-focusVisible": {
color: "orange"
},
"&.focused:not(.Mui-focusVisible):not($checked)": {
color: "pink"
}
}
}
}
});
function App() {
const [focused, setFocused] = React.useState(false);
return (
<ThemeProvider theme={theme}>
<div className="App">
<Checkbox
className={focused ? "focused" : ""}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
/>
<input value="somewhere to move focus" />
</div>
</ThemeProvider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
不确定我的解决方案是否比您的冗长得多...
答案 7 :(得分:2)
这是一个简单的迭代函数:
def merge_range(rng):
starts, ends = [], []
for i, (x, y) in enumerate(rng):
if i > 0:
if x<= ends[-1]:
ends[-1] = y
continue
starts.append(x)
ends.append(y)
return list(zip(starts, ends))
输出:
merge_range([(1, 50), (49, 70), (75, 85), (84, 88), (87, 92)])
# [(1, 70), (75, 92)]
merge_range([(1, 50), (49, 70), (75, 85), (84, 88), (87, 92), (99, 102), (105, 111), (150, 155), (152, 160), (154, 180)])
# [(1, 70), (75, 92), (99, 102), (105, 111), (150, 180)]
答案 8 :(得分:1)
大多数已发布的答案使用循环。您是否考虑过使用recursive solution:
def merge(ranges):
"""Given a sorted list of range tuples `(a, b)` merge overlapping ranges."""
if not(ranges):
return [];
if len(ranges) == 1:
return ranges;
a, b = ranges[0];
c, d = ranges[1];
# eg.: [(1, 10), (20, 30), rest]
if b < c:
return [(a,b)] + merge(ranges[1:]);
# examples: [(1, 5), (2, 3), rest],
# [(1, 5), (2, 10), rest]
return merge([(a, max(b, d))] + ranges[2:]);
>>> merge([(1, 50), (49, 70), (75, 85), (84, 88), (87, 92)])
[(1, 70), (75, 92)]
>>> merge([(1,10), (2,3), (2,3), (8,12)])
[(1, 12)]
>>> merge (sorted([(2,5),(1,3)], key = lambda x: x[0]))
[(1, 5)]