讲师中心 微信公众号
AI工具推荐 视频效率加速

利用NumPy高效修改二维数组的2x2分块

酷丽吖_6875

酷丽吖_6875

发布时间:2025-11-08 13:44:01

|

600人浏览过

|

来源于php中文网

原创

利用numpy高效修改二维数组的2x2分块

本文深入探讨了如何使用NumPy库高效地对二维数组进行2x2分块操作和修改,避免了低效的Python循环。核心方法包括利用`np.lib.stride_tricks.as_strided`创建数组视图,以及通过构建查找表(lookup table, LUT)结合高级索引来实现批量、矢量化的数据转换,显著提升了处理性能。

在处理大型二维数组时,对特定大小(例如2x2)的子块进行迭代和修改是一个常见的需求。传统的Python循环(如itertools.product)虽然直观,但在面对大规模数据时性能瓶颈显著。NumPy提供了强大的矢量化操作能力,通过巧妙地运用视图和查找表,可以实现远超循环的效率。

问题背景与传统方法局限

假设我们有一个二维网格,需要根据每个2x2子块的当前状态,将其内容修改为预定义的新状态。原始问题中提供了一个基于Python循环的示例:

import numpy as np
import itertools

# 假设 transitions 是一个查找表,将2x2块的字节表示映射到新的布尔数组
# transitions = {b'\x00\x00\x00\x00': np.array([True, True, True, True]), …}
# grid 是一个二维NumPy数组
# yshift, xshift, ny, nx 定义了迭代范围

# 传统的Python循环方式
# for y, x in itertools.product(range(yshift, ny, 2), range(xshift, nx, 2)):
#     grid[y:y+2, x:x+2].flat = transitions[grid[y:y+2, x:x+2].tobytes()]

这种方法的问题在于,每次迭代都会创建一个新的2x2切片,并可能涉及数据复制,同时Python循环本身的开销也很大。为了提升效率,我们需要转向NumPy的矢量化特性。

利用 np.lib.stride_tricks.as_strided 创建分块视图

NumPy的np.lib.stride_tricks.as_strided函数是一个非常强大的工具,它允许我们创建一个现有数组的“视图”,而无需复制数据。通过巧妙地设置shape和strides参数,我们可以将一个二维数组看作是由多个重叠或不重叠的子块组成的更高维数组。

对于一个形状为 (N, M) 的二维数组 A,要创建一个2x2分块的视图 Av,其 shape 将是 (N/2, M/2, 2, 2)。strides 参数则需要计算原始数组 A 在行和列方向上移动一个2x2块所需的字节步长,以及在块内部移动一个元素所需的字节步长。

import numpy as np

# 示例:创建一个10x10的0/1随机数组
A = np.random.randint(0, 2, (10, 10))
print("原始数组 A:\n", A)

