【问题标题】:find the first occurrence of a number greater than k in a sorted array在排序数组中找到大于 k 的数字的第一次出现
【发布时间】:2019-06-12 18:45:54
【问题描述】:

对于给定的排序列表,程序应返回列表中大于作为输入给出的数字的数字的索引。

现在,当我运行代码并检查它是否正常工作时,我得到了 2 个输出。一个是值,另一个输出是 None。

如果说我为下面的代码输入了 3。预期的输出是索引 20,即 1,而不是我得到 1,然后是 None。

如果我给出的值大于列表中的值,我将得到正确的输出,即“输入的数字大于列表中的数字”

num_to_find = int(input("Enter the number to be found"))

a=[2,20,30]
def occur1(a,num_to_find):
    j = i = 0
    while j==0:
        if a[len(a)-1] > num_to_find:
            if num_to_find < a[i]:
                j=1
                print(i)
                break
            else:
                i = i + 1
        else:
            ret_state = "The entered number is greater than the numbers in the list"
            return ret_state

print(occur1(a,num_to_find))

【问题讨论】:

  • 你在 print(i) 中打印 i 并且没有返回它,并且你的 return ret_state 永远不会被命中,所以函数返回 None 并且你打印它

标签: python-3.x


【解决方案1】:

由于额外的变量、糟糕的变量名称(j 通常用作索引,而不是布尔标志)、break 的使用、嵌套条件和side effect,此代码难以推理。这也是低效的,因为它需要在最坏的情况下访问列表中的每个元素,并且无法充分利用列表的排序特性。但是,它似乎有效。

您的第一个误解可能是print(i) 正在打印下一个最大元素的索引,而不是元素本身。在您对 occur1([2, 20, 30], 3)) 的示例调用中,1 是 20 在数组中的位置。

其次,一旦找到的元素被打印出来,函数在退出循环后返回Noneprint尽职尽责地打印None。希望这可以解释您的输出 - 您可以使用 return a[i] 代替 break 来解决您的直接问题并满足您的期望。


话虽如此,Python 有一个内置模块:bisect。这是一个例子:

from bisect import bisect_right

a = [1, 2, 5, 6, 8, 9, 15]
index_of_next_largest = bisect_right(a, 6)
print(a[index_of_next_largest]) # => 8

如果大于k 的下一个数字超出范围,您可以try/except 或使用条件来报告您认为合适的失败。该函数利用了列表使用binary search algorithm 排序的事实,这将每一步的搜索空间减半。时间复杂度为 O(log(n)),非常快。


如果您确实希望坚持使用类似于您的解决方案的线性算法,您可以将您的逻辑简化为:

def occur1(a, num_to_find):
    for n in a:
        if n > num_to_find:
            return n

# test it...
a = [2, 5, 10]

for i in range(11):
    print(i, " -> ", occur1(a, i))

输出:

0  ->  2
1  ->  2
2  ->  5
3  ->  5
4  ->  5
5  ->  10
6  ->  10
7  ->  10
8  ->  10
9  ->  10
10  ->  None

或者,如果你想要下一个最大数的索引:

def occur1(a, num_to_find):
    for i, n in enumerate(a):
        if n > num_to_find:
            return i

但我想强调的是,无论从哪方面来说,二分搜索都优于线性搜索。对于十亿个元素的列表,在线性版本将进行十亿次比较的最坏情况下,二分搜索将进行大约 20 次比较。不使用它的唯一原因是如果不能保证列表是预先排序的,这里不是这种情况。

为了更具体,您可以使用这个程序(但在实践中使用内置模块):

import random

def bisect_right(a, target, lo=0, hi=None, cmps=0):
    if hi is None:
        hi = len(a)

    mid = (hi - lo) // 2 + lo
    cmps += 1

    if lo <= hi and mid < len(a):
        if a[mid] < target:
            return bisect_right(a, target, mid + 1, hi, cmps)
        elif a[mid] > target:
            return bisect_right(a, target, lo, mid - 1, cmps)
        else:
            return cmps, mid + 1

    return cmps, mid + 1

def linear_search(a, target, cmps=0):
    for i, n in enumerate(a):
        cmps += 1

        if n > target:
            return cmps, i

    return cmps, i

if __name__ == "__main__":
    random.seed(42)
    trials = 10**3
    list_size = 10**4
    binary_search_cmps = 0
    linear_search_cmps = 0

    for n in range(trials):
        test_list = sorted([random.randint(0, list_size) for _ in range(list_size)])
        test_target = random.randint(0, list_size)
        res = bisect_right(test_list, test_target)[0]
        binary_search_cmps += res
        linear_search_cmps += linear_search(test_list, test_target)[0]

    binary_search_avg = binary_search_cmps / trials
    linear_search_avg = linear_search_cmps / trials
    s = "%s search made %d comparisons across \n%d searches on random lists of %d elements\n(found the element in an average of %d comparisons\nper search)\n"
    print(s % ("binary", binary_search_cmps, trials, list_size, binary_search_avg))
    print(s % ("linear", linear_search_cmps, trials, list_size, linear_search_avg))

输出:

binary search made 12820 comparisons across
1000 searches on random lists of 10000 elements
(found the element in an average of 12 comparisons
per search)

linear search made 5013525 comparisons across
1000 searches on random lists of 10000 elements
(found the element in an average of 5013 comparisons
per search)

添加的元素越多,线性搜索的情况就越糟糕。

【讨论】:

  • 如果我只想要索引而不想要数字怎么办?那么上面提到的代码是没有用的权利(在def函数中提到)。
  • 如果可能的话,您能否查看此代码github.com/sathwik007/test/blob/master/count.py
  • 我更新以展示如何使用线性搜索返回索引。您更新的代码有效,但它可能需要大量清理(请参阅帖子中的建议)。但是你真的应该使用二进制搜索。如果你必须手写,那就去做吧。我希望我传达的信息比一个一个的要快得多——在十亿个元素的列表中,它会在最坏的情况下找到 20 次(!)比较的结果,而你的算法在最坏的情况下会采用亿 (!) 比较。这是一个巨大的改进。
  • 格格伦。感谢您的见解和帮助我。
  • 没问题。如果问题得到解决,请随时接受答案。
【解决方案2】:

我会按照以下方式做一些事情:

num_to_find = int(input("Enter the number to be found"))

a=[2,20,30]

def occur1(a, num_to_find):
  for i in a:
    if not i <= num_to_find:
      return a.index(i)
  return "The entered number is greater than the numbers in the list"

print(occur1(a, num_to_find))

这给出了1 的输出(输入 3 时)。

之所以给你 2 个输出,是因为你的代码中有 2 个 print 语句。

【讨论】:

  • 哦,如果您担心复杂性,我建议您使用 @ggorlen 的出色答案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多