Reputation: 13
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)]. 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. So, the number can't be between 2 and 6 (including both), 0 and 3, 9 and 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).
Upvotes: 0
Views: 991
Reputation: 1
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. Also you need to check if the start point of the very first interval is > 0. In that case the answer will be 0.
Upvotes: 0
Reputation: 1686
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))
Upvotes: 0
Reputation: 1
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)
Upvotes: 0
Reputation: 88226
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
. The result is flattened using 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
diff = set(range(10)).difference(ranges)
# {7, 8}
Upvotes: 2
Reputation: 82899
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:
Or shorter, using next
and any
:
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
Upvotes: 0
Reputation: 727
I don't have Python here to check, but it seems your function does not return anything, so print(smallest_number(intervals))
prints 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.
Upvotes: 0