获取连接范围列表的索引
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了获取连接范围列表的索引相关的知识,希望对你有一定的参考价值。
我有一个范围列表,该列表中的所有范围都有同样的 start
和 stop
但不一样 step
......例如:......................................................................................................................................................................................................................................:
[range(0, 10, 2), range(0, 10, 3)]
当然,这个列表可以包含超过2个范围。并列的范围列表表示以下数字。
[0, 2, 3, 4, 6, 8, 9]
我想得到 x
并列的范围列表的索引。例如,最后一个例子的5索引将是 8
.
问题是,这个范围可能很大(几百万),我不想把这个范围变成一个列表来获取 x
索引。我需要以某种方式来计算该指数的值。x
索引,而不需要 "打开 "该范围列表
试着想了几个小时,想出一个算法来做,我找到的最好的解决方案包括使用二进制搜索来做,我认为这不是理想的方式。
有什么办法可以让我达到这个目的吗?
你可以制作一个新的 range
一样 start
和 end
,并打包所有 step
到一个新的列表中。现在你可以检查范围内的每一个数字是否符合 任何 步。你可以把它做成一个生成器。
def steps_range(start, end, steps):
for i in range(start, end):
if any(i % x == 0 for x in steps):
yield i
现在你可以在这个生成器上循环,直到你到达相关的索引。根据你的例子。
ranges = [range(0, 10, 2), range(0, 10, 3)]
start = ranges[0].start
end = ranges[0].stop
steps = [r.step for r in ranges]
target_index = 5
for i, num in enumerate(steps_range(start, end, steps)):
print(num)
if i == target_index:
break
这个就会被打印出来
0
2
3
4
6
8
关键点是要使用 yield
.
困难的是如何处理这种情况,当一个人有一个人的时候。yield
端。最后我选择的解决方案是用dict,用 min
得到 iterator
谁要搬家next
). 并检查是否有 iterator
到最后。如果是,就把它从 dict
.
#!/usr/bin/env python3
import operator
class IterTool:
def __init__(self, the_range):
def foo(the_range):
def bar():
for i in the_range:
yield i
return bar()
self.the_range = foo(the_range)
self.end = False
def next_item(self):
try:
foo = next(self.the_range)
return foo
except StopIteration:
self.end = True
def is_end(self):
return self.end
pool = {}
for i in [IterTool(range(0, 10000000000000, 2)), IterTool(range(0, 10000000000000, 3))]:
pool[i] = i.next_item()
idx = 0
last_val = None
while all(map(lambda x: not x.is_end(), pool)):
if len(pool) == 0:
break
key = min(pool.items(), key=operator.itemgetter(1))[0]
val = pool[key]
if val != last_val:
if idx == 99999:
print("%s=> %s" % (idx, val))
break
idx += 1
last_val = val
pool[key] = key.next_item()
if key.is_end():
del pool[key]
结果。
99999=> 149998
real 0m0.209s
user 0m0.200s
sys 0m0.004s
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
'th指数,我们会直接说 x
作为答案。然而,在这个序列中,范围的步骤会跳过一些值。例如,如果步骤是2和3,我们就会得到 0, 2, 3, 4, 6, ..
以此类推。因此,如果我们能找到那些跳过的数字中不被任何给定步骤(直到给定指数,包括在内)所整除的数字,那么我们只需将其相加,就能得到解题的候选数,但候选数仍可能不被任何步骤整除。
例如,考虑到你在问题中的例子,不被除数的数目是2(1和5),我们将2加到给定的指数5上,得到7;但这并不能均匀地除以2或3)。因此,我们做一个增量搜索,由 candidate
等,直到我们找到我们想要的值。最后,因为我们假设start为0,所以我们把原来的start值加回来,得到结果。
编辑:我为索引添加了一个检查,使其不超过停止值。
以上是关于获取连接范围列表的索引的主要内容,如果未能解决你的问题,请参考以下文章