# 计算步长
# A.strides[0] 是行步长(移动一行所需的字节数)
# A.strides[1] 是列步长(移动一列所需的字节数)
# 对于2x2块,外部视图的行步长是 A.strides[0]*2
# 外部视图的列步长是 A.strides[1]*2
# 内部2x2块的行步长是 A.strides[0]
# 内部2x2块的列步长是 A.strides[1]
Av = np.lib.stride_tricks.as_strided(A, 
                                     shape=(A.shape[0]//2, A.shape[1]//2, 2, 2), 
                                     strides=(A.strides[0]*2, A.strides[1]*2, A.strides[0], A.strides[1]))

print("\nAv 的形状:", Av.shape) # (5, 5, 2, 2)
print("Av 是 A 的视图吗?", np.shares_memory(A, Av)) # True

现在,Av 是一个 (5, 5, 2, 2) 形状的视图,可以被看作是 A 中 5x5 个 2x2 子块的集合。对 Av 的任何修改都会直接反映到 A 上,因为它们共享内存。

基于查找表(LUT)的矢量化修改

有了分块视图 Av,我们就可以利用查找表进行高效的矢量化修改。

Shadows Python Sensei
Shadows Python Sensei

Python 最佳实践助手——代码规范、设计模式、性能优化、测试与类型注解。适用于编写或审查 Python 代码。

下载

方法一:多维查找表

这种方法为每个2x2块的四个元素(假设为布尔值或0/1整数)创建一个多维索引,然后映射到对应的输出2x2块。

# 创建一个多维查找表 lut
# lut 的形状 (2,2,2,2, 2,2) 意味着:
# 前四个维度对应2x2块的四个布尔值 (0或1)
# 后两个维度是替换后的2x2结果块
lut = np.zeros((2, 2, 2, 2, 2, 2), dtype=A.dtype)

# 填充查找表(根据实际转换规则)
# 例如,如果块是 [[0,0],[0,0]],替换为 [[1,1],[1,1]]
lut[0, 0, 0, 0] = [[1, 1], [1, 1]]
# 如果块是 [[0,0],[0,1]],替换为 [[1,1],[1,0]]
lut[0, 0, 0, 1] = [[1, 1], [1, 0]]
# 如果块是 [[0,0],[1,0]],替换为 [[1,1],[0,1]]
lut[0, 0, 1, 0] = [[1, 1], [0, 1]]
# 更多规则...
lut[1, 1, 0, 0] = [[1, 1], [1, 1]] # 示例规则

# 使用高级索引进行赋值
# Av[...,0,0] 提取所有2x2块的左上角元素
# Av[...,0,1] 提取所有2x2块的右上角元素
# 依此类推
Av[:] = lut[Av[..., 0, 0], Av[..., 0, 1], Av[..., 1, 0], Av[..., 1, 1]]

print("\n修改后的数组 A (方法一):\n", A)

这种方法的优点是直观,直接将2x2块的每个元素作为查找表的索引。缺点是如果2x2块中的元素数量增加,查找表的维度会迅速增长,导致创建和填充变得复杂。

方法二:扁平化索引查找表

为了简化查找表的索引,我们可以将每个2x2块的布尔(或0/1)状态转换为一个单一的整数索引。对于2x2块,共有 $2^4 = 16$ 种可能的组合,因此我们可以将它们映射到0-15的整数。

例如,一个2x2块 [[a, b], [c, d]] 可以通过加权和 a*8 + b*4 + c*2 + d*1 转换为一个0-15的索引。

# 创建一个扁平化索引的查找表 lut2
# lut2 的形状 (16, 2, 2) 意味着:
# 第一个维度是0-15的索引
# 后两个维度是替换后的2x2结果块
lut2 = np.zeros((16, 2, 2), dtype=A.dtype)

# 填充查找表(根据实际转换规则)
# 索引 0 (0000b) 对应 [[0,0],[0,0]]
lut2[0] = [[1, 1], [1, 1]]
# 索引 1 (0001b) 对应 [[0,0],[0,1]]
lut2[1] = [[1, 1], [1, 0]]
# 索引 2 (0010b) 对应 [[0,0],[1,0]]
lut2[2] = [[1, 1], [0, 1]]
# 索引 12 (1100b) 对应 [[1,1],[0,0]]
lut2[12] = [[1, 1], [1, 1]] # 示例规则

# 重新生成一个随机数组 A 进行演示
A_new = np.random.randint(0, 2, (10, 10))
Av_new = np.lib.stride_tricks.as_strided(A_new, 
                                        shape=(A_new.shape[0]//2, A_new.shape[1]//2, 2, 2), 
                                        strides=(A_new.strides[0]*2, A_new.strides[1]*2, A_new.strides[0], A_new.strides[1]))

# 计算每个2x2块的扁平化索引
# [[8,4],[2,1]] 是权重矩阵
idx = (Av_new * [[8, 4], [2, 1]]).sum(axis=(2, 3))

# 使用扁平化索引进行赋值
Av_new[:] = lut2[idx]

print("\n修改后的数组 A (方法二):\n", A_new)

这种方法通过将每个2x2块的状态编码为一个整数,极大地简化了查找表的索引方式,使得代码更简洁,并且在处理具有更多状态的块时也更具扩展性。

局部修改分块

上述方法默认对整个数组进行分块操作。如果只需要修改数组的某个特定区域,我们可以对 Av 视图进行切片操作,然后进行赋值。

例如,只修改 Av 的 (2,2) 到 (4,4) 范围内的块:

# 假设 A 和 lut2 已经定义
# 重新生成一个随机数组 A 进行演示
A_partial = np.random.randint(0, 2, (10, 10))
Av_partial = np.lib.stride_tricks.as_strided(A_partial, 
                                             shape=(A_partial.shape[0]//2, A_partial.shape[1]//2, 2, 2), 
                                             strides=(A_partial.strides[0]*2, A_partial.strides[1]*2, A_partial.strides[0], A_partial.strides[1]))

# 计算局部区域的索引
idx_partial = (Av_partial[2:4, 2:4] * [[8, 4], [2, 1]]).sum(axis=(2, 3))

# 对 Av 的局部区域进行赋值
Av_partial[2:4, 2:4] = lut2[idx_partial]

print("\n局部修改后的数组 A (2:4, 2:4 块):\n", A_partial)

注意事项与总结

  1. as_strided 的风险: np.lib.stride_tricks.as_strided 是一个底层函数,使用不当可能导致内存访问错误。务必确保 shape 和 strides 参数设置正确,以避免越界访问。在大多数情况下,它是一个创建视图的强大工具,但需要谨慎使用。
  2. 数据类型: 本文示例假设数组元素是布尔值或0/1整数。如果数组包含其他类型的数据,需要调整查找表的索引转换逻辑和查找表本身的 dtype。
  3. 性能优势: 这种基于 as_strided 和查找表的方法,将Python循环的开销转移到了NumPy的C实现中,通过矢量化操作显著提升了性能,尤其适用于大型数组。
  4. 内存效率: as_strided 创建的是视图,不涉及数据复制,因此在内存使用上非常高效。查找表虽然会占用额外内存,但对于固定大小的块(如2x2),其大小是固定的且通常很小。

通过结合 np.lib.stride_tricks.as_strided 创建分块视图和预计算的查找表,我们可以高效、矢量化地对NumPy二维数组的2x2分块进行修改,从而在处理图像、模拟网格或其他基于块的数据结构时获得显著的性能提升。

热门AI工具

更多
Atoms
Atoms Hot

Atoms是一款AI智能体工具,第一支自动构建真实业务的 AI 团队。

UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

Loomy
Loomy Hot

一款AI工具,主要用于科大讯飞发布的桌面级 AI 助理,比 OpenClaw 更易用、更安全!,适合需要提升相关任务效率的用户。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

相关专题

更多
数据类型有哪几种
数据类型有哪几种

数据类型有整型、浮点型、字符型、字符串型、布尔型、数组、结构体和枚举等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

2331

2023.10.31

php数据类型
php数据类型

本专题整合了php数据类型相关内容,阅读专题下面的文章了解更多详细内容。

494

2025.10.31

c语言 数据类型
c语言 数据类型

本专题整合了c语言数据类型相关内容,阅读专题下面的文章了解更多详细内容。

402

2026.02.12

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

2121

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

296

2025.12.22

深入理解算法:高效算法与数据结构专题
深入理解算法:高效算法与数据结构专题

本专题专注于算法与数据结构的核心概念,适合想深入理解并提升编程能力的开发者。专题内容包括常见数据结构的实现与应用,如数组、链表、栈、队列、哈希表、树、图等;以及高效的排序算法、搜索算法、动态规划等经典算法。通过详细的讲解与复杂度分析,帮助开发者不仅能熟练运用这些基础知识,还能在实际编程中优化性能,提高代码的执行效率。本专题适合准备面试的开发者,也适合希望提高算法思维的编程爱好者。

337

2026.01.06

C++ 数据结构与算法实现教程合集
C++ 数据结构与算法实现教程合集

以 C++ 为实现语言,系统讲解核心数据结构与算法,涵盖链表(单链表/双链表/环检测)、栈与队列(单调栈/优先队列)、二叉树(遍历/BST/AVL/红黑树)、哈希表(开地址法/链地址法)、图(邻接表/BFS/DFS/Dijkstra/拓扑排序)、常见排序算法(快排/归并/堆排/计数排序)的实现与复杂度分析,同时分享 LeetCode 刷题技巧、竞赛编程常用模板(二分/前缀和/滑动窗口/动态规划),帮助开发者夯实算法基础。

372

2026.05.09

go语言 数组和切片
go语言 数组和切片

本专题整合了go语言数组和切片的区别与含义,阅读专题下面的文章了解更多详细内容。

1372

2025.09.03

Conan私有仓库搭建教程
Conan私有仓库搭建教程

本专题系统的讲解Conan私有仓库的搭建流程,涵盖仓库服务部署、存储目录配置、用户认证、权限划分和远程地址添加,并介绍内部C++依赖包的上传、下载及版本维护方法。

0

2026.09.22

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn