python中加快计算速度的方式

236 阅读5分钟

一、使用更快的数学计算的库,如numpy

使用NumPy进行向量化计算,这通常比纯Python循环快很多。

1.1numpy为什么快:

  1. 内部优化:NumPy的内部实现使用了高效的、针对数组操作优化的代码,通常是用C或Fortran编写的。这比Python的内置类型和操作要快得多,因为它减少了解释器的开销,并利用了低层次语言的效率。
  2. 向量化操作:NumPy允许进行向量化计算,这意味着您可以对整个数组进行操作,而无需显式编写循环。向量化操作减少了Python循环的开销,并允许NumPy在底层利用更有效的算法和硬件加速(如SIMD指令)。
  3. 减少内存访问次数:在常规的Python循环中,每次循环迭代都涉及到多次内存访问。而NumPy的向量化操作可以减少内存访问次数,因为它处理的是整个数组块,这在内存使用和缓存效率上通常比逐个元素处理更优。
  4. 广播机制:NumPy的广播机制允许不同形状的数组进行数学运算。在计算距离矩阵的过程中,广播允许我们不用显式地编写嵌套循环,而是利用更高效的数组操作来完成同样的任务。

1.2示例-计算某三维空间中各个三角形之间的距离:

def calculate_distance_matrix(centers):
    num_triangles = centers.shape[0]
    # centers.shape[0] 获取centers数组的第一维大小,即三角形的数量。

    diff = centers[:, np.newaxis, :] - centers[np.newaxis, :, :]
		#centers[:, np.newaxis, :] : shape(1608, 1, 3)
    #centers[np.newaxis, :, :] : shape(1, 1608, 3)

    # 这行代码使用NumPy的广播机制。centers[:, np.newaxis, :] 将centers数组转换为一个三维数组,
    # 其中第二维是单一元素的维度。centers[np.newaxis, :, :] 也是如此,但是单一维度在第一维。
    # 当两个数组相减时,NumPy会自动扩展这两个数组,使它们具有相同的形状,然后逐元素相减。
    # 这样得到的diff数组是一个三维数组,其中diff[i, j, :] 表示第i个和第j个三角形中心的坐标差。

    dist_matrix = np.sqrt(np.sum(diff**2, axis=-1))
    # 这里首先计算diff数组中每个元素的平方(diff**2),然后沿最后一个轴(axis=-1,即每个坐标差向量的轴)求和。
    # 这相当于计算了每对中心点差向量的平方和。最后,对这个和取平方根,得到实际的欧几里得距离。
    # 结果dist_matrix是一个二维数组,其中dist_matrix[i, j] 表示第i个和第j个三角形中心之间的距离。

    np.fill_diagonal(dist_matrix, 0)
    # 这行代码将距离矩阵的对角线元素设置为0。因为每个三角形与自身的距离是0。
    # 这是必要的,因为在前面的计算中,每个三角形与自身的距离会被计算为非常小的数,而不是准确的0。

    return dist_matrix
    # 返回计算得到的距离矩阵。

相关解释np.newaxis 是 NumPy 中用于增加数组维度的一个特殊标记。在数组索引操作中使用 np.newaxis 会在相应的位置增加一个新的轴(即维度)。这是一个非常有用的特性,尤其是在需要改变数组维度以进行广播操作时。

以下是一些关于 np.newaxis 的关键点:

  1. 增加维度:当你在数组索引中使用 np.newaxis,NumPy 会在这个位置增加一个新的轴。例如,如果你有一个形状为 (3,) 的一维数组,使用 np.newaxis 可以将其转换为形状为 (1, 3) 的二维数组,或者 (3, 1) 的二维数组,这取决于你将 np.newaxis 放在哪个位置。
  2. 便于广播:在执行数组间操作时,np.newaxis 可以帮助调整数组维度,使得它们可以进行广播。广播是 NumPy 用来处理不同形状数组的强大机制,它按照一定的规则自动扩展数组的形状,使得它们具有兼容的形状,从而可以进行逐元素的操作。

例如,假设有一个形状为 (3,) 的一维数组 a 和一个形状为 (4, 3) 的二维数组 b,你想要在 a 的每一行上执行操作,但由于它们的形状不匹配,直接操作会出错。通过使用 a[np.newaxis, :] ,你可以将 a 的形状变为 (1, 3) ,这样它就可以广播到与 b 兼容的形状 (4, 3) 上,然后进行操作。

在该距离计算示例中,np.newaxis 用于将中心点坐标数组转换为三维数组,以便在每对三角形中心之间进行广播操作,从而计算它们的欧几里得距离。

二、列表推导式

如果列表中包含的是不同大小的数组,那么无法直接将它们堆叠成一个规则的二维数组,因为NumPy的数组要求所有维度上的大小必须相同。在这种情况下,我们需要采用不同的方法。

一种可能的解决方案是使用列表推导式结合NumPy的向量化操作。这种方法相比于传统的 for 循环在性能上有所提升,尤其是在处理大量数据时。下面是示例代码:

import numpy as np

# 假设这是您的数据列表,列表中包含不同大小的数组
data_list = [np.array([10, 15, 20]), np.array([5, 7]), np.array([1, 3, 5, 7])]

# 使用列表推导式进行归一化处理
normalized_data_list = [(data - data.min()) / (data.max() - data.min()) for data in data_list]

# 输出归一化后的数据
print("归一化后的数据:")
for normalized_data in normalized_data_list:
    print(normalized_data)

三、检查数据类型

确保使用的是高效的数据类型。例如,如果坐标数据不需要非常高的精度,可以使用 np.float32而不是默认的np.float64,这样可以减少内存占用和提高处理速度。eg: np.array (dtype=np.float32)

# 将vertices转换为np.float32类型
vertices = np.array([polydata.GetPoint(i) for i in range(polydata.GetNumberOfPoints())], dtype=np.float32)

四、使用更契合的更高效的数据结构

例如对于图来说 , 使用空间索引:比如使用KD树或球树等空间索引结构,可以快速找到每个三角形的最近邻,从而减少距离计算的次数。