有没有更好的方法来检查一个数字是不是是两个数字的范围

Posted

技术标签:

【中文标题】有没有更好的方法来检查一个数字是不是是两个数字的范围【英文标题】:Is there a better way to check if a number is range of two numbers有没有更好的方法来检查一个数字是否是两个数字的范围 【发布时间】:2020-09-18 11:40:12 【问题描述】:

我正在尝试检查一个数字是否在整数范围内,并根据它所在的范围返回一个数字。我想知道是否有更好、更有效的方法:

def checkRange(number):
    if number in range(0, 5499):
        return 5000
    elif number in range(5500, 9499):
        return 10000
    elif number in range(9500, 14499):
        return 15000
    elif number in range(14500, 19499):
        return 20000
    elif number in range(19500, 24499):
        return 25000
    elif number in range(24500, 29499):
        return 30000
    elif number in range(29500, 34499):
        return 35000
    elif number in range(34500, 39499):
        return 40000
    elif number in range(39500, 44499):
        return 45000

这感觉像是在浪费资源,如果有更好的方法来做到这一点,我们将不胜感激。

【问题讨论】:

一种更有效的方法是检查数字是否 >= 每个范围的下限和 “这感觉像是在浪费资源,如果有更好的方法可以做到这一点,我们将不胜感激。”什么意义上的资源浪费?以什么方式更好?请具体/准确 您的代码有缺陷,因为 checkRange(5499) 返回 None 而不是 5000 【参考方案1】:

由于您有连续的、排序的范围,因此一种更快、更简洁的方法是使用bisect 模块在断点列表中查找索引,然后使用它从列表中获取相应的值价值观:

import bisect

break_points = [5499,  9499, 14499, 19499, 24499, 29499, 34499, 39499, 44499]
values       = [5000, 10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000]

n = 10000
index = bisect.bisect_left(break_points, n)

values[index]
# 15000

如果有可能,您需要测试超过最后一个断点的n 值。或者,您可以将默认值添加到值列表的末尾。

【讨论】:

现在这实际上在算法上更快(只要您没有在每个方法调用上创建列表..)!但是,对于这么小的东西,我敢打赌线性搜索会赢,除非列表变得更大 哦,哇,这太棒了,因为我计划添加更多的条件语句而不在代码中添加太多行。感谢您采用这种新方法! @juanpa.arrivillaga 你可能是对的,但bisect 速度非常快。我不想用纳秒来阻塞这个线程。但是,即使对于短列表,我也无法进行更快的线性搜索。【参考方案2】:

如果 更好 是指更快,最好检查下限和上限,正如之前的 cmets/answers 所建议的那样。

但是,请注意,在 Python3 中,range() 对象会为您执行此操作,导致in range() 检查几乎是一个恒定的时间操作,所以我认为使用您的代码运行时应该不会有问题。

我强烈推荐阅读这个帖子:

Why is "1000000000000000 in range(1000000000000001)" so fast in Python 3?

【讨论】:

【参考方案3】:

如果有很多范围,您的解决方案就会变得非常冗长。你可能想试试这个(更简单的)代码:

limits = (0, 5500, 9500, 14500, 19500, 24500, 29500, 34500, 39500, 44500)

def checkRange(number):
    for i, (low, high) in enumerate(zip(limits[:-1], limits[1:]), 1):
        if low <= number < high:
            return 5000*i
    return 0  # when the passed number is out of range

值得指出的是,您的逻辑有一个错误,而在我的实现中,这个问题已得到解决。

演示:

In [188]: checkRange(5499)
Out[188]: 5000

In [189]: checkRange(5500)
Out[189]: 10000

In [190]: checkRange(24872)
Out[190]: 30000

【讨论】:

【参考方案4】:

如果你的范围是一成不变的,你可以遍历你的范围列表:

ranges = [[0,5499],[5500,9499],[9500,14499],[14500,19499],[19500,24499],[24500,29499],[29500,34499],[34500,39499],[39500,44499]]
returns = [5000,10000,15000,20000,25000,30000,35000,40000,45000]

def checkRange(number):
    for i in range(len(returns)):
        if number in range(ranges[i][0], ranges[i][1]):
            return returns[i]

# Test a few values:
print(checkRange(10))
print(checkRange(6000))

我得到输出:

5000 
10000

另外,请务必修复您的第 8 个条目,使其成为 range 而不是单个 int

