2017-02-24 44 views
1

我正在嘗試改進我正在處理的算法的處理速度。在嘗試使用多處理池和映射在所有CPU核心上有效地分配工作負載之前,我想要矢量化(如果可能的話)此循環。我該如何使用numpy進行矢量化,而+(2x for)嵌套循環?

這裏是一個例子。

v = [1,2,3,4,5,6,7,8,9,10] 
w = [-3,-2,-1,0,1,2,3] 
u = sorted(w, reverse=True) 
i = 0 
check = 0 

while v[i] != v[-1]: 
    if check == 0: 
     for k in range(len(w)): 
      if (v[i] < w[k] & v[i+1] >= w[k]) or (v[i] > w[k] & v[i+1] <= w[k]): 
       do_somthing() 
       check = 1 
       break 
    i = i+1 
    if check == 1: 
     for k in range(len(u)): 
      if (v[i] <= u[k] & v[i-1] > u[k]) or (v[i] >= u[k] & v[i-1] < u[k]): 
       do_something_else() 
       check = 0 
       break 
    i = i+1  

該示例中的數組值完全是隨機的。 V至少包含2000個元素,而w的大小始終是固定的。

+0

是'w'排序,這樣'u'和'w'互爲鏡像? –

+0

是的,w排序爲lowest_value - > highest_value。 – ilpomo

+0

爲什麼不讓這個函數接受'ndarray''v'和'w'參數。然後用'numba.jit'編譯函數。對於可以不依賴本地CPython數據類型的特殊循環操作,'numba.jit'幾乎總是您最好的選擇,並且通常比矢量化的numpy版本更快,同時允許您仍然可以在一種可讀的方式,不需要混淆的矢量操作。 – ely

回答

1

這是一種嘗試。我觀察到,在第一和第二for塊的條件是相同的,只有一個環挑選滿足它的最低w,其它採最高w

能否請您檢查是否follwing給出正確的結果?

import numpy as n 

assert len(v) % 2 == 0 
sg = w[-1] + 1 # create numbers that are safely out of range 
sl = w[0] - 1 # 

# in the original code, do_something is executed at most once for 
# every pair of v depending on some conditions relating to w. 
# the next lines find the smallest w that is still larger than v and 
# similar things (in other words we are binning v with respect to w), 
# in a vectorised fashion, i.e. for all v in one go. 
# using this info we can avoid the loop and for each pair of v just 
# pick the first w if any that would have left the two v through in 
# the if inside the loop 
# the difference between bin_inds_left and _right is whether the bins 
# are l <= bin < r or l < bin <= r 
bin_inds_left = np.digitize(v, w) 
bin_inds_right = np.digitize(v, w, True) 

# in your loops, various conditions are applied to v[i] and v[i+1] 
# or similarly, in vectorised code this translates to slice offsets 
# the two following lines create the logical masks corresponding to 
# the conditions in the left and right halves of the if statement 
# in the first loop (IIRC they are swapped in the second loop) 
# these masks are True if there is any permissible w and otherwise 
# False 
mask_l = bin_inds_left[1::2] > bin_inds_left[::2] 
mask_r = bin_inds_right[1::2] < bin_inds_right[::2] 

mask = mask_l | mask_r 

# for each pair find the smallest w that satisfies the left condition 
k1 = bin_inds_left[::2][mask] 
k1[~mask_l[mask]] = sg # this marks places where there is no such w 

# and in the right condition 
k2 = bin_inds_right[1::2][mask] 
k2[~mask_r[mask]] = sg 

# since it is or gated the first (smaller) of the two w's wins 
kminw = np.minimum(k1, k2) 

# same for the second loop 
k1 = bin_inds_left[1::2][mask] 
k1[~mask_l[mask]] = sl 

k2 = bin_inds_right[::2][mask] 
k2[~mask_r[mask]] = sl 

# but since u is upside-down compared to w and we do all caluclations 
# in w coordinates we must take the maximum 
# and in the very last step we translate to u coordinates 
kminu = len(w) - 1 - np.maximum(k1, k2) 

do_something(kminw, 2*np.where(mask)[0]) 
do_something_else(kminu, 1 + 2*np.where(mask)[0]) 

說明:我們使用np.digitize找到指數smalles /最大w滿足一氣呵成所有v的各種不平等。這給了一對夫婦,我們共同決定爲其v, wdo_somethingdo_something_else需要執行的口罩。在最後兩行的參數是索引到W和V。

+0

感謝您的快速回答。我從頭開始重新整理整個算法,所以我仍然必須重寫do_something()和do_something_else()函數以及其他許多事情。我會盡快通知你,謝謝你! – ilpomo

+0

我得到了兩次相同的「TypeError:只有長度爲1的數組可以轉換爲Python標量」。第一次kminw = np.min(k1,k2),第二次kminu = len(w)-1 -np.max(k1,k2)。 我讀關於您所使用的模塊和功能numpy的文檔,但很顯然,我不是爲你熟練。你能提供一行一行的解釋或你的代碼做什麼?誠摯地說,我幾乎沒有任何關於它的事情。再次感謝你。 – ilpomo

+0

糟糕,使用錯誤的'max',(有'max'和'maximum')對此感到抱歉。我會看看我能做些什麼來解釋更好。 –