def is_not_divisible_by_any(num, divs):
return all(num % divisor for divisor in divs)
def get_idx_of_concated_list(the_list, idx):
# Get the start, end and steps
start, end = the_list[0].start, the_list[0].stop
shifted_end = end - start
steps = [r.step for r in the_list]
# Get the number of numbers non-divisble by the steps until the given index (inclusive)
num_not_divisibles = sum(is_not_divisible_by_any(num, steps) for num in range(idx+1))
# The first candidate will be the given index + the above number of non-divisibles
candidate = idx + num_not_divisibles
# Forward by one till it is divisible by any of the steps
while is_not_divisible_by_any(candidate, steps):
candidate += 1
# Check if the given index was valid
if candidate > shifted_end:
raise ValueError("The given index with those ranges exceed the stop value")
# Since assumed start = 0, add the original start
return candidate + start
# Example:
concat_list = [range(0, 1_000_000, 2), range(0, 1_000_000, 3), range(0, 1_000_000, 7)]
idx = 225_000
print(get_idx_of_concated_list(concat_list, idx))
# 289286
Объяснение: без ограничения общности предположим, что начало равно 0 (мы можем легко добавить исходное начало обратно в конец, как вы увидите). Тогда, по сути, мы имеем следующую последовательность:
0, 1, 2, 3, 4, 5, ..., stop-1
Если бы для этой последовательности нам было предложено найти значение в x
-м индексе, мы бы прямо сказали x
в качестве ответа. Однако шаги диапазонов пропускают некоторые значения в этой последовательности. Например, если шаги 2 и 3, у нас будет 0, 2, 3, 4, 6, ..
и так далее. Итак, если мы сможем найти количество тех пропущенных чисел, которые не делятся ни на один из заданных шагов (до данного индекса включительно), то мы просто добавим его и получим кандидата для решения.
Но кандидат по-прежнему может не делиться ни на один из этапов (например, рассмотрите свой пример в вопросе, где количество неделимых будет 2 (1 и 5), и мы добавляем 2 к заданному проиндексируйте 5 и получите 7; но это не делит 2 или 3 равномерно). Поэтому мы выполняем инкрементный поиск от candidate
и далее, пока не найдем желаемое значение. И, наконец, поскольку мы приняли start как 0, мы добавляем исходное начальное значение обратно, чтобы получить результат.
Edit: я добавил проверку индекса, чтобы он не превышал конечное значение.