Проверка значения в наборе диапазонов
Нет необходимости создавать и повторять списки. Я подозреваю, что было бы более эффективно просто использовать операторы сравнения.
Однако, если вы хотите подтвердить, какой подход наиболее эффективен, используйте инструменты профилирования .
Вот пример профилирования нескольких подходов к определению, находится ли значение в наборе диапазонов, чтобы проверить, какой подход наиболее эффективен.
import cProfile
# Input data.
ranges = [[1, 5], [11, 14], [15, 19], [23, 30]]
values = [4, 14, 40]
# An implementation using a comparison operator (i.e. "<=").
def is_in_range_comparison_operator(v):
for r in ranges:
if r[0] <= v <= r[1]:
return True
return False
# An implementation using a range object.
def is_in_range_range_object(v):
for r in ranges:
if v in range(r[0], r[1]+1):
return True
return False
# An implementation using precomputed range objects.
range_objects = [range(r[0], r[1]+1) for r in ranges]
def is_in_range_precomputed_range_objects(v):
for r in range_objects:
if v in r:
return True
return False
# A list of the implementations, to make looping through them easier.
implementations = [
is_in_range_comparison_operator,
is_in_range_range_object,
is_in_range_precomputed_range_objects,
]
# A function to execute an implementation and print output.
def is_in_range(is_in_range_func):
print("Using {}:".format(is_in_range_func.func_name))
for v in values:
if is_in_range_func(v):
print ("True")
else:
print ("False")
print
# Run each implementation, printing out the results.
for f in implementations:
is_in_range(f)
# A function for executing a implementation repeatedly, for profiling purposes.
def test_is_in_range(is_in_range_func, num_iterations):
for _ in range(num_iterations):
for v in values:
if is_in_range_func(v):
pass
# Profile each implementation by running it num_iterations times.
num_iterations = 100000
for f in implementations:
command = "test_is_in_range({}, {})".format(
f.func_name, num_iterations)
print("Profiling the following command: {}".format(command))
cProfile.run(command)
А вот результат выполнения сценария:
$ python in_range.py
Using is_in_range_comparison_operator:
True
True
False
Using is_in_range_range_object:
True
True
False
Using is_in_range_precomputed_range_objects:
True
True
False
Profiling the following command: test_is_in_range(is_in_range_comparison_operator, 100000)
300004 function calls in 0.388 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.388 0.388 <string>:1(<module>)
1 0.172 0.172 0.388 0.388 in_range.py:51(test_is_in_range)
300000 0.212 0.000 0.212 0.000 in_range.py:8(is_in_range_comparison_operator)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.004 0.004 0.004 0.004 {range}
Profiling the following command: test_is_in_range(is_in_range_range_object, 100000)
1000004 function calls in 1.209 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 1.209 1.209 <string>:1(<module>)
300000 0.639 0.000 1.033 0.000 in_range.py:15(is_in_range_range_object)
1 0.174 0.174 1.209 1.209 in_range.py:51(test_is_in_range)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
700001 0.396 0.000 0.396 0.000 {range}
Profiling the following command: test_is_in_range(is_in_range_precomputed_range_objects, 100000)
300004 function calls in 0.391 seconds
Ordered by: standard name
ncalls tottime percall cumtime percall filename:lineno(function)
1 0.000 0.000 0.391 0.391 <string>:1(<module>)
300000 0.220 0.000 0.220 0.000 in_range.py:23(is_in_range_precomputed_range_objects)
1 0.171 0.171 0.391 0.391 in_range.py:51(test_is_in_range)
1 0.000 0.000 0.000 0.000 {method 'disable' of '_lsprof.Profiler' objects}
1 0.001 0.001 0.001 0.001 {range}
Некоторые выводы из результатов профилирования:
- Использование операторов сравнения более эффективно, чем повторное создание экземпляров объектов диапазона.
- Использование предварительно вычисленных объектов диапазонов наравне с использованием операторов сравнения.
Однако, если у вас есть огромное количество диапазонов для обработки, это, вероятно, в конечном итоге будет более эффективным, чтобы отказаться отиспользуя объекты диапазона. Что приводит к ...
Эффективная обработка произвольно большого набора диапазонов
Если входной набор диапазонов достаточно велик, что вас беспокоитисчерпание памяти, вот подход, который обрабатывает каждый диапазон по одному, проверяя все значения с этим диапазоном.
Примечания:
- Подход обрабатывает каждый диапазон по одному,тестирование всех
значений против него. Это позволяет обрабатывать произвольное количество диапазонов без необходимости загружать все начальные значения. - Это не учитывает произвольное количество значений для поиска.
- Есть некоторые оптимизациина основе как входных диапазонов, так и значений
, которые сортируются. Если на отсортированный ввод нельзя положиться, оптимизация
может быть изменена или удалена соответственно.
ranges = [[4, 5], [11, 14], [15, 19], [23, 30]]
values = [4, 14, 20]
# Create a dictionary to keep track whether or not each value falls within
# a range, with a default value of False.
is_value_in_range = {}
def init_results():
global is_value_in_range
is_value_in_range = {v: False for v in values}
def print_values_and_results():
for v in values:
print("{}: {}".format(v, is_value_in_range[v]))
def check_for_values_in_range(r, values_index):
for i, v in enumerate(values[values_index:]):
# If the value is greater than the upper end of the range, move on
# to the next range.
if v > r[1]:
return (values_index + i, True)
# If the value is less than the lower end and we're on the last
# value, stop altogether.
if v < r[0] and values_index == len(values) - 1:
return (0, False)
if r[0] <= v <= r[1]:
is_value_in_range[v] = True
return (values_index, True)
def check_for_values_in_ranges(verbose=False):
init_results()
if verbose:
print("Initial results:")
print_values_and_results()
print
i = 0
for r in ranges:
i, continue_searching = check_for_values_in_range(r, i)
if verbose:
print("After checking range: {}".format(r))
print_values_and_results()
print
if not continue_searching:
break
print("Final results:")
print_values_and_results()
print
print("*** Check ranges for values (non-verbose) ***")
check_for_values_in_ranges()
print("*** Check ranges for values (verbose) ***")
check_for_values_in_ranges(True)
Вывод сценария:
$ python large_input.py
*** Check ranges for values (non-verbose) ***
Final results:
4: True
14: True
20: False
*** Check ranges for values (verbose) ***
Initial results:
4: False
14: False
20: False
After checking range: [4, 5]
4: True
14: False
20: False
After checking range: [11, 14]
4: True
14: True
20: False
After checking range: [15, 19]
4: True
14: True
20: False
After checking range: [23, 30]
4: True
14: True
20: False
Final results:
4: True
14: True
20: False