【问题标题】:test if list contains a number in some range测试列表是否包含某个范围内的数字
【发布时间】:2017-10-21 21:31:00
【问题描述】:

假设我有一个列表L=[1.1, 1.8, 4.4, 5.2]。对于某个整数n,我想知道L 是否具有valn-1<val<n+1 的值,如果是,我想知道val 的索引。

到目前为止我能做的最好的事情就是定义一个生成器

x = (index for index,val in enumerate(L) if n-1<val<n+1)

并使用try... except 检查它是否具有适当的值。所以让我们假设我正在寻找最小的 n>=0 存在这样的值......

L=[1.1, 1.8, 4.4, 5.2]
n=0
while True:
    x = (index for index,val in enumerate(L) if n-1<val<n+1)
    try:
        index=next(x)
        break
    except StopIteration:
        n+=1
print n,index

1 0

实际上,我正在做一项更复杂的任务。我希望能够取一个 n,找到第一个索引,如果它不存在,我需要做其他事情。

这对我来说似乎不是特别干净的代码。有没有更好的办法?我觉得 numpy 可能有答案,但我不太清楚。

【问题讨论】:

  • 不这么认为——想先大于n-1,小于n+1,如果不存在怎么处理(快点)
  • 如果该项目不存在,您的期望不是很清楚
  • 如果该项目不存在,在我的示例中,我只需将 n 增加 1。更一般地说,我想获取索引,或者如果它不存在我想执行其他一些操作。
  • 但是 n 是一个输入,你为什么要把它作为一个输出呢?无论如何,您可以使用next((index for index,val in enumerate(L) if n-1&lt;val&lt;n+1), n + 1)第二个参数来获取默认值

标签: python numpy


【解决方案1】:

如果 L 已排序,您可以使用 bisect.bisect_left 查找所有 L[= i] 的索引 i。

然后

if n - L[i-1] < 1.0:
    val = L[i-1]
elif L[i] - n < 1.0:
    val = L[i]
else:
    val = None     # no such value found

编辑:根据您的数据、您想要完成的任务以及您想要花费多少时间编写一个聪明的算法,排序可能会也可能不会为您提供良好的解决方案;在我看到更多的 O(n) 挥手之前,我想指出他的实际问题似乎涉及反复探索 n 的各种值——这将很快分摊初始排序开销——他建议上面的算法其实是O(n**2)。

@AntoinePelisse:无论如何,让我们做一些分析:

from bisect import bisect_left, bisect_right
from functools import partial
import matplotlib.pyplot as plt
from random import randint, uniform
from timeit import timeit

#blues    
density_col_lin = [
    (0.000, 0.502, 0.000, 1.000),
    (0.176, 0.176, 0.600, 1.000),
    (0.357, 0.357, 0.698, 1.000),
    (0.537, 0.537, 0.800, 1.000)
]

# greens
density_col_sor = [
    (0.000, 0.502, 0.000, 1.000),
    (0.176, 0.600, 0.176, 1.000),
    (0.357, 0.698, 0.357, 1.000),
    (0.537, 0.800, 0.537, 1.000)
]

def make_data(length, density):
    max_ = length / density
    return [uniform(0.0, max_) for _ in range(length)], max_

def linear_probe(L, max_, probes):
    for p in range(probes):
        n = randint(0, int(max_))
        for index,val in enumerate(L):
            if n - 1.0 < val < n + 1.0:
                # return index
                break

def sorted_probe(L, max_, probes):
    # initial sort
    sL = sorted((val,index) for index,val in enumerate(L))
    for p in range(probes):
        n = randint(0, int(max_))
        left  = bisect_right(sL, (n - 1.0, max_))
        right = bisect_left (sL, (n + 1.0, 0.0 ), left)
        if left < right:
            index = min(sL[left:right], key=lambda s:s[1])[1]
            # return index

def main():
    densities = [0.8, 0.2, 0.08, 0.02]
    probes    = [1, 3, 10, 30, 100]
    lengths   = [[]                   for d in densities]
    lin_pts   = [[[] for p in probes] for d in densities]
    sor_pts   = [[[] for p in probes] for d in densities]

    # time each function at various data lengths, densities, and probe repetitions
    for d,density in enumerate(densities):
        for trial in range(200):
            print("{}-{}".format(density, trial))

             # length in 10 to 5000, with log density
            length = int(10 ** uniform(1.0, 3.699))
            L, max_ = make_data(length, density)
            lengths[d].append(length)

            for p,probe in enumerate(probes):
                lin = timeit(partial(linear_probe, L, max_, probe), number=5) / 5
                sor = timeit(partial(sorted_probe, L, max_, probe), number=5) / 5
                lin_pts[d][p].append(lin / probe)
                sor_pts[d][p].append(sor / probe)

    # plot the results
    plt.figure(figsize=(9.,6.))
    plt.axis([0, 5000, 0, 0.004])

    for d,density in enumerate(densities):
        xs = lengths[d]
        lcol = density_col_lin[d]
        scol = density_col_sor[d]

        for p,probe in enumerate(probes):
            plt.plot(xs, lin_pts[d][p], "o", color=lcol, markersize=4.0)
            plt.plot(xs, sor_pts[d][p], "o", color=scol, markersize=4.0)

    plt.show()

