【问题标题】:Insert element into numpy array and get all rolled permutations将元素插入numpy数组并获取所有滚动排列
【发布时间】:2019-02-12 01:34:51
【问题描述】:

有没有更好的方法将数组中的元素一个一个地插入
到所有可能的位置(n+1 个位置)

例如,将 [1] 插入到 [6 7 8 9] 应该会产生:

[1 6 7 8 9]
[9 1 6 7 8]
[8 9 1 6 7]
[7 8 9 1 6]
[6 7 8 9 1]

所以如果我将A = [1 2 3] 一个一个插入到 B = [6 7 8 9] 它应该产生:

[1 6 7 8 9]
[9 1 6 7 8]
[8 9 1 6 7]
[7 8 9 1 6]
[6 7 8 9 1]
--------------------
[2 6 7 8 9]
[9 2 6 7 8]
[8 9 2 6 7]
[7 8 9 2 6]
[6 7 8 9 2]
--------------------
[3 6 7 8 9]
[9 3 6 7 8]
[8 9 3 6 7]
[7 8 9 3 6]
[6 7 8 9 3]
--------------------

目前我像这样使用numpy.roll

import numpy as np
import timeit

A = np.array([1, 2, 3, 4, 5])
B = np.array([6, 7, 8, 9])

def inject_one(Ad, Bd):
    for i, _ in enumerate(Ad):
        C = np.append(Ad[i], Bd)
        for _ in range(len(C) - 1):
            C = np.roll(C, 1)

t = timeit.Timer(lambda: inject_one(A, B))
print("{:.3f}secs for 1000 iterations".format(t.timeit(number=1000))) 
# > 0.160 secs

【问题讨论】:

  • np.append 只是np.concatenate 的前端;它不是append 克隆列表。因此,在循环中重复应用时效率不高。
  • 如果你的问题是滚动列表,所以最后一个数字在前,你可以写 outa = list[-1]+list[:-1]
  • 你不是在每个可能的位置插入,而是在开头插入然后滚动你的列表。
  • @user3483203 是的,没错……但我会做任何更快的事情。
  • @JonasWolff 你的解决方案很好,为什么要删除它?

标签: python python-3.x algorithm numpy matrix


【解决方案1】:

2D案例

我们可以利用基于np.lib.stride_tricks.as_stridedscikit-image's view_as_windows 来获得滑动窗口。 More info on use of as_strided based view_as_windows.

这个想法是用1D 数组的两个副本以及要添加的新值(在本例中为1)填充,然后将2D 视图放入其中。作为一个视图,它会非常有效并且看起来像这样 -

from skimage.util.shape import view_as_windows

def rolling_add(a,val=1):
    a_ext = np.r_[a,val,a]
    return view_as_windows(a_ext,len(a)+1,1)[::-1]

我们可以通过直接使用np.lib.stride_tricks.as_strided 来获得一些边际改进,以避免出现翻转部分:[::-1],但读者可能难以理解该设置。

示例运行 -

In [254]: a = np.array([6, 7, 8, 9])

In [255]: rolling_add(a)
Out[255]: 
array([[1, 6, 7, 8, 9],
       [9, 1, 6, 7, 8],
       [8, 9, 1, 6, 7],
       [7, 8, 9, 1, 6],
       [6, 7, 8, 9, 1]])

非常大的阵列上的时间(也展示效率部分)-

In [263]: a = np.random.randint(0,10,10000)

In [264]: %timeit rolling_add(a)
10000 loops, best of 3: 58 µs per loop

3D案例

扩展到3D 需要一些额外的步骤,但这是值得的,因为我们仍然可以将输出保留为视图,因此在时间上几乎是免费的(再次向南!)-

def rolling_add3D(a,add_ar):
    a_ext = np.r_[a,0,a]
    a_ext2 = np.repeat(a_ext[None],len(add_ar),0)
    a_ext2[:,len(a)] = add_ar
    return view_as_windows(a_ext2,(1,len(a)+1))[...,0,:][:,::-1]

示例运行 -

In [292]: a
Out[292]: array([6, 7, 8, 9])

