【问题标题】:Finding elements of an array lying between two values查找位于两个值之间的数组元素
【发布时间】:2013-02-27 14:19:23
【问题描述】:

那么问题如下:

首先,我使用 Python 进行编码。 我有一个排序自然数“givenY”的数组(Numpy 数组,但如果有帮助,我可以将其更改为列表)。我想找到并指向位于两个指定值a=Y[i]b=Y[i+1] 之间的第一个和最后一个元素。我编写了代码,但我相信我是以最糟糕的方式之一,我不确定代码是否在时间上有效。因此,如果我能获得 cmets 或获得从头开始编写它的建议,我会很高兴。重要的是在Y[i]Y[i+1]之间没有givenY的元素时有很多例外情况(通过分配-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。

非常感谢。

【问题讨论】:

  • 请提供样例输入输出。
  • 什么是givenY,是列表吗?

标签: python arrays intervals binary-search


【解决方案1】:

我不太了解返回的索引。例如,如果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

下面是一个返回 startend 的实现,因此具有以下属性:

  1. end-start 等于给定边界之间的元素数。
  2. list[start:end] 返回包含给定范围内所有值的切片。
  3. end-start 等于找到的元素数
  4. 当没有找到值时start==end

代码:

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() 文档字符串中描述。保留原始代码,因为恕我直言,使用 Python 编程时感觉更自然。

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()

【讨论】:

  • 非常感谢您抽出宝贵时间,但此代码不起作用:对于 givenY=[1,3,5,7] 它应该返回 (3,3) 但它返回 (3,4)这甚至超出了界限。而且正如我之前提到的,我在a和b之间没有给定Y值时设置start=-1以供以后使用。
  • 在这种情况下 Y[i] 和 Y[i+1] 的值是多少?请阅读帖子,因为我已经明确定义了返回值是什么。特别是,我声明 array[start:end] 将返回边界之间的值。在这种特殊情况下,4 不在边界之外,[1,3,5,7][3:4] 将返回 [7],因为上限指向不包含的第一个元素(类似于 C++ 中的迭代器)。如果您可以指定在不同情况下返回的确切内容,我将修改代码以处理此问题。
  • 它们是整数值,但这并不重要,因为我说过你可以用 a 和 b 替换它们。非常感谢,但我想 end 指向间隔中的最后一个元素!我已经在我的帖子中写了很多输入和输出(在帖子的末尾)
  • 我想要实际的绑定值进行测试 :) 我又添加了一个代码块,我希望它与您最初要求的匹配。根据代码、示例输出和您的 cmets,我不完全确定输出是什么。请检查我的文档字符串,看看它是否符合您的需要。
【解决方案2】:

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

去掉分号,不需要也不需要...

【讨论】:

  • 感谢您提醒列表已排序。因此使用二分搜索似乎比线性搜索更合乎逻辑。我更喜欢分号,它让我想起了 C 和 Java,但感谢提及!
  • 线性搜索不会比二分搜索慢吗?
猜你喜欢
  • 1970-01-01
  • 2015-07-20
  • 2015-04-08
  • 1970-01-01
  • 2021-12-27
  • 2012-02-21
  • 1970-01-01
  • 2012-01-12
  • 1970-01-01
相关资源
最近更新 更多