Python 在满足条件的列表中计算元素的最快方法

Python 在满足条件的列表中计算元素的最快方法,python,Python,我想得到满足另一个列表指定的某些条件的列表中元素的数量。我的方法是使用sum和any。简单的测试代码是: >>> x1 = list(xrange(300)) >>> x2 = [random.randrange(20, 50) for i in xrange(30)] >>> def test(): ns = [] for i in xrange(10000): ns.append(s

我想得到满足另一个列表指定的某些条件的列表中元素的数量。我的方法是使用
sum
any
。简单的测试代码是:

>>> x1 = list(xrange(300))
>>> x2 = [random.randrange(20, 50) for i in xrange(30)]
>>> def test():
        ns = []
        for i in xrange(10000):
            ns.append(sum(1 for j in x2 if any(abs(k-j)<=10 for k in x1)))
        return ns
函数
test
仅包含10000次迭代。一般来说,我会有数万次迭代,并且使用
cProfile.run
显示此块会导致大部分执行时间

===================================================================

编辑

根据答案,使用二进制搜索

from _bisect import *
>>> x1 = list(xrange(300))
>>> x2 = [random.randrange(20, 50) for i in xrange(30)]
>>> def testx():
        ns = []
        x2k = sorted(x2)
        x1k = sorted(x1)
        for i in xrange(10000):
            bx = [bisect_left(x1k, xk) for xk in x2k]
            rn = sum(1 if k==0 and x1k[k]-xk<=10
                 else 1 if k==len(x1k) and xk-x1k[k-1]<=10
                 else xk-x1k[k-1]<=10 or x1k[k]-xk<=10
                 for k, xk in zip(bx, x2k))
            ns.append(rn)
        return ns
从_对分导入*
>>>x1=列表(X范围(300))
>>>x2=[随机.随机范围(20,50)对于X范围(30)中的i]
>>>def testx():
ns=[]
x2k=已排序(x2)
x1k=已排序(x1)
对于X范围内的i(10000):
bx=[x2k中xk的左对分(x1k,xk)]

rn=sum(如果k==0和x1k[k]-xk谓词的性质是至关重要的;因为它是一条直线上的距离,所以可以为数据提供相应的结构以加快搜索速度。有几种变体:

对列表进行排序
x1
:然后可以使用二进制搜索查找最近的值,并检查它们是否足够接近

如果列表
x2
长得多,并且它的大多数元素不在范围内,则可以通过对其进行排序并搜索每个可接受间隔的开始和结束来加快它的速度

如果你对两个列表进行排序,你可以一步一步地对它们进行排序,并在线性时间内完成。这是渐近等价的,当然,除非有其他原因对它们进行排序。

代码 使用数据结构。适合您需要的非常简单的实现可以如下所示:

class SimpleIntervalTree:
    def __init__(self, points, radius):
        intervals = []
        l, r = None, None
        for p in sorted(points):
            if r is None or r < p - radius:
                if r is not None:
                    intervals.append((l, r))
                l = p - radius
            r = p + radius
        if r is not None:
            intervals.append((l, r))
        self._tree = self._to_tree(intervals)

    def _to_tree(self, intervals):
        if len(intervals) == 0:
            return None
        i = len(intervals) // 2
        return {
            'left': self._to_tree(intervals[0:i]),
            'value': intervals[i],
            'right': self._to_tree(intervals[i + 1:])
        }

    def __contains__(self, item):
        t = self._tree
        while t is not None:
            l, r = t['value']
            if item < l:
                t = t['left']
            elif item > r:
                t = t['right']
            else:
                return True
        return False
代码的作用是什么
\uuuuu init\uuuuuuu
中,首先将点列表转换为连续间隔列表。然后,将间隔放入平衡树中。在该树中,每个节点包含一个间隔,节点的每个左子树包含较低的间隔,节点的每个右子树包含较高的间隔。这样,每当我们想要测试一个点位于任何段中(
\uuuuuu包含\uuuuu
),我们从根开始执行二进制搜索。

我认为您没有正确解释结果。从您引用的手册页面上看,“cumtime”包括花费在子函数调用上的时间,因此说“sum”是花费时间最多的调用之一是不合理的。对于您提出的问题,我建议使用“tottime”作为更好的选择tric,因此“any”和“abs”是前两位用户。在这种情况下,使用
abs()
有什么好处?尝试使用列表理解来构建
ns
-这通常比重复
append()更快
方法调用。@Rolf这只是一个简单的例子。实际上,对于
x2
中的任何元素,我想使用绝对误差来确定
x1
中的任何元素在这两个元素之间的差值是否小于此误差。然后计算
x2
中回答为“是”的元素数。实际的列表元素是sc吗Alar还是vectors?谢谢。已经取得了显著的改进。我已经编辑了我的问题,以包括通过使用实现您的建议。但是我必须在
x2
中迭代两次,并使用几个
if…else..
来检查通过
bisect\u left
获得的索引。是否进一步优化搜索/代码存在吗?@Elkan:你可以先写
sum(k>0和xk-x1k[k-1]
x1 = list(range(300))
x2 = [random.randrange(20, 50) for i in range(30)]
it = SimpleIntervalTree(x1, 10)
def test():
    ns = []
    for i in range(10000):
        ns.append(sum(1 for j in x2 if j in it))
    return ns