In [293]: rolling_add3D(a,[1,2,3])
Out[293]: 
array([[[1, 6, 7, 8, 9],
        [9, 1, 6, 7, 8],
        [8, 9, 1, 6, 7],
        [7, 8, 9, 1, 6],
        [6, 7, 8, 9, 1]],

       [[2, 6, 7, 8, 9],
        [9, 2, 6, 7, 8],
        [8, 9, 2, 6, 7],
        [7, 8, 9, 2, 6],
        [6, 7, 8, 9, 2]],

       [[3, 6, 7, 8, 9],
        [9, 3, 6, 7, 8],
        [8, 9, 3, 6, 7],
        [7, 8, 9, 3, 6],
        [6, 7, 8, 9, 3]]])

非常大的数组再次计时 -

In [294]: a = np.random.randint(0,10,10000)

In [295]: %timeit rolling_add3D(a,[1,2,3])
10000 loops, best of 3: 83.7 µs per loop

性能将与要添加的数组长度成正比。因此,将 1000 元素数组添加到 10000 长度输入数组将是 -

In [301]: a = np.random.randint(0,10,10000)

In [302]: add_array = np.random.randint(0,10,1000)

In [303]: %timeit rolling_add3D(a,add_array)
100 loops, best of 3: 16.9 ms per loop

【讨论】:

    【解决方案2】:

    您在这里要求的是Toeplitz Matrix,即:

    一个矩阵,其中从左到右的每个下降对角线都是常数

    幸运的是,scipy 有一个易于使用的实现:

    from scipy.linalg import toeplitz
    
    def magic_toeplitz(arr, to_add):
        return toeplitz(np.hstack([to_add, arr[::-1]]), np.hstack([to_add, arr]))
    
    a = [6,7,8,9]
    add = [1]
    magic_toeplitz(a, add)
    

    array([[1, 6, 7, 8, 9],
           [9, 1, 6, 7, 8],
           [8, 9, 1, 6, 7],
           [7, 8, 9, 1, 6],
           [6, 7, 8, 9, 1]])
    

    缩放此解决方案的矢量化方式:

    A = np.array([1, 2, 3, 4, 5])
    B = np.array([6, 7, 8, 9])
    
    out = toeplitz(np.hstack([[np.nan], B[::-1]]), np.hstack([np.nan, B]))
    out = np.tile(out, (len(A), 1, 1))
    m = np.ma.array(out, mask=np.isnan(out))
    vals = np.repeat(A, (B.shape[0] + 1)**2).reshape(out.shape)
    print(m.filled(vals))
    

    array([[[1, 6, 7, 8, 9],
            [9, 1, 6, 7, 8],
            [8, 9, 1, 6, 7],
            [7, 8, 9, 1, 6],
            [6, 7, 8, 9, 1]],
    
           [[2, 6, 7, 8, 9],
            [9, 2, 6, 7, 8],
            [8, 9, 2, 6, 7],
            [7, 8, 9, 2, 6],
            [6, 7, 8, 9, 2]],
    
           [[3, 6, 7, 8, 9],
            [9, 3, 6, 7, 8],
            [8, 9, 3, 6, 7],
            [7, 8, 9, 3, 6],
            [6, 7, 8, 9, 3]],
    
           [[4, 6, 7, 8, 9],
            [9, 4, 6, 7, 8],
            [8, 9, 4, 6, 7],
            [7, 8, 9, 4, 6],
            [6, 7, 8, 9, 4]],
    
           [[5, 6, 7, 8, 9],
            [9, 5, 6, 7, 8],
            [8, 9, 5, 6, 7],
            [7, 8, 9, 5, 6],
            [6, 7, 8, 9, 5]]])
    

    【讨论】:

      【解决方案3】:
      for i in A:
         new_list = B[:]
         new_list.append(i)
         print(new_list)
         for q in B:
              new_list = [new_list[-1]]+new_list[:-1]
              print(new_list)
         print("-"*15)
      

      【讨论】:

        猜你喜欢
        • 2017-05-03
        • 2022-12-21
        • 2020-11-19
        • 1970-01-01
        • 2020-08-12
        • 2014-07-01
        • 1970-01-01
        • 2013-06-26
        • 2020-12-20
        相关资源
        最近更新 更多