稀疏矩陣(Sparse Matrix):對於稀疏矩陣,目前尚未一個確切的定義。設矩陣A是一個n*m的矩陣中有s個非零元素,設 δ=s/(n*m),稱δ爲稀疏因子,算法
若是某一矩陣的稀疏因子δ知足δ≦0.05時稱爲稀疏矩陣,數組
稀疏矩陣的壓縮存儲app
對於稀疏矩陣,採用壓縮存儲方法時,只存儲非0元素。必須存儲非0元素的行下標值、列下標值、元素值。所以,一個三元組(i, j, aij)惟一肯定稀疏矩陣的一個非零元素。ide
上圖的稀疏矩陣A的三元組線性表爲: ( (1,2,12), (1,3,9), (3,1,-3), (3,8,4), (4,3,24), (5,2,18), (6,7,-7), (7,4,-6) )ui
1 三元組順序表this
若以行序爲主序,稀疏矩陣中全部非0元素的三元組,就能夠得構成該稀疏矩陣的一個三元組順序表。spa
1 function Triple(i, j, elem) { 2 // 該非零元的行下標和列下標 3 this.i = i || 0; 4 this.j = j || 0; 5 this.e = elem || null; 6 } 7 8 function TSMatrix(mu, nu) { 9 // 非零元三元組表 10 this.data = []; 11 // 矩陣的行數,列數 12 this.mu = mu || 0; 13 this.nu = nu || 0; 14 }
下圖所示的稀疏矩陣及其相應的轉置矩陣所對應的三元組順序表:prototype
一個m*n的矩陣A,它的轉置B是一個n*m的矩陣,且b[i][j]=a[j][i],0≦i≦n,0≦j≦m,即B的行是A的列,B的列是A的行。code
設稀疏矩陣A是按行優先順序壓縮存儲在三元組表a.data中,若僅僅是簡單地交換a.data中i和j的內容,獲得三元組表b.data,blog
b.data將是一個按列優先順序存儲的稀疏矩陣B,要獲得按行優先順序存儲的b.data,就必須從新排列三元組表b.data中元素的順序。
求轉置矩陣的基本算法思想是:
① 將矩陣的行、列下標值交換。即將三元組表中的行、列位置值i 、j相互交換;
② 重排三元組表中元素的順序。即交換後仍然是按行優先順序排序的。
方法一: 算法思想:按稀疏矩陣A的三元組表a.data中的列次序依次找到相應的三元組存入b.data中。
每找轉置後矩陣的一個三元組,需從頭到尾掃描整個三元組表a.data 。找到以後天然就成爲按行優先的轉置矩陣的壓縮存儲表示
TSMatrix.prototype = { constructor: TSMatrix, addTriple: function (triple) { if (triple instanceof Triple) { if(triple.i >= this.mu) this.mu = triple.i + 1; if(triple.j >= this.nu) this.nu = triple.j + 1; this.data.push(triple); return true; } return false; }, // 採用三元組表存儲表示,求稀疏矩陣的轉置矩陣t // 按照b.data中三元組的次序依次在a.data中找到相應的三元組進行轉置 transposeSMatrix: function () { var t = new TSMatrix(); t.mu = this.nu; t.nu = this.mu; if (this.data.length) { var q = 0; for (var col = 0; col < this.nu; col++) { for (var p = 0; p < this.data.length; p++) { if (this.data[p].j === col) t.data[q++] = new Triple(this.data[p].j, this.data[p].i, this.data[p].e); } } } return t; } };
算法分析:本算法主要的工做是在p和col的兩個循環中完成的,故算法的時間複雜度爲O(nu * data.length),即矩陣的列數和非0元素的個數的乘積成正比。
方法二(快速轉置的算法) 算法思想:
直接按照稀疏矩陣A的三元組表a.data的次序依次順序轉換,並將轉換後的三元組放置於三元組表b.data的恰當位置。
前提:若能預先肯定原矩陣A中每一列的(即B中每一行)第一個非0元素在b.data中應有的位置,則在做轉置時就可直接放在b.data中恰當的位置。
所以,應先求得A中每一列的非0元素個數。 附設兩個輔助向量num[ ]和cpot[ ] 。
◆ num[col]:統計A中第col列中非0元素的個數;
◆ cpot[col] :指示A中第一個非0元素在b.data中的恰當位置。
顯然有位置對應關係:
快速轉置算法以下:
1 // 採用三元組表存儲表示,求稀疏矩陣的轉置矩陣t 2 /* 3 按照a.data中三元組的次序進行轉置,並將轉置後的三元組置入b中恰當的位置。 4 若是能預先肯定矩陣M中每一列(即T中每一行)的第一個非零元在b.data中應有的位置, 5 那麼在對a.data中的三元組依次作轉置時,即可直接放到b.data中恰當的位置上去。 6 爲了其額定這些位置,在轉置前,應先求得M的每一列中非零元的個數,進而求得每一列的第一個非零元在b.data中應有的位置。 7 在此,須要設num和cpot兩個變量。num[col]表示矩陣M中第col列中非零元的個數, 8 cpot[col]指示M中第col列的第一個非零元在b.data中的恰當位置。顯然有: 9 cpot[0] = 1; 10 cpot[col] = cpot[col - 1] + num[col - 1] 2 <= col <= a.nu 11 */ 12 TSMatrix.prototype.fastTransposeSMatrix = function(){ 13 var t = new TSMatrix(); 14 t.mu = this.nu; 15 t.nu = this.mu; 16 17 if(this.data.length){ 18 var num = []; 19 for(var col = 0; col < this.nu; col++) 20 num[col] = 0; 21 for(var i = 0; i < this.data.length; i++) 22 ++num[this.data[i].j]; // 求矩陣中每一列含非零元個數 23 // 求第col列中第一個非零元在b.data中的序號 24 var cpot = [0]; 25 for(col = 1; col < this.nu; col++) 26 // 上一列以前的序號+上一列的非零元個數 = 該列的序號 27 cpot[col] = cpot[col - 1] + num[col - 1]; 28 for(var p = 0; p < this.data.length; p++){ 29 col = this.data[p].j; 30 var q = cpot[col]; 31 t.data[q] = new Triple(this.data[p].j, this.data[p].i, this.data[p].e); 32 // 給該列的序號+1,用做相同列數的狀況 33 ++cpot[col]; 34 } 35 } 36 37 return t; 38 }
三元組順序表又稱有序的雙下標法,它的特色是,非零元在表中按行序有序存儲,所以便於進行依行順序處理的矩陣運算。
然而,若需按行號存取某一行的非零元,則從頭開始進行查找。
行邏輯連接的順序表
爲了便於隨機存取任意一行的非零元,則需知道每一行的第一個非零元在三元組表中的位置。
爲此可將快速轉置矩陣的算法中建立的,指示「行」信息的輔助數組cpot固定在稀疏矩陣的存儲結構中。
稱這種「帶行連接信息」的三元組表爲行邏輯連接的順序表
設有兩個稀疏矩陣A=(aij)m*n ,B=(bij)n*p ,其存儲結構採用行邏輯連接的三元組順序表。求稀疏矩陣的乘法:
1 function RLSMatrix(mu, nu){ 2 TSMatrix.apply(this, arguments); 3 this.rpos = [0]; 4 } 5 RLSMatrix.MAXSIZE = 100; 6 RLSMatrix.prototype = { 7 constructor: RLSMatrix, 8 __proto__: TSMatrix.prototype, 9 // todo 10 /** 11 * 求矩陣乘積Q = M * N,採用行邏輯連接存儲表示 12 * @param nMatrix 13 * @returns {RLSMatrix} 14 */ 15 multSMatrix: function(nMatrix){ 16 if(this.nu !== nMatrix.mu) throw Error('nu is not equivalent to mu'); 17 18 // 初始化Q 19 var qMatrix = new RLSMatrix(this.mu, nMatrix.nu); 20 // Q是非零矩陣 21 if(this.data.length * nMatrix.data.length !== 0){ 22 // 處理M的每一行 23 for(var arow = 0; arow < this.mu; arow++){ 24 // 當前行各元素累加器清零 25 var ctemp = []; 26 qMatrix.rpos[arow] = qMatrix.data.length + 1; 27 var tp, ccol; 28 29 if(arow < this.mu) 30 tp = this.rpos[arow + 1]; 31 else 32 tp = this.data.length + 1; 33 34 //對當前行中每個非零元找到對應元在N中的行號 35 for(var p = this.rpos[arow]; p < tp; p++){ 36 var brow = this.data[p].j; 37 var t; 38 if(brow < nMatrix.mu) 39 t = nMatrix.rpos[brow + 1]; 40 else 41 t = nMatrix.data.length + 1; 42 43 for(var q = nMatrix.rpos[brow]; q < t; q++){ 44 // 乘積元素在Q中的序號 45 ccol = nMatrix.data[q].j; 46 ctemp[ccol] = (ctemp[ccol] || 0) + this.data[p].e * nMatrix.data[q].e; 47 } 48 } 49 50 // 壓縮存儲該行非零元 51 for(ccol = 1; ccol < qMatrix.nu; ccol++){ 52 if(ctemp[ccol]){ 53 if(++qMatrix.data.length > RLSMatrix.MAXSIZE) throw Error('overflow'); 54 qMatrix.data[qMatrix.data.length - 1] = new Triple(arow, ccol, ctemp[ccol]); 55 } 56 } 57 } 58 } 59 60 return qMatrix; 61 }, 62 _calcPos: function clcPos(){ 63 var num = []; 64 for(var col = 0; col < this.nu; col++) 65 num[col] = 0; 66 for(var i = 0; i < this.data.length; i++) 67 ++num[this.data[i].j]; // 求矩陣中每一列含非零元個數 68 // 求第col列中第一個非零元在b.data中的序號 69 for(col = 1; col < this.nu; col++) 70 // 上一列以前的序號+上一列的非零元個數 = 該列的序號 71 this.rpos[col] = this.rpos[col - 1] + num[col - 1]; 72 } 73 };
全部代碼:
1 /** 2 * 係數矩陣的三元組順序表存儲表示 3 */ 4 5 6 function Triple(i, j, elem) { 7 // 該非零元的行下標和列下標 8 this.i = i || 0; 9 this.j = j || 0; 10 this.e = elem || null; 11 } 12 13 function TSMatrix(mu, nu) { 14 // 非零元三元組表 15 this.data = []; 16 // 矩陣的行數,列數 17 this.mu = mu || 0; 18 this.nu = nu || 0; 19 } 20 TSMatrix.prototype = { 21 constructor: TSMatrix, 22 addTriple: function (triple) { 23 if (triple instanceof Triple) { 24 if(triple.i >= this.mu) 25 this.mu = triple.i + 1; 26 if(triple.j >= this.nu) 27 this.nu = triple.j + 1; 28 29 this.data.push(triple); 30 return true; 31 } 32 return false; 33 }, 34 // 採用三元組表存儲表示,求稀疏矩陣的轉置矩陣t 35 // 按照b.data中三元組的次序依次在a.data中找到相應的三元組進行轉置 36 transposeSMatrix: function () { 37 var t = new TSMatrix(); 38 t.mu = this.nu; 39 t.nu = this.mu; 40 41 if (this.data.length) { 42 var q = 0; 43 for (var col = 0; col < this.nu; col++) { 44 for (var p = 0; p < this.data.length; p++) { 45 if (this.data[p].j === col) 46 t.data[q++] = new Triple(this.data[p].j, this.data[p].i, this.data[p].e); 47 } 48 } 49 } 50 51 return t; 52 }, 53 // 採用三元組表存儲表示,求稀疏矩陣的轉置矩陣t 54 /* 55 按照a.data中三元組的次序進行轉置,並將轉置後的三元組置入b中恰當的位置。 56 若是能預先肯定矩陣M中每一列(即T中每一行)的第一個非零元在b.data中應有的位置, 57 那麼在對a.data中的三元組依次作轉置時,即可直接放到b.data中恰當的位置上去。 58 爲了其額定這些位置,在轉置前,應先求得M的每一列中非零元的個數,進而求得每一列的第一個非零元在b.data中應有的位置。 59 在此,須要設num和cpot兩個變量。num[col]表示矩陣M中第col列中非零元的個數, 60 cpot[col]指示M中第col列的第一個非零元在b.data中的恰當位置。顯然有: 61 cpot[0] = 1; 62 cpot[col] = cpot[col - 1] + num[col - 1] 2 <= col <= a.nu 63 */ 64 fastTransposeSMatrix: function(){ 65 var t = new TSMatrix(); 66 t.mu = this.nu; 67 t.nu = this.mu; 68 69 if(this.data.length){ 70 var num = []; 71 for(var col = 0; col < this.nu; col++) 72 num[col] = 0; 73 for(var i = 0; i < this.data.length; i++) 74 ++num[this.data[i].j]; // 求矩陣中每一列含非零元個數 75 // 求第col列中第一個非零元在b.data中的序號 76 var cpot = [0]; 77 for(col = 1; col < this.nu; col++) 78 // 上一列以前的序號+上一列的非零元個數 = 該列的序號 79 cpot[col] = cpot[col - 1] + num[col - 1]; 80 for(var p = 0; p < this.data.length; p++){ 81 col = this.data[p].j; 82 var q = cpot[col]; 83 t.data[q] = new Triple(this.data[p].j, this.data[p].i, this.data[p].e); 84 // 給該列的序號+1,用做相同列數的狀況 85 ++cpot[col]; 86 } 87 } 88 89 return t; 90 } 91 }; 92 93 var a1 = new Triple(1, 2, 12); 94 var a2 = new Triple(1, 3, 9); 95 var a3 = new Triple(3, 1, -3); 96 var a4 = new Triple(3, 6, 14); 97 var a5 = new Triple(4, 3, 24); 98 var a6 = new Triple(5, 2, 18); 99 var a7 = new Triple(6, 1, 15); 100 var a8 = new Triple(6, 4, -7); 101 102 var matrix = new TSMatrix(); 103 matrix.addTriple(a1); 104 matrix.addTriple(a2); 105 matrix.addTriple(a3); 106 matrix.addTriple(a4); 107 matrix.addTriple(a5); 108 matrix.addTriple(a6); 109 matrix.addTriple(a7); 110 matrix.addTriple(a8); 111 112 console.log(matrix.transposeSMatrix()); 113 console.log(matrix.fastTransposeSMatrix()); 114 115 /* 116 三元組順序表又稱有序的雙下標法,它的特色是,非零元在表中按行序有序存儲,所以便於進行依行順序處理的矩陣運算。 117 然而,若需按行號存取某一行的非零元,則從頭開始進行查找。 118 */ 119 120 /** 121 * 行邏輯連接的順序表 122 * 123 * 爲了便於隨機存取任意一行的非零元,則需知道每一行的第一個非零元在三元組表中的位置。 124 * 爲此可將快速轉置矩陣的算法中建立的,指示「行」信息的輔助數組cpot固定在稀疏矩陣的存儲結構中。 125 * 稱這種「帶行連接信息」的三元組表爲行邏輯連接的順序表 126 */ 127 128 function RLSMatrix(mu, nu){ 129 TSMatrix.apply(this, arguments); 130 this.rpos = [0]; 131 } 132 RLSMatrix.MAXSIZE = 100; 133 RLSMatrix.prototype = { 134 constructor: RLSMatrix, 135 __proto__: TSMatrix.prototype, 136 // todo 137 /** 138 * 求矩陣乘積Q = M * N,採用行邏輯連接存儲表示 139 * @param nMatrix 140 * @returns {RLSMatrix} 141 */ 142 multSMatrix: function(nMatrix){ 143 if(this.nu !== nMatrix.mu) throw Error('nu is not equivalent to mu'); 144 145 // 初始化Q 146 var qMatrix = new RLSMatrix(this.mu, nMatrix.nu); 147 // Q是非零矩陣 148 if(this.data.length * nMatrix.data.length !== 0){ 149 // 處理M的每一行 150 for(var arow = 0; arow < this.mu; arow++){ 151 // 當前行各元素累加器清零 152 var ctemp = []; 153 qMatrix.rpos[arow] = qMatrix.data.length + 1; 154 var tp, ccol; 155 156 if(arow < this.mu) 157 tp = this.rpos[arow + 1]; 158 else 159 tp = this.data.length + 1; 160 161 //對當前行中每個非零元找到對應元在N中的行號 162 for(var p = this.rpos[arow]; p < tp; p++){ 163 var brow = this.data[p].j; 164 var t; 165 if(brow < nMatrix.mu) 166 t = nMatrix.rpos[brow + 1]; 167 else 168 t = nMatrix.data.length + 1; 169 170 for(var q = nMatrix.rpos[brow]; q < t; q++){ 171 // 乘積元素在Q中的序號 172 ccol = nMatrix.data[q].j; 173 ctemp[ccol] = (ctemp[ccol] || 0) + this.data[p].e * nMatrix.data[q].e; 174 } 175 } 176 177 // 壓縮存儲該行非零元 178 for(ccol = 1; ccol < qMatrix.nu; ccol++){ 179 if(ctemp[ccol]){ 180 if(++qMatrix.data.length > RLSMatrix.MAXSIZE) throw Error('overflow'); 181 qMatrix.data[qMatrix.data.length - 1] = new Triple(arow, ccol, ctemp[ccol]); 182 } 183 } 184 } 185 } 186 187 return qMatrix; 188 }, 189 _calcPos: function clcPos(){ 190 var num = []; 191 for(var col = 0; col < this.nu; col++) 192 num[col] = 0; 193 for(var i = 0; i < this.data.length; i++) 194 ++num[this.data[i].j]; // 求矩陣中每一列含非零元個數 195 // 求第col列中第一個非零元在b.data中的序號 196 for(col = 1; col < this.nu; col++) 197 // 上一列以前的序號+上一列的非零元個數 = 該列的序號 198 this.rpos[col] = this.rpos[col - 1] + num[col - 1]; 199 } 200 }; 201 202 var b1 = new Triple(1, 1, 3); 203 var b2 = new Triple(1, 3, 5); 204 var b3 = new Triple(2, 2, -1); 205 var b4 = new Triple(3, 1, 2); 206 207 var t1 = new RLSMatrix(); 208 t1.addTriple(b1); 209 t1.addTriple(b2); 210 t1.addTriple(b3); 211 t1.addTriple(b4); 212 t1._calcPos(); 213 214 var c1 = new Triple(1, 2, 2); 215 var c2 = new Triple(2, 1, 1); 216 var c3 = new Triple(3, 1, -2); 217 var c4 = new Triple(3, 2, 4); 218 219 var t2 = new RLSMatrix(); 220 t2.addTriple(c1); 221 t2.addTriple(c2); 222 t2.addTriple(c3); 223 t2.addTriple(c4); 224 t2._calcPos(); 225 226 t1.multSMatrix(t2);