if __name__ == "__main__":
    main()

导致

x 轴是 L 中的项目数,y 轴是每个探针的摊销时间;绿点是 sorted_probe(),蓝点是 linear_probe()。

结论:

  • 这两个函数的运行时间与长度呈显着线性关系
  • 对于 L 的单次探测,预排序比迭代慢大约 4 倍
  • 交叉点似乎是大约 5 个探针;比这更少,线性搜索更快,更多,预排序更快。

【讨论】:

  • 我不能假设已排序。
  • @Joel:那么要么对它进行排序,要么你必须比较每个列表项。
  • 有趣的是,您会建议在 O(n) 之前使用 O(n lg n) 解决方案
  • @AntoinePelisse:取决于相对常数和他的列表的长度,是的,“慢”方法可能会更快。
  • @HughBothwell 你能举个例子吗?我会很好奇调查一个?排序意味着您必须从列表中读取所有值,而一个接一个地进行缓存优化,并且大多数情况下甚至不会进行完整读取。
【解决方案2】:

这是一个不依赖try...except 并且相对容易阅读的解决方案。因此它对我来说“感觉更干净”,但这总是带有主观性的元素。

def where_within_range( sequence, lower, upper ):
    for index, value in enumerate( sequence ):
        if lower < value < upper: return index

L = [ 1.1, 1.8, 4.4, 5.2 ]

import itertools
for n in itertools.count():
    index = where_within_range( L, n - 1, n + 1 )
    if index != None: break

print n, index

如果您希望避免重复的函数调用开销,您可以改为按如下方式进行,它再次使用StopIteration 异常,但使用itertools.countreturn 语句,(再次, “不知何故”)最终对我来说似乎更干净。也许是因为在try...except... 子句的每一部分中只有一个语句(诚然,这种感觉没有太多合理的基础)。

import itertools
def find_joel_root( sequence ):
    for n in itertools.count():
        solutions = ( index for index, value in enumerate( sequence ) if n - 1 < value < n + 1 )
        try: return n, next( solutions )
        except StopIteration: pass

L = [ 1.1, 1.8, 4.4, 5.2 ]
n, index = find_joel_root( L )
print n, index

【讨论】:

    【解决方案3】:

    我有一个有趣的想法,通过使用defaultdict 并使用值(n-1)(n+1) 构建索引,它将需要循环一次列表,然后只需比较键/值,如下所示:

    from collections import defaultdict
    
    L = [1.1, 1.8, 4.4, 5.2]
    
    x = defaultdict(dict)
    for idx, item in enumerate(L):
        x[int(item)] = {int(item-1): item-1, int(item+1): item+1, 'index':idx}
    

    用法:

    n = 5
    
    x[n].get(n-1) < n < x[n].get(n+1) and x[n]['index']
    Out[8]: 3
    
    n = 2
    
    x[n].get(n-1) < n < x[n].get(n+1) and x[n]['index']
    Out[10]: False
    

    解释:

    好像:

    1) TrueIndex 返回 Index

    2) FalseIndex 将返回 False

    因为您要输入n 作为整数,如果第一部分是True,它将返回第二部分index 值。如果第一部分失败,则返回False

    这将返回nLAST 出现,如果您需要nFIRST 出现,只需颠倒列表和索引:

    ...
    l = len(L)
    for idx, item in enumerate(reversed(L)):
        x[int(item)] = {int(item-1): item-1, 
                        int(item+1): item+1, 
                        'index': l-idx-1}
    ...
    

    【讨论】:

      【解决方案4】:

      l 可以是列表或 numpy 数组:

      next(((i,v) for i,v in enumerate(l) if n-1<v<n+1))
      

      使用生成器并在第一个值处停止。

      【讨论】:

        【解决方案5】:

        现在,当我想时,我终于明白了你的任务: 只需找到数组中的最小值和它的索引 - n 将等于单元格(最小值)。 甚至更简单:

        n,index = int(min(L)),L.index(min(L))
        

        【讨论】:

        • @Joel 你现在可以看看我的代码吗?我认为这会很好
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-27
        • 2023-04-09
        • 2016-05-07
        相关资源
        最近更新 更多