简体   繁体   English

找出不在区间列表中的最小数字

[英]Find the smallest number that is not in a list of intervals

I have to find the smallest number that is not in a list of intervals.我必须找到不在区间列表中的最小数字。 For example, i have a list: [(2, 6), (0, 3), (9, 10)].例如,我有一个列表:[(2, 6), (0, 3), (9, 10)]。 I have to find the smallest number by checking all numbers from 0 to 10 until i find the smallest one that is not in any of these ranges.我必须通过检查从 0 到 10 的所有数字来找到最小的数字,直到找到不在这些范围内的最小数字。 So, the number can't be between 2 and 6 (including both), 0 and 3, 9 and 10.因此,数字不能介于 2 和 6(包括两者)、0 和 3、9 和 10 之间。

I've written a function and i can't get the right answer.我写了一个函数,但我无法得到正确的答案。 I always get all first numbers that the program checks, but i just need the last one.我总是得到程序检查的所有第一个数字,但我只需要最后一个。 And i don't understand why it doesn't work with return.我不明白为什么它不适用于回报。

intervals = [(2, 6), (0, 3), (9, 10)]
def smallest_number(intervals):
   i = 0
   while True:
      for first, second in intervals:
         if i in range(first, second + 1):
            i += 1
            print(i)

print(smallest_number(intervals))

I expect the output 7, but i get a loop of 7's or numbers from 1 to 7, depending on where i put print(i).我期望输出 7,但我得到一个循环 7 或数字从 1 到 7,这取决于我把 print(i) 放在哪里。

Here's one approach:这是一种方法:

from itertools import chain
intervals = [(2, 6), (0, 3), (9, 10)]

ranges = chain.from_iterable(range(i, j+1) for i, j in l)
min(set(range(10)).difference(ranges))
# 7

Details细节

The first step creates a set from the ranges contained in the tuples using a generator comprehension and calling range with both elements from each tuple .第一步使用生成器推导式和调用range并使用每个tuple两个元素从tuple包含的范围创建一个set The result is flattened using itertools.chain :使用itertools.chain将结果展平:

ranges = chain.from_iterable(range(i, j+1) for i, j in l)
print(list(ranges))
# {0, 1, 2, 3, 4, 5, 6, 9, 10}

Now we only need to look for the min of the difference between this set and a range up to n现在我们只需要寻找这个集合和一个到nrange之间的差异的min

diff = set(range(10)).difference(ranges)
# {7, 8}

I don't have Python here to check, but it seems your function does not return anything, so print(smallest_number(intervals)) prints None .我这里没有 Python 可以检查,但您的函数似乎没有返回任何内容,因此print(smallest_number(intervals))打印None

def smallest_number(intervals):
   i = 0
   while True:
      for first, second in intervals:
      if i in range(first, second + 1):
          i += 1
      else:
          return i

Now print(smallest_number(intervals)) should work.现在print(smallest_number(intervals))应该可以工作了。

Your code will print all the numbers that are in the intervals, not those that are not, before it ends in an infinite loop.在无限循环结束之前,您的代码将打印所有在间隔中的数字,不是那些不在的数字。 Try this:尝试这个:

def smallest_number(intervals):
    i = 0
    while True:
        for first, second in intervals:
            if i in range(first, second + 1):
                i += 1
                break # break from inner loop
        else:
            break # break from outer loop
    return i # return number

Also, while if i in range(first, second + 1): is okay (and fast) in Python 3, I'd rather suggest using if first <= i <= second:此外,虽然if i in range(first, second + 1):在 Python 3 中没问题(而且速度很快),我宁愿建议使用if first <= i <= second:

Or shorter, using next and any :或者更短,使用nextany

def smallest_number(intervals, max_ = 10):
    return next((i for i in range(max_ + 1)
                 if not any(first <= i <= second for first, second in intervals)),
                None) # None is default if none is found

My solution with protection from case when there is no such minimal number.当没有这样的最小数字时,我的解决方案可以防止案例。

checking = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
intervals = [(2, 6), (0, 3), (9, 10)]
min_number = None

unique = set()
for left, right in intervals:
    unique.update(range(left, right + 1))

subset = set(checking) - unique
if subset:
    min_number = min(subset)

print(min_number)

This might be a good choice if you want a list of permitted numbers, not just the smallest.如果您想要一个允许的数字列表,而不仅仅是最小的,这可能是一个不错的选择。

import itertools


def flatten(list_of_sublists):
    return itertools.chain.from_iterable(list_of_sublists)


def excluded_numbers(intervals):
    excluded_ranges = [range(lower, upper + 1) for lower, upper in intervals]
    excluded_numbers = set(flatten(excluded_ranges))
    return excluded_numbers


def permitted_numbers(intervals):
    excluded = excluded_numbers(intervals)
    max_excluded = max(excluded)
    candidates = set(range(max_excluded + 2))
    return candidates - excluded


def smallest_number(intervals):
    return min(permitted_numbers(intervals))

Sort the intervals by their start (end points if the start points are equal).按起点对间隔进行排序(如果起点相等,则为终点)。 Then start with the first interval and keep expanding the right bound till the next interval's start point is greater than our current coverage end point.然后从第一个区间开始,继续扩大右边界,直到下一个区间的起点大于我们当前的覆盖终点。

Take (2,6),(0,3),(9,10)
Sorting: (0,3),(2,6),(9,10)
current coverage = [0,3]

since 3 < 6 and 2<3, we can increase our right point to 6 so coverage becomes [0,6] but 6 < 9, so 7 is the smallest number which isn't in any of the intervals.由于 3 < 6 和 2<3,我们可以将右点增加到 6,因此覆盖率变为 [0,6] 但 6 < 9,因此 7 是不在任何间隔中的最小数字。 Also you need to check if the start point of the very first interval is > 0. In that case the answer will be 0.您还需要检查第一个间隔的起点是否 > 0。在这种情况下,答案将为 0。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM