查找位于两个值之间的数组元素

时间:2013-02-26 17:13:28

标签: python arrays intervals binary-search

问题如下:

首先,我在Python编码。 我有一个数组(Numpy数组,但如果它可以是任何帮助,我可以将其更改为列表)排序的自然数,“givenY”。我想找到并指向落在两个指定值a=Y[i]b=Y[i+1]之间的第一个和最后一个元素。我编写了代码,但我相信我是用最糟糕的方式之一做的,我不确定代码是否在时间上有效。如果我能得到意见或得到建议从头开始编写,我会很高兴。重要的是,当Y[i]Y[i+1]之间没有给定Y的元素时(通过分配-1来处理),有许多例外情况。我的代码是:

<击>

<击>
startRes=binSearch(givenY,Y[i]);
endRes=binSearch(givenY,Y[i+1]);        
start=startRes[1]
end=endRes[1];        
if(givenY.size==0 or (givenY.size>0 and givenY[start]<=Y[i])):
    start=startRes[1]+1;
if(endRes[0]):
    end=endRes[1]-1;
if end<start or (givenY.size>0 and (givenY[end]>Y[i+1] or givenY[start]>=Y[i+1])) or givenY[end]<=Y[i]:
    start=-1;

<击>

startRes=binSearch(givenY,a);
endRes=binSearch(givenY,b);        
start=startRes[1]
if startRes[0]:
    start=start+1;
end=endRes[1]-1;        

这是binSearch的实现:

def binSearch(arr,element):
left=0
right=arr.size;
mid=(left+right)/2
while left<right:
    mid=(left+right)/2
    if(arr[mid]<element):
        left=mid+1;
    elif (arr[mid]>element):
        right=mid;
    else: 
        return True,mid;
return False,left;

一些简单的输入和输出:

对于给定的Y = [2,5,8,10]:

  • a = 3,b = 4,输出:值之间没有。(我的代码中的start = -1)
  • a = 2,b = 5,输出:值之间没有。(我的代码中的start = -1)
  • a = 2,b = 9输出:start = 1,end = 2
  • a = 1,b = 10,输出:start = 0,end = 2
  • a = 1,b = 11,输出:start = 0,end = 3
  • a = 11,b = 12,输出:值之间没有。(我的代码中的start = -1)
  • a = 0,b = 2,输出:值之间没有。(我的代码中的start = -1)
  • a = 3,b = 3,输出:值之间没有。(我的代码中的start = -1)
  • a = 5,b = 5,输出:值之间没有。(我的代码中的start = -1)

在我目前工作的情况下,b总是大于a。

非常感谢。

2 个答案:

答案 0 :(得分:3)

我不太明白返回的指数。例如,如果givenY是空列表,则startend都将为-1。此外,您发布的代码不会处理列表中的重复值。

您可以使用bisect模块,而不是手动编码的二进制搜索。有关详细信息,请参阅API文档:

  1. Python 3.3 - 8.6. bisect — Array bisection algorithm
  2. Python 2.7.3 - 8.5. bisect — Array bisection algorithm
  3. 以下是返回startend的实现,以便保留以下属性:

    1. end-start等于给定边界之间的元素数。
    2. list[start:end]返回包含给定边界之间所有值的切片。
    3. end-start等于找到的元素数量
    4. 未找到任何值时start==end
    5. 代码:

      import unittest
      
      from bisect import bisect_left, bisect_right
      
      
      def find_range(array, a, b):
          start = bisect_right(array,a)
          end = bisect_left(array,b)
          return (start, end)
      
      
      class TestCase(unittest.TestCase):
          Y = [1, 3, 5, 10, 15]
          givenY = [3, 4, 5, 6, 7, 8, 9, 10, 11]
      
          def test_empty_array(self):
              self.assertEqual( (0, 0), find_range([], 1, 2) )
      
          def test_all_values_larger(self):
              self.assertEqual( (0, 0), find_range([4,5,6], 1, 3) )
      
          def test_all_values_larger_or_equal(self):
              self.assertEqual( (0, 0), find_range(self.givenY, self.Y[0], self.Y[1]) )
      
          def test_both_endpoints_inside_list(self):
              self.assertEqual( (1, 2), find_range(self.givenY, self.Y[1], self.Y[2]))
              self.assertEqual( [4], self.givenY[1:2])
      
          def test_2(self):
              self.assertEqual( (3, 7), find_range(self.givenY, self.Y[2], self.Y[3]) )
              self.assertEqual( [6, 7, 8, 9], self.givenY[3:7])
      
          def test_no_values_larger_or_equal_to_upper_limit(self):
              self.assertEqual( (8, 9), find_range(self.givenY, self.Y[3], self.Y[4]) )
              self.assertEqual( [11], self.givenY[8:9])
      
      
      if __name__=="__main__":
          unittest.main()
      

      注意:如果需要,可以很容易地将返回的开始和结束位置调整为当前值,只需确保它是一致的。

      修改

      以下是返回所请求值的代码,据我所知,从给出的示例中可以看出。逻辑在find_range() docstring中描述。原始代码保留,因为在使用Python编程时,IMHO感觉更自然。

      import unittest
      
      from bisect import bisect_left, bisect_right
      
      
      def find_range(array, a, b):
          """Find elements that are greater than a and less than b.
          Returns a tuple (start,end) where array[start] is the first
          value and array[end] is the last value.
          If no value is found, returns start=end=-1.
          """
          start = bisect_right(array,a)
          end = bisect_left(array,b)
          if start==end:
              return (-1,-1)
          else:
              return (start, end-1)
      
      
      class TestCase(unittest.TestCase):
          Y = [1, 3, 5, 10, 15]
          givenY = [3, 4, 5, 6, 7, 8, 9, 10, 11]
      
          def test_empty_array(self):
              self.assertEqual( (-1, -1), find_range([], 1, 2) )
      
          def test_all_values_larger(self):
              self.assertEqual( (-1, -1), find_range([4,5,6], 1, 3) )
      
          def test_all_values_larger_or_equal(self):
              self.assertEqual( (-1, -1), find_range(self.givenY, self.Y[0], self.Y[1]) )
      
          def test_both_endpoints_inside_list(self):
              self.assertEqual( (1, 1), find_range(self.givenY, self.Y[1], self.Y[2]))
      
          def test_2(self):
              self.assertEqual( (3, 6), find_range(self.givenY, self.Y[2], self.Y[3]) )
      
          def test_no_values_larger_or_equal_to_upper_limit(self):
              self.assertEqual( (8, 8), find_range(self.givenY, self.Y[3], self.Y[4]) )
      
          def test_sample(self):
              self.assertEqual( (3,3), find_range([1,3,5,7], 5, 8)  )
              self.assertEqual( (3,3), find_range([1,3,5,7], 6, 8)  )
      
      
      if __name__=="__main__":
          unittest.main()
      

答案 1 :(得分:1)

首先对列表进行排序,然后进行线性搜索。

删除分号,不需要它们也不需要......