pytorch统计矩阵非0的个数_矩阵的三种存储方式---三元组法 行逻辑链接法 十字链表法...
在介紹矩陣的壓縮存儲(chǔ)前,我們需要明確一個(gè)概念:對(duì)于特殊矩陣,比如對(duì)稱矩陣,稀疏矩陣,上(下)三角矩陣,在數(shù)據(jù)結(jié)構(gòu)中相同的數(shù)據(jù)元素只存儲(chǔ)一個(gè)。 @[TOC]
三元組順序表
稀疏矩陣由于其自身的稀疏特性,通過壓縮可以大大節(jié)省稀疏矩陣的內(nèi)存代價(jià)。具體操作是:將非零元素所在的行、列以及它的值構(gòu)成一個(gè)三元組(i,j,v),然后再按某種規(guī)律存儲(chǔ)這些三元組,這種方法可以節(jié)約存儲(chǔ)空間 。 如下圖所示為一個(gè)稀疏矩陣,我們應(yīng)該怎么樣存儲(chǔ)呢?
若對(duì)其進(jìn)行壓縮存儲(chǔ),我們可以將一個(gè)非零數(shù)組元素的三元看成一個(gè)單位存入一維數(shù)組,具體如下所示。比如(1,1,1)代表第一行第一列的元素值為1。注意,這里我們只存儲(chǔ)非零值。 具體代碼如下: #include<stdio.h> #include <stdlib.h> #include <time.h> #define NUMBER 3//三元組結(jié)構(gòu)體 typedef struct {//行標(biāo)r,列標(biāo)cint r,c;//元素值int data; }triple; //矩陣的結(jié)構(gòu)表示 typedef struct {//存儲(chǔ)該矩陣中所有非0元素的三元組triple data[NUMBER];//row和column分別記錄矩陣的行數(shù)和列數(shù),num記錄矩陣中所有的非0元素的個(gè)數(shù)int row,column,num; }TSMatrix; //輸出存儲(chǔ)的稀疏矩陣 void print(TSMatrix M); int main() {int i;srand((unsigned)time(NULL));TSMatrix M;M.row=3;M.column=3;M.num=3;//初始化矩陣for(i=0;i<M.num;i++){//隨機(jī)數(shù)范圍[1,3]M.data[i].r=rand()%M.num+1;M.data[i].c=rand()%M.num+1;M.data[i].data=rand()%10;}print(M);return 0; } void print(TSMatrix M){for(int i=1;i<=M.row;i++){for(int j=1;j<=M.column;j++){int value =0;for(int k=0;k<M.num;k++){//遍歷時(shí)的r,c行列值和實(shí)際存儲(chǔ)row,column行列值的比較,相同的話就說明有非零元素,打印存儲(chǔ)的非0值if(i == M.data[k].r && j == M.data[k].c){printf("%d ",M.data[k].data);value =1;break;}}if(value == 0)printf("0 ");}printf("n");} }行邏輯鏈接的順序表
使用三元組順序表存儲(chǔ)稀疏矩陣,我們每次訪問其中一個(gè)元素都要遍歷整個(gè)矩陣,效率比較低。我們可以使用一個(gè)一維數(shù)組來存儲(chǔ)每行第一個(gè)非零元素在一維數(shù)組中的位置,這樣就可以提升訪問效率。這樣的表就叫做行邏輯鏈接的順序表。 下圖為一個(gè)稀疏矩陣,當(dāng)使用行邏輯鏈接的順序表對(duì)其進(jìn)行壓縮存儲(chǔ)時(shí),需要做以下兩個(gè)工作:
1.將矩陣中的非 0 元素采用三元組的形式存儲(chǔ)到一維數(shù)組 data 中: 2.使用數(shù)組 rpos 記錄矩陣中每行第一個(gè)非 0 元素在一維數(shù)組中的存儲(chǔ)位置。 通過以上兩步操作,即實(shí)現(xiàn)了使用行邏輯鏈接的順序表存儲(chǔ)稀疏矩陣。 此時(shí),如果想從行邏輯鏈接的順序表中提取元素,則可以借助 rpos 數(shù)組提高遍歷數(shù)組的效率。 例如,提取圖 1 稀疏矩陣中的元素 2 的過程如下: 由 rpos 數(shù)組可知,第一行首個(gè)非 0 元素位于data[1],因此在遍歷此行時(shí),可以直接從第 data[1] 的位置開始,一直遍歷到下一行首個(gè)非 0 元素所在的位置(data[3])之前; 同樣遍歷第二行時(shí),由 rpos 數(shù)組可知,此行首個(gè)非 0 元素位于 data[3],因此可以直接從第 data[3] 開始,一直遍歷到下一行首個(gè)非 0 元素所在的位置(data[4])之前;遍歷第三行時(shí),由 rpos 數(shù)組可知,此行首個(gè)非 0 元素位于 data[4],由于這是矩陣的最后一行,因此一直遍歷到 rpos 數(shù)組結(jié)束即可(也就是 data[tu],tu 指的是矩陣非 0 元素的總個(gè)數(shù))。 具體代碼如下 #include <stdio.h> #include <stdlib.h> #include <time.h> #define MAXSIZE 12500 #define MAXRC 100 typedef struct {//行,列int r,c;//元素值int e; }Triple;typedef struct {//矩陣中元素的個(gè)數(shù)Triple data[MAXSIZE];//每行第一個(gè)非零元素在data數(shù)組中的位置int rpos[MAXRC];//行數(shù),列數(shù),元素個(gè)數(shù)int row,column,number; }RLSMatrix; //矩陣的輸出函數(shù) void print(RLSMatrix M){for(int i=1;i<=M.row;i++){for(int j=1;j<=M.column;j++){int value=0;if(i+1 <=M.row){for(int k=M.rpos[i];k<M.rpos[i+1];k++){if(i == M.data[k].r && j == M.data[k].c){printf("%d ",M.data[k].e);value=1;break;}}if(value==0){printf("0 ");}}else{for(int k=M.rpos[i];k<=M.number;k++){if(i == M.data[k].r && j == M.data[k].c){printf("%d ",M.data[k].e);value=1;break;}}if(value==0){printf("0 ");}}}printf("n");} } int main() {int i;//srand((unsigned)time(NULL));RLSMatrix M;//矩陣的大小M.number = 4;M.row = 3;M.column = 4;//每一行首個(gè)非零元素在一維數(shù)組中的位置M.rpos[1] = 1;M.rpos[2] = 3;M.rpos[3] = 4;M.data[1].e = 3;M.data[1].r = 1;M.data[1].c = 2;M.data[2].e = 5;M.data[2].r = 1;M.data[2].c = 4;M.data[3].e = 1;M.data[3].r = 2;M.data[3].c = 3;M.data[4].e = 2;M.data[4].r = 3;M.data[4].c = 1;//輸出矩陣print(M);return 0; }十字鏈表法
關(guān)于十字鏈表法,具體可看下圖。我們把矩陣的每一行每一列分別看成一個(gè)鏈表,然后將每一行和每一列的鏈表的第一個(gè)元素存放在一個(gè)數(shù)組中。這個(gè)數(shù)組就叫行鏈表的頭指針數(shù)組,列鏈表的頭指針數(shù)組。當(dāng)我們?cè)L問矩陣的時(shí)候,就可以從行/列頭指針數(shù)組中取出對(duì)應(yīng)的指針,就可以訪問這一行或者這一列的元素了。
鏈表中節(jié)點(diǎn)的結(jié)構(gòu)應(yīng)如下圖。所以,除了定義三元組的行,列,數(shù)值外,我們還需要定義指向行的指針,指向列的指針。最后還需要定義一個(gè)存放行/列鏈表頭結(jié)點(diǎn)的數(shù)組專門存放各行各列的頭結(jié)點(diǎn)。具體代碼如下。
typedef struct CLNode {//矩陣三元組i代表行 j代表列 e代表當(dāng)前位置的數(shù)據(jù)int r, c, data; //指針域 行指針 列指針struct CLNode *prow, *pcolumn; }CLNode, *CLink; typedef struct {//行和列鏈表頭數(shù)組 CLink rhead[] 這樣寫也可以。寫成指針是為了方便動(dòng)態(tài)分配內(nèi)存CLink *rhead, *chead; //矩陣的行數(shù),列數(shù)和非零元的個(gè)數(shù)int rows, columns, num; }CrossList;下面我們將要根據(jù)用戶輸入的行數(shù),列數(shù),非零元素的值,來創(chuàng)建矩陣。
//注意檢查用戶的輸入do { flag = 1; printf("輸入矩陣的行數(shù)、列數(shù)和非0元素個(gè)數(shù):");scanf("%d%d%d",&m,&n,&t);if (m<0 || n<0 || t<0 || t>m*n) flag = 0; }while (!flag); M.rows = m;M.columns = n;M.num = t;用戶輸入合法的情況下我們要?jiǎng)?chuàng)建并初始化存放行列鏈表頭的數(shù)組。
//因?yàn)橄聵?biāo)從1開始,所以頭結(jié)點(diǎn)指針多分配一個(gè)內(nèi)存if (!(M.rhead = (CLink*)malloc((m + 1) * sizeof(CLink))) || !(M.chead = (CLink*)malloc((n + 1) * sizeof(CLink)))){printf("初始化矩陣失敗rn");exit(0);}// 初始化行頭指針向量;各行鏈表為空鏈表 for (r = 1; r <= m; r++){M.rhead[r] = NULL;}// 初始化列頭指針向量;各列鏈表為空鏈表 for (c = 1; c <= n; c++){M.chead[c] = NULL;}存放行列鏈表頭的數(shù)組準(zhǔn)備好了,接下來我們就要?jiǎng)?chuàng)建數(shù)據(jù)節(jié)點(diǎn)了。根據(jù)用戶輸入的行號(hào),列好,數(shù)值創(chuàng)建節(jié)點(diǎn)。這里同樣要檢查用戶的輸入。
for (scanf("%d%d%d", &r,&c,&data); ((r<=0)||(c<=0)); scanf("%d%d%d", &r,&c,&data)) {if (!(p = (CLNode*)malloc(sizeof(CLNode)))){printf("初始化三元組失敗");exit(0);}p->r = r;p->c = c;p->data= data; }當(dāng)創(chuàng)建好一個(gè)節(jié)點(diǎn)之后,我們就要放到行或者列的正確的位置。根據(jù)輸入的行號(hào)列號(hào)確定插入的位置。那么應(yīng)該怎樣去插入?分兩種情況 1、當(dāng)這一行中沒有結(jié)點(diǎn)的時(shí)候,那么我們直接插入 2、當(dāng)這一行中有結(jié)點(diǎn)的時(shí)候我們插入到正確的位置 對(duì)于第一個(gè)問題,因?yàn)橹耙呀?jīng)對(duì)頭結(jié)點(diǎn)數(shù)組進(jìn)行了初始化NULL,所以直接根據(jù)NULL==M->rhead[i]就可以判斷一行中有沒有節(jié)點(diǎn)。 對(duì)于第二個(gè)問題,當(dāng)行中有節(jié)點(diǎn)的時(shí)候,無非是插入到某個(gè)節(jié)點(diǎn)之前或者某個(gè)節(jié)點(diǎn)之后。什么時(shí)候插入到節(jié)點(diǎn)前?什么時(shí)候插入到節(jié)點(diǎn)后呢? 1.插入節(jié)點(diǎn)前:當(dāng)我們要插入的節(jié)點(diǎn)的列號(hào)小于已經(jīng)存在的節(jié)點(diǎn)的列號(hào),這個(gè)時(shí)候就要插入到這個(gè)節(jié)點(diǎn)之前了。 2.插入節(jié)點(diǎn)后:當(dāng)我們要插入的節(jié)點(diǎn)的列號(hào)大于已經(jīng)存在的節(jié)點(diǎn)的列號(hào),這個(gè)時(shí)候就要插入到這個(gè)節(jié)點(diǎn)之后了。 對(duì)于第一種情況,代碼如下。
//p為準(zhǔn)備插入的節(jié)點(diǎn),要插入到M.rhead[r]之前。if (NULL == M.rhead[r] || M.rhead[r]->c> c){p->prow = M.rhead[r];//M.rhead[r]要始終指向行的第一個(gè)節(jié)點(diǎn)M.rhead[r] = p;}對(duì)于第二種情況,我們要插入的節(jié)點(diǎn)插入到已有節(jié)點(diǎn)的后面,那么,已有將要插入節(jié)點(diǎn)的列號(hào)必定大于已有節(jié)點(diǎn)的列號(hào)。我們只要找到一個(gè)節(jié)點(diǎn)比我們將要插入節(jié)點(diǎn)的列號(hào)大的節(jié)點(diǎn)就好,然后插入到這個(gè)節(jié)點(diǎn)的前面。如果現(xiàn)有的結(jié)點(diǎn)沒有一個(gè)結(jié)點(diǎn)列號(hào)是大于要插入的節(jié)點(diǎn)的列號(hào)的,那么我們就應(yīng)該插入到最后一個(gè)結(jié)點(diǎn)之后!
//我們要找到一個(gè)比q節(jié)點(diǎn)大的節(jié)點(diǎn)。在這個(gè)節(jié)點(diǎn)之前插入for (q = M.rhead[r]; (q->prow) && q->prow->c < c; q = q->prow);p->prow = q->prow;q->prow = p;對(duì)于列的插入同樣如此,就不一一分析了,下面給出具體代碼。
//鏈接到列的指定位置if (NULL == M.chead[c] || M.chead[c]->r> r){p->pcolumn = M.chead[c];M.chead[c] = p;}else{for (q = M.chead[c]; (q->pcolumn) && q->pcolumn->r < r; q = q->pcolumn);p->pcolumn = q->pcolumn;q->pcolumn = p;}打印矩陣 對(duì)于十字鏈表矩陣的打印,我們每次從行/列頭結(jié)點(diǎn)數(shù)組中取出每一行或者每一列的第一個(gè)節(jié)點(diǎn)依次往下訪問就可以了,和普通的鏈表訪問沒有區(qū)別。如果對(duì)鏈表不熟悉的可以參考這篇文章史上最全單鏈表的增刪改查反轉(zhuǎn)等操作匯總以及5種排序算法(C語言)
void PrintClist(CrossList M) {for (int i = 1; i <= M.num; i++){if (NULL != M.chead[i]){CLink p = M.chead[i];while (NULL != p){printf("%dt%dt%dn",p->r, p->c, p->data);p = p->pcolumn;}}} }完整代碼如下:
/** @Description: 十字鏈表存儲(chǔ)壓縮矩陣* @Version: V1.0* @Autor: Carlos* @Date: 2020-05-26 16:43:48* @LastEditors: Carlos* @LastEditTime: 2020-05-28 14:40:19*/ #include<stdio.h> #include<stdlib.h> typedef struct CLNode {//矩陣三元組i代表行 j代表列 e代表當(dāng)前位置的數(shù)據(jù)int r, c, data; //指針域 行指針 列指針struct CLNode *prow, *pcolumn; }CLNode, *CLink; typedef struct {//行和列鏈表頭指針CLink *rhead, *chead; //矩陣的行數(shù),列數(shù)和非零元的個(gè)數(shù)int rows, columns, num; }CrossList; CrossList InitClist(CrossList M) {CLNode *p,*q;int r,c,data;int m, n, t;int flag;// printf("輸入矩陣的行數(shù)、列數(shù)和非0元素個(gè)數(shù):");// scanf("%d%d%d",&m,&n,&t);do { flag = 1; printf("輸入矩陣的行數(shù)、列數(shù)和非0元素個(gè)數(shù):");scanf("%d%d%d",&m,&n,&t);if (m<0 || n<0 || t<0 ) flag = 0; }while (!flag); M.rows = m;M.columns = n;M.num = t;//因?yàn)橄聵?biāo)從1開始,所以頭結(jié)點(diǎn)指針多分配一個(gè)內(nèi)存if (!(M.rhead = (CLink*)malloc((m + 1) * sizeof(CLink))) || !(M.chead = (CLink*)malloc((n + 1) * sizeof(CLink)))){printf("初始化矩陣失敗rn");exit(0);}// 初始化行頭指針向量;各行鏈表為空鏈表 for (r = 1; r <= m; r++){M.rhead[r] = NULL;}// 初始化列頭指針向量;各列鏈表為空鏈表 for (c = 1; c <= n; c++){M.chead[c] = NULL;} //行數(shù)列數(shù)不為0 for (scanf("%d%d%d", &r,&c,&data); ((r<=0)||(c<=0)); scanf("%d%d%d", &r,&c,&data)) {if (!(p = (CLNode*)malloc(sizeof(CLNode)))){printf("初始化三元組失敗");exit(0);}p->r = r;p->c = c;p->data= data;//鏈接到行的指定位置。 if (NULL == M.rhead[r] || M.rhead[r]->c> c){p->prow = M.rhead[r];M.rhead[r] = p;}else{for (q = M.rhead[r]; (q->prow) && q->prow->c < c; q = q->prow);p->prow = q->prow;q->prow = p;} //鏈接到列的指定位置if (NULL == M.chead[c] || M.chead[c]->r> r){p->pcolumn = M.chead[c];M.chead[c] = p;}else{for (q = M.chead[c]; (q->pcolumn) && q->pcolumn->r < r; q = q->pcolumn);p->pcolumn = q->pcolumn;q->pcolumn = p;} }return M; }void PrintClist(CrossList M) {for (int i = 1; i <= M.num; i++){if (NULL != M.chead[i]){CLink p = M.chead[i];while (NULL != p){printf("%dt%dt%dn",p->r, p->c, p->data);p = p->pcolumn;}}} } int main() {CrossList M;M.rhead = NULL;M.chead = NULL;M = InitClist(M);PrintClist(M);return 0; }文中代碼均已測(cè)試,有任何意見或者建議均可聯(lián)系我。歡迎學(xué)習(xí)交流! 如果覺得寫的不錯(cuò),請(qǐng)點(diǎn)個(gè)贊再走,謝謝! 如遇到排版錯(cuò)亂的問題或者有任何疑問、建議,可以在“我的主頁”找到我的聯(lián)系方式和我的博客鏈接。
總結(jié)
以上是生活随笔為你收集整理的pytorch统计矩阵非0的个数_矩阵的三种存储方式---三元组法 行逻辑链接法 十字链表法...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java web响应式框架_Web开发的
- 下一篇: 用定时器控制灯的闪烁梯形图_用西门子PL