【问题标题】:How to find every walk in a numpy array如何在一个numpy数组中找到每一个walk
【发布时间】:2016-03-14 00:23:39
【问题描述】:

我试图通过一个数组找到长度为 n 的每一个“步行”。在这种情况下,游走被定义为数组中相邻元素(水平、对角线或垂直)的长度为 n 的序列,以便重复该点。例如,一个 2x2 矩阵

[1 2]
[4 8]

会有长度为 2 的步行:(1, 2), (1, 4), (1, 8), (2, 1), (2, 4), (2, 8) ...
长度为 3 的路径:(1, 2, 4), (1, 2, 8), (1, 4, 2), (1, 4, 8) ... 等等

我如何在 python/numpy 中为小型 (5x5) 矩阵实现这种算法的快速实现,可能使用我目前不知道的数学的某些方面?

当前缓慢的实施:

from copy import deepcopy

def get_walks(arr, n):
    n = n-1
    dim_y = len(arr)
    dim_x = len(arr[0])

    # Begin with every possibly starting location
    walks = [[(y, x)] for y in range(dim_y) for x in range(dim_x)]

    # Every possible direction to go in
    directions = [(0,1), (1,1), (1,0), (1, -1), (0, -1), (-1,-1), (-1, 0), (-1, 1)]

    temp_walks = []
    for i in range(n):
        # Go through every single current walk and add every 
        # possible next move to it, making sure to not repeat any points
        #
        # Do this n times
        for direction in directions:
            for walk in walks:
                y, x = walk[-1]
                y, x = y+direction[0], x+direction[1]
                if -1 < y < dim_y and -1 < x < dim_x and (y, x) not in walk:
                    temp_walks.append(walk + [(y, x)])

        # Overwrite current main walks list with the temporary one and start anew
        walks = deepcopy(temp_walks)
        temp_walks = []

    return walks

【问题讨论】:

  • 您的步行是否需要支持最长 N 或无限的长度?
  • 对于任何给定的 y x x 矩阵,最长可能的步行将是 (y*x),因此直到 n 为止都是有限的。
  • 我的错,它是用python 3.5编写的,我会更新它。
  • 你不能。有太多的步行。不管你如何生成它们,输出的绝对大小对于任何其他非常小的矩阵来说都太大了。
  • 从角度来看,考虑一个NxN 数组。考虑从左上角到右下角的路径。仅考虑向右和向下移动,您有二项式 (2N N) 方式,这已经是指数级的了。添加可以上下或左右移动的步行...

标签: python arrays numpy optimization


【解决方案1】:

我想出了一个递归解决方案。由于您只想处理小问题,因此这种方法是可行的。我没有为 python 3 安装 numpy,所以这只能保证对 python 2 原样工作(但它应该是相当兼容的)。另外,我很确定我的实现远非最佳。

在对照您的输出检查我的输出时,我突然想到,对于 3x3 案例,我得到 200 条路径,而您得到 160 条路径。查看路径,我认为您的代码有一些错误,而您是缺少的路径之一(而不是我有额外的)。这是我的版本:

import numpy as np
import timeit

def get_walks_rec(shape,inpath,ij,n):
    # add n more steps to mypath, with dimensions shape
    # procedure: call shorter walks for allowed neighbouring sites


    mypath = inpath[:]
    mypath.append(ij)

    # return if this is the last point
    if n==0:
        return mypath

    i0 = ij[0]
    j0 = ij[1]

    neighbs = [(i,j) for i in (i0-1,i0,i0+1) for j in (j0-1,j0,j0+1) if 0<=i<shape[0] and 0<=j<shape[1] and (i,j)!=(i0,j0)]
    subpaths = [get_walks_rec(shape,mypath,neighb,n-1) for neighb in neighbs]

    # flatten out the sublists for higher levels
    if n>1:
        flatpaths = []
        map(flatpaths.extend,subpaths)
    else:
        flatpaths = subpaths

    return flatpaths

# front-end for recursive function, called only once
def get_walks_rec_caller(mat,n):
    # collect all the paths starting from each point of the matrix

    sh = mat.shape
    imat,jmat = np.meshgrid(np.arange(sh[0]),np.arange(sh[1]))
    tmppaths = [get_walks_rec(sh,[],ij,n-1) for ij in zip(imat.ravel(),jmat.ravel())]

    # flatten the list of lists of paths to a single list of paths
    allpaths = []
    map(allpaths.extend,tmppaths)

    return allpaths

# input
mat = np.random.rand(3,3)
nmax = 3

# original:
walks_old = get_walks(mat,nmax)

# new recursive:
walks_new = get_walks_rec_caller(mat,nmax)

# timing:
number = 1000
print(timeit.timeit('get_walks(mat,nmax)','from __main__ import get_walks,mat,nmax',number=number))
print(timeit.timeit('get_walks_rec_caller(mat,nmax)','from __main__ import get_walks_rec_caller,mat,nmax',number=number))

对于这个最大路径长度为 3 的 3x3 案例,使用 timeit 运行 1000 次后,您的运行时间为 1.81 秒,而我的运行时间为 0.53 秒(您丢失了 20% 的路径)。对于最大长度为 4 的 4x4 案例,100 次运行需要 2.1 秒(你的)和 0.67 秒(我的)。

一个示例路径,它存在于我的路径中,但您的路径中似乎没有:

[(0, 0), (0, 1), (0, 0)]

【讨论】:

  • 谢谢,这很好用!您提到的额外路径实际上是包含两次相同点的路径,不应该在步行中。我在neighbs = [(i,j) for... 行的末尾添加了一个and (i, j) not in mypath,以确保步行中没有两个点重复,它给出的结果与我最初的实现相同。
  • 另外,为了使这项工作适用于 3.5,我只需将 flatpaths = [] map(flatpaths.extend,subpaths) 替换为 [item for subpath in subpaths for item in subpath]
  • @AlexShmakov 感谢您的反馈:) 抱歉,我完全不清楚您是否需要自我避免散步。这也让你的问题更容易处理,现在我明白你为什么不理解我们对输出大小的担忧了。
  • @AlexShmakov 同样,如果性能至关重要并且您的机器中有多个内核,您应该考虑使用类似multiprocessing.Pool.map 的东西来并行计算n^2 起点的路径,因为这些彼此完全独立。
猜你喜欢
  • 2018-04-26
  • 1970-01-01
  • 2012-01-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-07
相关资源
最近更新 更多