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

来源:互联网 发布:centos编译安装php7.1 编辑:程序博客网 时间:2024/06/15 11:59

        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。


0 0
原创粉丝点击