计算许多三角形的签名区域

时间:2018-05-18 12:41:07

标签: python arrays numpy

我需要计算2D中许多三角形的signed area,由一个形状为(2, 3, n)的numpy数组(x / y坐标,三角形中的节点,三角形数)给出。我正在寻找一种快速做到这一点的方法,到目前为止我能想出的最好的是

def mix(p):
    return (
        + p[0][2] * (p[1][0] - p[1][1])
        + p[0][0] * (p[1][1] - p[1][2])
        + p[0][1] * (p[1][2] - p[1][0])
        ) / 2

其他尝试较慢:

import numpy
import perfplot


def six(p):
    return (
        + p[0][2]*p[1][0] + p[0][0]*p[1][1] + p[0][1]*p[1][2]
        - p[0][2]*p[1][1] - p[0][0]*p[1][2] - p[0][1]*p[1][0]
        ) / 2

def mix(p):
    return (
        + p[0][2] * (p[1][0] - p[1][1])
        + p[0][0] * (p[1][1] - p[1][2])
        + p[0][1] * (p[1][2] - p[1][0])
        ) / 2

def mix2(p):
    p1 = p[1] - p[1][[1, 2, 0]]
    return (
        + p[0][2] * p1[0]
        + p[0][0] * p1[1]
        + p[0][1] * p1[2]
        ) / 2

def cross(p):
    e1 = p[:, 1] - p[:, 0]
    e2 = p[:, 2] - p[:, 0]
    return (e1[0]*e2[1] - e1[1]*e2[0]) / 2

def einsum(p):
    return (
        + numpy.einsum('ij,ij->j', p[0][[2, 0, 1]], p[1][[0, 1, 2]])
        - numpy.einsum('ij,ij->j', p[0][[2, 0, 1]], p[1][[1, 2, 0]])
        ) / 2

def einsum2(p):
    return numpy.einsum(
        'ij,ij->j',
        p[0][[2, 0, 1]],
        p[1] - p[1][[1, 2, 0]]
        ) / 2

def einsum3(p):
    return numpy.einsum(
        'ij,ij->j',
        numpy.roll(p[0], 1, axis=0),
        p[1] - numpy.roll(p[1], 2, axis=0)
        ) / 2


perfplot.show(
    setup=lambda n: numpy.random.rand(2, 3, n),
    kernels=[six, mix, mix2, cross, einsum, einsum2, einsum3],
    n_range=[2**k for k in range(19)],
    logx=True,
    logy=True,
    )

有关如何提高效率的任何提示?

enter image description here

4 个答案:

答案 0 :(得分:2)

我认为问题不是计算,而是数据在内存中的组织方式。如果您不需要保留原始数据,可以尝试使用inplace函数最小化内存中的临时对象数量:

def mymix(p):

    p[0][1] -= p[0][0] # x1 = x1 - x0
    p[0][2] -= p[0][0] # x2 = x2 - x0
    p[1][1] -= p[1][0] # y1 = y1 - y0
    p[1][2] -= p[1][0] # y2 = y2 - y0

    p[0][1] *= p[1][2] # x1 = (x1-x0) * (y2-y0)
    p[0][2] *= p[1][1] # x2 = (x2-x0) * (y1-y0)

    p[0][1] -= p[0][2] # r = (x1-x0) * (y2-y0) - (x2-x0) * (y1-y0)
    p[0][1] /= 2

    return p[0][1]

答案 1 :(得分:2)

您可以使用Numba或Cython来优化此类型问题。最好的方法可能是当你从这些三角形得到角点时直接计算三角形的有符号区域。即使使用单线程版本,您的内存带宽也可能是少量添加和乘法的限制因素。

<强>代码

import numba as nb
import numpy 

@nb.njit(fastmath=True)
def mix_nb(p):
    assert p.shape[0]==2
    assert p.shape[1]==3

    res=numpy.empty(p.shape[2],dtype=p.dtype)

    for i in range(p.shape[2]):
      res[i]=(+p[0,2,i] * (p[1,0,i] - p[1,1,i])+ p[0,0,i] * (p[1,1,i] - p[1,2,i])+ p[0,1,i] * (p[1,2,i] - p[1,0,i])) /2.

    return res

#Compile
p= np.random.rand(2, 3, 10000)
A=mix_nb(p)

import perfplot

#Benchmark
perfplot.show(
    setup=lambda n: np.random.rand(2, 3, n),
    kernels=[six, mix, mix2, cross, einsum, einsum2, einsum3,mix_nb],
    n_range=[2**k for k in range(19)],
    logx=True,
    logy=True,
    )

<强>结果

Results

答案 2 :(得分:1)

这是Cython中的一个简单版本,我在这里只是为了完整起见:

    for exlrows in excelrows:
        for var in exlrows:
            worksheet.cell(row = (row_num + 1), column = col_num+1,value=var)
            col_num=col_num+1
            row_num=row_num+1
         col_num=1

答案 3 :(得分:1)

我有点同意@ TrapII的回答:这是一个内存使用问题。但是,我认为数据如何存储在内存中更为重要。让我们进行以下实验:

In [1]: import numpy as np

In [2]: p = np.random.random((1000,3,2))

In [3]: p2 = np.array(np.moveaxis(p, (1, 0), (0, 2)).reshape((6, 1000)), order='C')

In [4]: def mix(p):
   ...:     return (
   ...:           p[:,2,0] * (p[:,0,1] - p[:,1,1])
   ...:         + p[:,0,0] * (p[:,1,1] - p[:,2,1])
   ...:         + p[:,1,0] * (p[:,2,1] - p[:,0,1])
   ...:         ) / 2.0
   ...:     

In [5]: def cross2(p2):
   ...:     e0x, e0y, e1x, e1y, e2x, e2y = p2
   ...:     return 0.5 * ((e1x - e0x)*(e2y - e0y) - (e1y - e0y)*(e2x - e0x))
   ...: 

In [6]: %timeit mix(p)
18.5 µs ± 351 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)

In [7]: %timeit cross2(p2)
9.03 µs ± 90.9 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)