1

问题可能听起来很复杂,但实际上很简单,但我在 Python 中找不到任何好的解决方案。

我有像 ("8X5000", "8X5099"). 这里X可以是任何数字,所以我想匹配属于某个范围(805000..805099815000..815099或... 895000..895099)的数字。

我怎样才能做到这一点?

4

3 回答 3

3

@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 倍。

更新 2

受到@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%。

于 2012-11-28T08:51:34.403 回答
1
range = (80555,80888)

x = 80666

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

也许你在找什么...

于 2012-11-28T06:52:18.293 回答
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)
于 2012-11-28T07:04:55.433 回答