矩阵及多维数组

简介: 矩阵及多维数组

Lua 语言中,有两种方式来表示矩阵。第一种方式是使用一个不规则数组,即数组的数组,也就是一个所有元素均是另一个表的表。例如,可以使用如下代码来创建一个全 0 元素的 NxM 维矩阵:

local mt = {}         -- 创建矩阵
for i = 1, N do
  local row = {}      -- 创建新的一行
  mt[i] = row
  for j = 1, M do
    row[j] = 0
  end
end


由于表在 Lua 语言中是一种对象,因此在创建矩阵时必须显式地创建每一行。一方面,这比在 C 语言中直接声明一个多维数组更加具体;另一方面,这也给我们提供了更多的灵活性。例如,只需将前例中的内层循环 for j = 1, i do ... end 就可以创建一个三角形矩阵。使用这套代码,三角形矩阵较原来的矩阵可以节约一半的内存。


Lua 中表示矩阵的第二种方式是将两个索引合并为一个。典型情况下,我们通过将第一个索引乘以一个合适的常量再加上第二个索引来实现这种效果。在这种方式下,我们可以使用以下的代码来创建一个全 0 元素的 NxM 维矩阵:

local mt = {}
for i = 1, N do
  local aux = (i - 1) * M
  for j = 1, M do
    mt[aux + j] = 0
  end
end


应用程序会经常用到稀疏矩阵,这种矩阵中的大多数元素是 0nil 。例如,我们可以使用邻接矩阵来表示。当矩阵 (m, n) 处元素的值为 x 时,表示图中的节点 mn 是相连的,连接的权重为 x ;如上述的两个节点不相连,那么矩阵的 (m, n) 处元素的值是 nil 。如果要表示一个具有 1 万个节点的图(其中每个节点有 5 个邻居),那么需要一个能包含 1 亿个元素的矩阵( 1000列x1000行 的方阵),但是其中大约只有 5 万个元素不为 nil (每行有 5 列不为 nil ,对应每个节点有 5 个邻居)。许多有关数据结构的书籍都会深入地讨论如何实现这种稀疏矩阵而不必浪费 800M 内存空间,但在 Lua 语言中却很少需要用到那些技巧。这是因为,我们使用表实现数组而表本来就是稀疏的。在第一种实现中(表的表),需要 1 万个表,每个表包含 5 个元素,总共 5 万个元素。在第二种实现中,只需要一个表,其中包含 5 万个元素。无论那种实现,都只有非 nil 的元素才占用空间。


由于在有效元素之间存在空洞( nil 值),因此不能对稀疏矩阵使用长度运算符。这没什么大不了的。即使我们能够使用长度运算符,最好也不要这么做。对于大多数针对稀疏矩阵的操作来说,遍历空元素是非常低效的。相反,可以使用 pairs 来只遍历非 nil 的元素。例如,考虑如何进行由不规则数组表示的稀疏矩阵的矩阵乘法。


假设矩阵 a[M,K] 乘以矩阵 b[K,N] 的结果为矩阵 c[M,N] ,常见的矩阵相乘形式如下:

for i = 1, M do
  for j = 1, N do
    c[i][j] = 0
    for k = 1, K do
      c[i][j] = c[i][j] + a[i][k] * b[k][j]
    end
  end
end


外层的两个循环遍历了整个据结果矩阵,然后使用内层循环计算每一个元素的值。


对于使用不规则矩阵实现的稀疏矩阵,内存循环会有问题。由于内存循环遍历的是一列 b 而不是一行,因此不能在此处使用 pairs :这个循环必须遍历每一行来检查对应的行为是否在对应列中有元素。除了遍历了少量非 0 元素外,这个循环还遍历了所有的 0 元素。(由于不知道元素的空间位置,所以在其他场景下遍历一列也可能会有问题)

以下的算法与之前的示例非常类似,但是该算法调换了两个内存循环的顺序。通过这个简单的调整,该算法避免了遍历列:

-- 假设'c'的元素都是0
for i = 1, M do
  for k = 1, K do
    for j = 1, N do
      c[i][j] = c[i][j] + a [i][k] * b[k][j]
    end
  end
end


这样,中间的一层循环遍历行 a[i] ,而内层循环遍历行 b[k] 。这两个遍历都可以使用 pairs 来实现仅遍历非 0 元素。由于一个空的稀疏矩阵本身就是使用 0 填充的,所以对结果矩阵 c 的初始化没有任何问题。


以下的示例展示了上述算法的完整实现,其中使用了 pairs 来处理稀疏的元素。这种实现只访问非 nil 元素,同时结果也是稀疏矩阵。此外,下面的代码还删去了结果中偶然为 0 的元素:

function mult (a, b)
  local c = {}                                              -- 结果矩阵
  for i = 1, #a do
    local resultline = {}                                   -- 即'c[i]'
    for k, va in pairs(a[i]) do                             -- 'va'即a[i][k]
      for j, vb in pairs(b[k]) do                           -- 'vb'即b[k][j]
        local res = (resultline[j] or 0) + va * vb
        resultline[j] = (res ~= 0) and res or nil
      end
    end
    c[i] = resultline
  end
  return c
end
目录
相关文章
|
5天前
使用多维数组将两个矩阵相加
【10月更文挑战第30天】使用多维数组将两个矩阵相加。
15 2
|
5月前
|
存储
不会吧,不会吧,还在直接写二维数组?康康我一维变二维
不会吧,不会吧,还在直接写二维数组?康康我一维变二维
创建二维数组和矩阵
在Julia中,可以使用逗号或两个冒号创建二维数组和矩阵。例如,`[1 2 3 4]`和`[1;; 2;; 3;; 4]`创建1x4矩阵。添加分号`;`创建多行,如`[1 2; 3 4]`形成2x2矩阵。使用冒号和空格,如`[1:2 3:4]`也可得到2x2矩阵。通过嵌入相同长度的一维数组,如`[[1,2] [3,4] [5,6]]`,可构建2x3矩阵。利用分号和空格能创建不同形状的矩阵,如2x3和3x2矩阵。
|
6月前
|
索引
转置矩阵-暴力解法&一行代码
转置矩阵-暴力解法&一行代码
39 0
|
机器学习/深度学习 存储 人工智能
第3章 数组与矩阵——3.2 矩阵操作
第3章 数组与矩阵——3.2 矩阵操作
|
机器学习/深度学习 资源调度 算法
第3章 数组与矩阵——3.4 矩阵运算(2)
第3章 数组与矩阵——3.4 矩阵运算(2)
|
机器学习/深度学习 前端开发 rax
第3章 数组与矩阵——3.4 矩阵运算(1)
第3章 数组与矩阵——3.4 矩阵运算(1)
|
存储 NoSQL
第3章 数组与矩阵——3.5 稀疏矩阵
第3章 数组与矩阵——3.5 稀疏矩阵
第3章 数组与矩阵——3.3 矩阵元素的运算(1)
第3章 数组与矩阵——3.3 矩阵元素的运算(1)
第3章 数组与矩阵——3.3 矩阵元素的运算(2)
第3章 数组与矩阵——3.3 矩阵元素的运算(2)