【问题标题】:Optimized way to find in which range does a number lie查找数字在哪个范围内的优化方法
【发布时间】:2018-10-23 19:17:09
【问题描述】:

我有多个范围,比如说 1-1000、1000-2000、2000-3000、3000-4000、4000-5000。我从用户那里得到一个号码,现在我需要找到它在哪个范围内。一种方法是创建多个 if 语句并从那里进行检查,如下所示:

if num>=1 and num < 1000:
    print "1"
elif num >=1000 and num < 2000:
    print "2"
....   

这种方法会创建很多分支。

有没有一种优化的方法可以在没有这么多分支的情况下以最低的复杂度做到这一点?

PS:我只是在 python 中编写了代码,因为它编写起来更短,但这可以是任何语言的情况。范围和输出也可能非常不同。 范围和输出是示例,可以是 1-100、100-1000、1000-1500 等任何值,输出类似于“非常低、低、中”之类的东西。

【问题讨论】:

  • 除以 1000 并向上取整?
  • 这是这个特定示例的情况。如果我必须提供的输出不是数字而是完全别的东西怎么办?另外,如果范围不等间距怎么办?
  • 如果它不是数字或数字的东西,你会如何排列它?
  • @PrateekGupta 如果范围内没有模式,您将需要评估所有可能性。充其量,您可能希望优化开始搜索的方式。

标签: python


【解决方案1】:

将范围的开始或结束存储在列表中,并将其与数字一起排序以找到其确切范围。

import numpy as np
start = [1,1000,2000,3000,4000]
print(list(np.sort(start+[num])).index(num))

【讨论】:

    【解决方案2】:

    如果您的范围不遵循任何特定逻辑,那么除了逐个测试它们之外,您无能为力,但您仍然可以通过使用循环来简化代码:

    ranges = [[0,1000],[1500,1600],[1200,1220]]
    
    def find_range(num, ranges)
        for low, high in ranges:
            if low <= num < high:
                return low, high # or any other formating using a dict for example
    

    当然,您可以通过对范围进行排序然后进行二进制搜索而不是线性搜索来进行一些优化...

    【讨论】:

    • 二分搜索是一个不错的选择,它会很有帮助。只是为了澄清,虽然这方面并没有减少分支的数量,而是增加了复杂性以使代码简单?
    • 不确定你所说的“分支”是什么意思,如果它是 if 测试的数量,那么这与你的代码完全相同,没有更好也没有更糟(只是写得更好):与范围数线性n。如果您确实实现了二分搜索,那么它将大量减少到 log(n),如果需要,可能会产生一次性的 n log(n) 开销来对您的范围进行排序。
    【解决方案3】:
    my_range=(1,1000), (1000,2000), (2000,3000), (3000,4000), (4000,5000)
    my_output='Very Low, Low, Medium, High, Very High'.split(', ')
    num=3565
    for k,i in enumerate(my_range):
       if i[0]<=num<i[1]:print(my_output[k]);break
    else:
       print('Out of range')
    

    【讨论】:

      【解决方案4】:

      这样的事情怎么样:

      ranges = {
          0: 'undefined range',
          1000: '1',
          1500: '2',
          2500: '3'
      }
      
      num = 500
      
      print(ranges[max(ranges, key=lambda x: num < x)])
      

      输出:1

      【讨论】:

        【解决方案5】:

        怀疑您有 许多 个断点并需要优化搜索,您可以对有序的断点列表进行二等分,从而导致对数时间消耗:

        import random
        import time
        
        
        def split_pivot(intervals, number):
            """Divide and conquer recursively."""
            if len(intervals) == 1:
                return intervals[0]
            if len(intervals) == 2:
                if number >= intervals[1][1][0]:
                    return intervals[1]
                elif number < intervals[0][1][1]:
                    return intervals[0]
                else:
                    raise
        
            pivot = int(len(intervals) // 2.0)
        
            if number < intervals[pivot][1][1]:
                return split_pivot(intervals[:pivot + 1], number)
            elif number >= intervals[pivot + 1][1][0]:
                return split_pivot(intervals[pivot + 1:], number)
            else:
                raise
        
        
        if __name__ == '__main__':
            UPPER_BOUND = 10000000
        
            newbreak = 0
            manybreaks = []
            while newbreak < UPPER_BOUND:
                step = int(random.random() * 10) + 1
                manybreaks.append(newbreak + step)
                newbreak = manybreaks[-1]
            print('Breaks: {:d}'.format(len(manybreaks)))
        
            intervals = [
                (idx, (manybreaks[idx], manybreaks[idx + 1]))
                for idx in range(len(manybreaks) - 1)
            ]
            print('Intervals: {:d}'.format(len(intervals)))
            print(
                '  Example: idx {tpl[0]:d}, lower {tpl[1][0]:d}, upper {tpl[1][1]:d}'
                .format(tpl=random.choice(intervals)))
        
            thenumber = int(random.random() * UPPER_BOUND)
            print('Number: {:d}'.format(thenumber))
        
            t0 = time.time()
            result = split_pivot(intervals, thenumber)
            t1 = time.time()
        
            print('Result: {e[0]:d} ({e[1][0]:d}, {e[1][1]:d})'.format(e=result))
            print('  Done in {:.4f}s'.format(t1 - t0))
        

        搜索结果本身(在我的机器上)低于 0.05 秒。断点的生成和相应的时间间隔大约运行 4.5 秒:

        Breaks: 1818199
        Intervals: 1818198
          Example: idx 605849, lower 3330441, upper 3330446
        Number: 6951844
        Result: 1263944 (6951843, 6951847)
          Done in 0.0436s
        

        【讨论】:

          【解决方案6】:

          也许只是除以 1000 并取整个部分:

          这里是python中的例子:

          >>> x=3608
          >>> int(x/1000+1)
          4
          

          根据您在帖子中的评论/编辑,如果您需要不同的输出(例如字符串),您可以(在 python 中)使用dict

          >>> Output={'1': 'very low', '2': 'low', '3': 'medium','4':'high' }
          >>> x=2954
          >>> Output[str(int(x/1000+1))]
          'medium'
          

          【讨论】:

          • 它应该为3608而不是2打印4检查OP的问题
          猜你喜欢
          • 2017-06-08
          • 1970-01-01
          • 2014-03-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多