【讨论】:

【参考方案5】:

如果你的目标真的是做这种“四舍五入”运算,那么只使用数学可能是最快的:

def round_up_to(num, factor):
    return -(-num // factor ) * factor

def checkRange2(num):
    if num < 5500:  # was this first interval perhaps a mistake?
        return 5000
    else:
        return round_up_to(num + 501, 5000))

这适用于任意大的整数。

【讨论】:

【参考方案6】:

如果逻辑需要泛化,则可以使用以下某种形式的函数,具体取决于舍入和范围检查:

def checkRange(number):

   count = 0
   while int(number):
      number /= 10
      count += 1

   round_off = count - 1
   for i in range(count - round_off):
      number *= 10

   number = int(number)

   return int((10**(count - 1)) * (number))

所以基本上,找到数字的位数,然后计算要保留的位数,并根据这两个函数生成四舍五入的数字。可以根据需要进行舍入的参数化。我们还可以将偏移量作为位数减去所需四舍五入的函数。

同样来自示例代码,进一步简化代码的方法不需要下限。

【讨论】:

【参考方案7】:

首先,让我清除范围功能。它的工作原理不包括结束限制。 因此,您与 range(0,5499) 一起使用的 if 条件将检查此 list [0, 1, ....., 5477, 5488] 中的数字 不是这个列表 [0, 1, ..... , 5477, 5488, 5499]

现在回答您的问题,可以有多种有效的方法来完成这项工作(取决于最终结果),因此,作为公认答案提到的 bisect 包可以很好地完成工作。 如果您想在没有任何库的情况下以有效的方式执行此操作,下面的代码应该会有所帮助:

rangeList =  [5499, 9499, 14499, 19499, 24499, 29499, 34499, 39499, 44499]
returnList = [5000, 10000, 15000, 20000, 25000, 30000, 35000, 40000, 45000]

def checkRange(number):
    for i in range(len(rangeList)):
        if number<=rangeList[i]:
            return returnList[i]
    return -1

inp: checkRange(5499)
op: 5000

此代码使用 if 条件的基本原则并考虑到您的范围按升序有效地工作。

【讨论】:

【参考方案8】:

我倾向于将这些类型的条件写成数学公式

return (aFunction((number - Min)/intervalLength) + c1) * c2

c1c2 的位置以及 aFunctionceil 还是 floor 取决于您想要什么以及您处理边界的方式。

这里,除了前两个区间外,其他所有区间的长度都是 5000。所以我会为其余的写公式。所以,

def checkRange(number):
    if number in range(0, 5499):
        return 5000
    elif number in range(5500, 9499):
        return 10000
    elif number in range(9500, 44499):
        return (floor((number - 9500)/4000) + 3) * 5000

尽管如此,我不得不承认,只有当你的区间在相似的边界条件下都相同并且你的结果从一个条件到下一个条件具有相同的增量时,这个解决方案才是优雅的。

【讨论】:

【参考方案9】:

你应该替换:

if number in range(0,5499):

if 0 <= number < 5499 :

速度更快,内存占用更少。

【讨论】:

不,它占用的内存也少很多。 range 对象使用小而恒定的内存开销并具有恒定的时间成员资格测试 @juanpa.arrivillaga 你听说过 python2 吗? 是的。 Python 2 已经过了生命的尽头。在 Python 标签上,除非另有明确说明,否则通常假定您的意思是 python 3。在任何情况下,问题都被标记为 Python 3 @juanpa.arrivillaga 最新版本中引入的优化并不是写坏代码的借口 这不是优化,它是新 python 规范的一部分。而且它不是最新版本,Python 3 是十多年前问世的,而 Python 2 不再受支持。 python 3 中的range 返回一个range 对象,而不是列表,因此这是对范围对象的完全合理使用,这就是它们的用途。这不是“糟糕的代码”,它使用内置的数据结构来达到预期目的

以上是关于有没有更好的方法来检查一个数字是不是是两个数字的范围的主要内容,如果未能解决你的问题,请参考以下文章

有啥简单的方法可以检查两个数字是不是有不同的符号?

检查数字是不是有小数的最快方法[重复]

试图制作一个程序来检查密码是不是包含大写字母和 c 中的数字

检查数字是 int 还是 float

需要更好的解决方案来删除特殊字符和数字

检查一个数字是不是可以被另一个整除的快速方法?