简体   繁体   English

如何检查数字是否在一组非连续范围内?

[英]How to check if a number is within a group of non-consecutive ranges?

Question may sound complicated, but actually is pretty simple, but i can't find any nice solution in Python. 问题听起来很复杂,但实际上很简单,但是我在Python中找不到任何不错的解决方案。

I have ranges like ("8X5000", "8X5099") . 我有类似("8X5000", "8X5099") Here X can be any digit, so I want to match numbers that fall into one of the ranges ( 805000..805099 or 815000..815099 or ... 895000..895099 ). 这里X可以是任何数字,因此我想匹配属于范围之一( 805000..805099815000..815099或... 895000..895099 )的数字。

How can I do this? 我怎样才能做到这一点?

@TimPietzcker answer is correct and Pythonic, but it raises some performance concerns (arguably making it even more Pythonic). @TimPietzcker的答案是正确的,并且是Pythonic的,但是它引起了一些性能方面的顾虑(可以说使其变得更加Pythonic)。 It creates an iterator that is searches for a value. 它创建一个搜索值的迭代器。 I don't expect Python to be able to optimize the search. 我不希望Python能够优化搜索。

This should perform better: 这应该表现更好:

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

The second line inside the function is a nested list comprehension so may be a little hard to read but it sets: 函数内的第二行是嵌套列表理解,因此可能有点难以理解,但它设置为:

minr = [8, 5000]
maxr = [8, 5099]

When n = 595049: 当n = 595049时:

nl = (5, 5049)

The code just splits the ranges into parts (while converting to int), splits the target number into parts, then range checks the parts. 代码只是将范围分割为多个部分(转换为int时),将目标编号分割为多个部分,然后范围检查这些部分。 It wouldn't be hard to enhance this to handle multiple X's in the range specifiers. 增强它以处理范围说明符中的多个X并不难。

Update 更新资料

I just tested relative performance using timeit: 我只是使用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)        

On my 32-bit Win 7 machine running Python 2.7.2 my solution is almost 10 times faster than @TimPietzcker's (to be specific, it runs in 12% of the time). 在运行Python 2.7.2的32位Win 7计算机上,我的解决方案几乎比@TimPietzcker的解决方案快10倍(具体来说,它的运行时间为12%)。 As you increase the size of the range, it only gets worse. 随着范围大小的增加,它只会变得更糟。 When: 什么时候:

ranges=("8X5000", "8X5999")

The performance boost is 50x. 性能提升了50倍。 Even for the smallest range, my version runs 4 times faster. 即使在最小范围内,我的版本运行速度也快4倍。

With @PaulMcGuire suggested performance patch to in_range , my version runs 3 times faster. 使用@PaulMcGuire建议的性能补丁in_range ,我的版本运行速度提高了3倍。

Update 2 更新2

Motivated by @PaulMcGuire's comment I went ahead and refactored our functions into classes. 受@PaulMcGuire的评论的激励,我继续将我们的功能重构为类。 Here's mine: 这是我的:

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

This did close the gap, but even after pre-computing range invariants (for both) @PaulMcGuire's took 50% longer. 这确实弥合了差距,但是即使在预先计算了范围不变式(两者)之后,@ PaulMcGuire花费了50%以上的时间。

range = (80555,80888)

x = 80666

print range[0] < x < range[1]

maybe what your looking for ... 也许您正在寻找...

Example for Python 3 (in Python 2, use xrange instead of range ): 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)

Results: 结果:

>>> in_range(805001)
True
>>> in_range(895099)
True
>>> in_range(805100)
False

An improvement to this, suggested by Paul McGuire (thanks!): 保罗·麦圭尔(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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM