如何在NumPy或SciPy中实现基于自定义运算符(如异或)的广义克罗内克积?
如何在NumPy或SciPy中实现基于自定义运算符(如异或)的广义克罗内克积?
这个问题问得太实用了!默认的NumPy kron 函数确实只认乘法,但咱们完全可以用NumPy自带的数组操作,轻松实现基于自定义二元运算符的广义克罗内克积,连SciPy都不用额外装~
先从你给出的异或案例入手,一步步拆解实现:
首先先定义你提到的两个布尔数组:
import numpy as np A = np.asarray([[True, False], [False, False]]) B = np.asarray([[False, True], [True, True]])
针对异或的具体实现
核心思路是:把A扩展成和结果同尺寸的「块重复」结构,把B扩展成对应的「块平铺」结构,然后对两个扩展后的数组直接应用异或运算符。代码如下:
# 把A的每个元素,在行方向重复B的行数,列方向重复B的列数,得到和结果同形状的数组 A_expanded = np.repeat(np.repeat(A, B.shape[0], axis=0), B.shape[1], axis=1) # 把整个B数组,在行方向平铺A的行数,列方向平铺A的列数,同样得到结果形状 B_expanded = np.tile(B, (A.shape[0], A.shape[1])) # 直接对两个扩展数组做异或 C = A_expanded ^ B_expanded
运行后得到的C完全符合你想要的结果:
array([[ True, False, False, True], [False, False, True, True], [False, True, False, True], [ True, True, True, True]])
推广到任意自定义运算符
如果要支持任意二元运算符(只要它能兼容NumPy数组的广播机制),咱们可以把上面的逻辑封装成一个通用函数:
def generalized_kron(A, B, op): """ 实现基于自定义二元运算符的广义克罗内克积 参数: A, B: 输入的NumPy数组 op: 二元运算符,比如lambda表达式、NumPy内置的元素级函数 返回: 广义克罗内克积的结果数组 """ A_expanded = np.repeat(np.repeat(A, B.shape[0], axis=0), B.shape[1], axis=1) B_expanded = np.tile(B, (A.shape[0], A.shape[1])) return op(A_expanded, B_expanded)
比如用这个函数实现异或积,直接传异或的lambda表达式就行:
C = generalized_kron(A, B, lambda a, b: a ^ b)
再举个例子,如果你想做基于加法的广义积(虽然这不是数学上标准的克罗内克积,但作为演示):
# 先把布尔数组转成int类型,不然加法没意义 C_add = generalized_kron(A.astype(int), B.astype(int), lambda a, b: a + b)
更高效的广播优化版
上面的方法需要创建完整的扩展数组,对于大数组来说会占用较多内存。咱们可以用NumPy的新轴扩展+广播技巧,避免显式创建扩展数组,进一步优化内存和速度:
def generalized_kron_broadcast(A, B, op): # 给A添加两个新轴,形状从 (m,n) → (m,1,n,1) A_bd = A[:, :, np.newaxis, np.newaxis] # 给B添加两个新轴,形状从 (p,q) → (1,p,1,q) B_bd = B[np.newaxis, :, np.newaxis, :] # 广播后两者自动匹配成 (m,p,n,q) 的形状,应用运算符后reshape成最终结果 return op(A_bd, B_bd).reshape(A.shape[0]*B.shape[0], A.shape[1]*B.shape[1])
用这个函数处理你的异或案例,结果完全一致,但内存占用会低很多——因为广播是动态计算的,不会提前生成完整的中间数组,适合处理大尺寸的数组。
原理说明
不管用哪种方法,核心逻辑都和标准克罗内克积一致:把A的每个元素和整个B数组应用自定义运算符,然后按块拼接成最终结果。我们只是用NumPy的数组操作替代了默认的乘法,换成了你需要的异或(或其他任意运算符)。
备注:内容来源于stack exchange,提问作者Ben
相关产品推荐
相关产品推荐

