问题可能听起来很复杂,但实际上很简单,但我在 Python 中找不到任何好的解决方案。
我有像
("8X5000", "8X5099")
. 这里X
可以是任何数字,所以我想匹配属于某个范围(805000..805099
或815000..815099
或... 895000..895099
)的数字。
我怎样才能做到这一点?
@TimPietzcker 的答案是正确的并且是 Pythonic,但它会引起一些性能问题(可以说使它更加 Pythonic)。它创建一个搜索值的迭代器。我不希望 Python 能够优化搜索。
这应该表现更好:
def IsInRange(n, r=("8X5000", "8X5099")):
(minr, maxr) = [[int(i) for i in l.split('X')] for l in r]
p = len(r[0]) - r[0].find('X')
nl = (n // 10**p, n % 10**(p-1))
fInRange = all([minr[i] <= nl[i] <= maxr[i] for i in range(2)])
return fInRange
函数内的第二行是嵌套列表理解,因此可能有点难以阅读,但它设置:
minr = [8, 5000]
maxr = [8, 5099]
当 n = 595049 时:
nl = (5, 5049)
该代码只是将范围拆分为多个部分(同时转换为 int),将目标编号拆分为多个部分,然后范围检查这些部分。增强它以处理范围说明符中的多个 X 并不难。
我刚刚使用 timeit 测试了相对性能:
def main():
t1 = timeit.timeit('MultiRange.in_range(985000)', setup='import MultiRange', number=10000)
t2 = timeit.timeit('MultiRange.IsInRange(985000)', setup='import MultiRange', number=10000)
print t1, t2
print float(t2)/float(t1), 1 - float(t2)/float(t1)
在我运行 Python 2.7.2 的 32 位 Win 7 机器上,我的解决方案几乎比 @TimPietzcker 的解决方案快 10 倍(具体来说,它在 12% 的时间内运行)。当你增加范围的大小时,它只会变得更糟。什么时候:
ranges=("8X5000", "8X5999")
性能提升 50 倍。即使对于最小的范围,我的版本运行速度也快了 4 倍。
使用@PaulMcGuire 建议的性能补丁in_range
,我的版本运行速度提高了 3 倍。
受到@PaulMcGuire 评论的启发,我继续将我们的函数重构为类。这是我的:
class IsInRange5(object):
def __init__(self, r=("8X5000", "8X5099")):
((self.minr0, self.minr1), (self.maxr0, self.maxr1)) = [[int(i) for i in l.split('X')] for l in r]
pos = len(r[0]) - r[0].find('X')
self.basel = 10**(pos-1)
self.baseh = self.basel*10
self.ir = range(2)
def __contains__(self, n):
return self.minr0 <= n // self.baseh <= self.maxr0 and \
self.minr1 <= n % self.basel <= self.maxr1
这确实缩小了差距,但即使在预先计算范围不变量(两者)之后@PaulMcGuire 的时间也增加了 50%。
range = (80555,80888)
x = 80666
print range[0] < x < range[1]
也许你在找什么...
Python 3 的示例(在 Python 2 中,使用xrange
代替range
):
def in_range(number, ranges=("8X5000", "8X5099")):
actual_ranges = ((int(ranges[0].replace("X", digit)),
int(ranges[1].replace("X", digit)) + 1)
for digit in "0123456789")
return any(number in range(*interval) for interval in actual_ranges)
结果:
>>> in_range(805001)
True
>>> in_range(895099)
True
>>> in_range(805100)
False
Paul McGuire 建议对此进行改进(谢谢!):
def in_range(number, ranges=("8X5000", "8X5099")):
actual_ranges = ((int(ranges[0].replace("X", digit)),
int(ranges[1].replace("X", digit)))
for digit in "0123456789")
return any(minval <= number <= maxval for minval, maxval in actual_ranges)