【问题标题】:How can I select elements lesser than a given integer, from a sorted list?如何从排序列表中选择小于给定整数的元素?
【发布时间】:2015-04-30 02:16:02
【问题描述】:

我有一系列素数,例如介于 0 到 1000 之间的整数

primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997]

我得到输入

n = int(input())

在数组的最后一个元素小于n的情况下,将数组切片到新数组的最有效方法是什么?

【问题讨论】:

    标签: python list python-3.x


    【解决方案1】:

    你可以利用primes已经排序的事实,用bisect,像这样

    >>> from bisect import bisect
    >>> primes[:bisect(primes, n)]
    

    bisect 对输入列表进行二分查找并返回小于n 的元素的索引。

    【讨论】:

    • 是的,二等分需要 O(log N) 时间。然而,切片需要 O(N) 时间,就像简单的解决方案一样。它只是平均 N/2 而不是 N,而且更重要的是,它有一个令人眼花缭乱的快速乘数,因为列表切片主要只是一个 memmove……但它仍然是 O(N),而不是 O(log N)。如果不使用某种视图类而不是切片,你就无法击败它。
    • @abamert primes[bisect(primes, n):]=[] 可能可以做更少的工作,但我对实现的了解不够确定
    • @tobyodavies:好点。我也不确定,但如果这真的是某人的瓶颈,那值得测试......
    • 我认为使用一些领域知识可以更快!考虑返回素数 n/log(n) 就是这样一种估计器,还有更复杂的估计器。
    • @wim:好点。但是您仍然只是将 O(log N) 部分(二等分)优化为接近常数,同时仍然使 O(N) 部分保持线性。实际上这可能是值得的(同样,这非常快 O(N)...),但我个人不会浪费时间尝试,除非我有一个重要的真实测试用例。
    【解决方案2】:

    你有一个list,而不是一个数组。如果您确实需要对列表进行切片并构建一个新列表,那么无论您如何操作,这都将花费线性时间。 The answer by thefourtheye 可能是你最好的选择:

    small_primes = primes[:bisect.bisect(primes, n)]
    

    如果您有 NumPy,它知道如何创建 看起来 像切片但实际上引用而不是复制数据的视图。事实上,如果primesndarray,您可以使用与thefourtheye 的答案完全相同的代码,结果为O(log N)。

    small_primes = primes[:bisect.bisect(primes, n)]
    

    如果您只需要迭代“数组”一次而不是将其用作列表,则可以使用惰性迭代器:

    small_primes = itertools.takewhile(lambda p: p<n, primes)
    

    现在前期时间成本为0;但是在您使用它时,每个值都有一个比较。当然,它比其他任何东西都更节省空间。

    实际上,这里的“最有效”不太重要,如果它确实重要,您需要对其进行衡量,如果您不使用 NumPy 或在 PyPy 下运行代码,您几乎肯定想要执行以下操作之一在任何微优化之前的那些......

    【讨论】:

      【解决方案3】:

      你没有解释为什么你要求“最有效的方式”,或者你的意思是什么(时间?空间?别的什么?),我非常怀疑这真的是一个值得在任何地方优化的性能瓶颈,但如果是:

      您只有 168 个元素。而且您不会有很多类似的列表,因此空间不太可能相关。同时,对于一个超过 N=168 的线性算法来说,你必须调用它无数次——但只有 1000 个可能的值。所以,只需预先创建一个表:

      prime_slices = [[prime for prime in primes if prime < n] for n in range(1000)]
      

      现在,让素数达到n

      prime_slices[n]
      

      那是恒定的时间。当然,设置需要 O(N^2) 时间,但这没什么,因为它只发生一次,而节省 O(N) 工作无数次。它需要 O(N^2) 空间,但实际上这只是一个恒定的 15K 左右的指针,您几乎肯定可以负担得起。

      【讨论】:

      • 这确实是我的想法。只有 168 个值?滥用内存! +1
      • 我们也可以在外部列表中有多个索引指向同一个内部列表。那么您将只有 168 个左右的内部列表和 1000 个映射到这些内部列表的索引。这样会更有效率。
      • 那是作弊。 n = 5321573289143782914738291437218490372184 的休息时间。:)
      • @Shashank:是的,我真的应该添加一些东西来明确地“实习”列表而不是构建每个列表约 7 次......但我想把它写成一个明显的 1-liner。跨度>
      • @wim:好的,所以def prime_slices(n):\n\ttry: return _prime_slices[n]\n\texcept IndexError: return prime_slices[-1]\n。 :) 但老实说,我认为“我有一个不超过 1000 的素数列表”和“我想让素数达到任意 n”可能应该 引发 n>1000 的异常。否则,它会返回误导性的答案……
      【解决方案4】:

      如果您不再需要完整的素数列表,截断素数可能是最快的方法:

      primes[bisect(primes, n):]=[]
      

      但与往常一样,如果您关心性能,请对其进行衡量。

      【讨论】:

      • 当然这实际上并不是“将数组切片到新数组”......但是OP首先没有“数组”,所以我不太担心遵循说明信中……
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-01-30
      • 2019-05-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多