Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/320.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 将已排序的项目搜索到已排序的序列中_Python_Performance_Numpy_Big O_Binary Search - Fatal编程技术网

Python 将已排序的项目搜索到已排序的序列中

Python 将已排序的项目搜索到已排序的序列中,python,performance,numpy,big-o,binary-search,Python,Performance,Numpy,Big O,Binary Search,我想在排序的值数组中查找一系列项。 我知道有了numpy我可以做到: l = np.searchsorted(values, items) 这具有O(len(项)*log(len(值))的复杂性。 但是,我的项目也会被排序,所以我可以在O(len(项目)+len(值))中进行排序,这样做: l=np.zero(items.size,dtype=np.int32) k、 k=0,len(值) 对于范围内的i(len(items)): 而k

我想在排序的值数组中查找一系列项。 我知道有了numpy我可以做到:

l = np.searchsorted(values, items)
这具有O(len(项)*log(len(值))的复杂性。 但是,我的项目也会被排序,所以我可以在O(len(项目)+len(值))中进行排序,这样做:

l=np.zero(items.size,dtype=np.int32)
k、 k=0,len(值)
对于范围内的i(len(items)):
而k
问题是纯python中的这个版本比searchsorted慢得多,因为python循环,即使对于大的len(项)和len(值)(~10^6)

知道如何用numpy“矢量化”这个循环吗?

一些示例数据:

# some example data
np.random.seed(0)
n_values = 1000000
n_items = 100000
values = np.random.rand(n_values)
items = np.random.rand(n_items)
values.sort()
items.sort()
您的原始代码片段以及@PeterE建议的实现:

def original(values, items):
    l = np.empty(items.size, dtype=np.int32)
    k, K = 0, len(values)
    for i, item in enumerate(items):
        while k < K and values[k] < item:
            k += 1
        l[i] = k
    return l

def peter_e(values, items):
    l = np.empty(items.size, dtype=np.int32)
    last_idx = 0
    for i, item in enumerate(items):
        last_idx += values[last_idx:].searchsorted(item)
        l[i] = last_idx
    return l
时间:

In [1]: %timeit original(values, items)
10 loops, best of 3: 115 ms per loop

In [2]: %timeit peter_e(values, items)
10 loops, best of 3: 79.8 ms per loop

In [3]: %timeit values.searchsorted(items)
100 loops, best of 3: 4.09 ms per loop
因此,对于这种大小的输入,天真地使用
np.searchsorted
可以轻松地击败您的原始代码以及PeterE的建议

更新 为了避免任何可能扭曲计时的缓存效果,我们可以为基准的每次迭代生成一组新的随机输入数组:

In [1]: %%timeit values = np.random.randn(n_values); items = np.random.randn(n_items); values.sort(); items.sort();
original(values, items)
   .....: 
10 loops, best of 3: 115 ms per loop

In [2]: %%timeit values = np.random.randn(n_values); items = np.random.randn(n_items); values.sort(); items.sort();
peter_e(values, items)
   .....: 
10 loops, best of 3: 79.9 ms per loop

In [3]: %%timeit values = np.random.randn(n_values); items = np.random.randn(n_items); values.sort(); items.sort();
values.searchsorted(items)
   .....: 
100 loops, best of 3: 4.08 ms per loop
更新2 编写一个Cython函数来击败
np并不难。对于
值和
项都排序的情况,searchsorted

search\u doubly\u sorted.pyx

import numpy as np
cimport numpy as np
cimport cython

@cython.boundscheck(False)
@cython.wraparound(False)
def search_doubly_sorted(values, items):

    cdef:
        double[:] _values = values.astype(np.double)
        double[:] _items = items.astype(np.double)
        long n_items = items.shape[0]
        long n_values = values.shape[0]
        long[:] out = np.empty(n_items, dtype=np.int64)
        long ii, jj, last_idx

    last_idx = 0
    for ii in range(n_items):
        for jj in range(last_idx, n_values):
             if _items[ii] <= _values[jj]:
                break
        last_idx = jj
        out[ii] = last_idx

    return out.base
基准:

In [3]: %timeit values.searchsorted(items)
100 loops, best of 3: 4.07 ms per loop

In [4]: %timeit search_doubly_sorted(values, items)
1000 loops, best of 3: 1.44 ms per loop

不过,性能的改进相当有限。除非这是代码中的一个严重瓶颈,否则您可能应该坚持使用
np。searchsorted

在从上一个找到的索引到末尾切片值时,您不能对项目中的每个项目使用
searchsorted
?如果值中不存在项[i],那么您的方法是否可以正常工作?我认为这将表明第一个值大于items[I],即使你将其矢量化,你仍然会在python的土地上。。。numpy会把你轰出监狱water@JoranBeasley“矢量化”是指使用numpy原语。@PeterE建议的方法已经内置到numpy 1.9中,是相关的PR。它不应该是
l[I]=l[I-1]+值[l[I-1]:]。searchsorted(item)
?你必须从最后找到的索引开始切片。另外:你能不能简单地使用
值对它进行计时(或者验证)。searchsort(items)
?感谢这些计时,但我认为要看到显著的差异,数组的大小应该更大(然后np.searchsorted基线非常快)。我的问题是python开销(隐藏在big-o的常量因子中)确实很大。@PeterE我的版本在没有关系的情况下是等效的,但你的版本更健壮。我会更新我的代码。我已经分析了,这是瓶颈。我使用的是非常大的阵列,所以在我的例子中,大o复杂性不仅仅是理论上的。一个cythonized版本确实要快得多。
import numpy as np
cimport numpy as np
cimport cython

@cython.boundscheck(False)
@cython.wraparound(False)
def search_doubly_sorted(values, items):

    cdef:
        double[:] _values = values.astype(np.double)
        double[:] _items = items.astype(np.double)
        long n_items = items.shape[0]
        long n_values = values.shape[0]
        long[:] out = np.empty(n_items, dtype=np.int64)
        long ii, jj, last_idx

    last_idx = 0
    for ii in range(n_items):
        for jj in range(last_idx, n_values):
             if _items[ii] <= _values[jj]:
                break
        last_idx = jj
        out[ii] = last_idx

    return out.base
In [1]: from search_doubly_sorted import search_doubly_sorted

In [2]: print(all(search_doubly_sorted(values, items) == values.searchsorted(items)))                     
# True
In [3]: %timeit values.searchsorted(items)
100 loops, best of 3: 4.07 ms per loop

In [4]: %timeit search_doubly_sorted(values, items)
1000 loops, best of 3: 1.44 ms per loop