您的位置:首页 > 理论基础 > 数据结构算法

python数据结构学习笔记-2016-10-22-03-稀疏矩阵

2016-10-22 13:38 651 查看
        4.5 应用:稀疏矩阵

        稀疏矩阵(Sparse Matrix):一个矩阵中大部分元素都是零元素的矩阵(m × n),即其非零元素个数k << m × n。

        与稀疏矩阵相反的是稠密矩阵。

        如果采用二维数组来实现稀疏矩阵,就意味着大部分的内存都会用来储存零元素,这就会造成内存的极大浪费,相关操作也会变得低效(?)。

        基于此,本节来考虑使用python列表来实现稀疏矩阵。稀疏矩阵中的每一个非零元素都以储存类实例存在,其属性包括所在行、所在列以及相应值,而python列表的每一项就是指向储存类实例的引用。



        4.1.2 基于列表的实现
#-*-coding: utf-8-*-

# 使用列表实现稀疏矩阵

class SparseMatrix(object):
def __init__(self, numRows, numCols):
self._numRows = numRows
self._numCols = numCols
self._elementList = list()

def numRows(self):
return self._numRows

def numCols(self):
return self._numCols

def __getitem__(self, ndxTuple):
ndx = self._findPosition(ndxTuple[0], ndxTuple[1])
if ndx is not None:
return self._elementList[ndx].value
else:
return 0

def __setitem__(self, ndxTuple, scalar):
ndx = self._findPosition(ndxTuple[0], ndxTuple[1])
if ndx is not None: # 当指定索引是非零元素时
if scalar != 0.0: # 新传入的值不是0,直接赋值
self._elementList[ndx].value = scalar
else: # 新传入的值为0时,只需从列表中删除这一项,即表明指定索引的值是0
self._elementList.pop(ndx)
else: # 当指定索引是0时
if scalar != 0.0 # 传入值非零,即在底层列表中添加一项即可
element = _MatrixElement(ndxTuple[0], ndxTuple[1], scalar)
self._elementList.append(element)

# 数乘运算
def scaleBy(self, scalar):
for element in self._elementList:
element.value *= scalar

# 矩阵加法
def __add__(self, other):
assert other.numRows() == self.numRows() and other.numCols() == self.numCols(), "Matrix sizes not compatible for the add operation."
newMatrix = SparseMatrix(self.numRows(), self.numCols())
# 将self中的所有非零项复制到newMatrix中
for element in self._elementList:
dupElement = _MatrixElement(element.row, element.col, element.value)
newMatrix._elementList.append(dupElement)
# 将other中的所有非零项加到newMatrix中
for element in other._elementList:
newMatrix[element.row, element.col] += element.value
return newMatrix

# 矩阵减法
def __sub__(self, other):
pass

# 矩阵乘法
def __mul__(self, other):
pass

# 辅助方法
def _findPosition(self, row, col):
n = len(self._elementList)
for i in range(n):
if row == self._elementList[i].row and col == self._elementList[i].col:
return i
return None

# 储存类,储存非零元素的值,以及其在矩阵中的位置
class _MatrixElement(object):
def __init__(self, row, col, value):
self.row = row
self.col = col
self.value = value

        辅助方法_findPosition(),是将稀疏矩阵索引与列表索引一一对应起来。由于列表储存的实例对象中,已包含该非零项所在索引,所以稀疏矩阵的所有非零元素在列表中并不是按照特定顺序排列的。
        
        值得注意的是,在修改稀疏矩阵的值时,会出现以下四种情况:

原值非零,新值非零;
原值非零,新值是零;
原值是零,新值非零;
原值是零,新值是零。

        这四种情况在程序中得到了分别处理。

        还要值得注意的是稀疏矩阵的加法运算,程序中首先是创建新的系数矩阵,再将原有的稀疏矩阵所有非零项复制到新的稀疏矩阵中,再将另一个稀疏矩阵中的非零项加进来,最后一步依赖于__gettitem__()和__setitem__()的准确实现。

        4.5.2 效率分析

操作矩阵稀疏矩阵
构造器O(n²)O(1)
s.numRows()O(1)O(1)
s.numCols()O(1)O(1)
s.scaleBy(x)O(n²)O(k)
x = s[i, j]O(1)O(k)
s[i, j] = xO(1)O(k)
r = s + tO(n²)O(k²)
       假设矩阵是n × n,稀疏矩阵含有k个非零元素,_findPosition()的执行时间是O(k)。__gettitem__()和__setitem__()都调用了_findPosition(),所以执行时间都是O(k),比起矩阵来说要慢。
       但是在构造器方面,矩阵是O(n²),因为调用了clear(),而clear()中含有一个嵌套循环,而稀疏矩阵只是构造了一个空列表。
       数乘运算方面,矩阵是每一个元素都乘以相应的数值,需要遍历每一个元素,所以是O(n²),而稀疏矩阵只需将列表中每一个值乘以给定的数值即可,因此是O(k)。
       在加法上,矩阵需要遍历每一个元素,并与对应元素相加,时间是O(n²),而在稀疏矩阵中,复制所有项需要O(k),在第二个循环中,要注意newMatrix[element.row, element.col]本身就需要O(k),这一操作又要循环k次,所以时间是O(k) + O(k²)
= O(k²)。两者效率的高低要取决于k。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息