(原創)一般矩陣 Matrix類
討論矩陣的兩種表示方法,一種用一維陣列來儲存矩陣元素,另一種用二維陣列來儲存矩陣元素。然後比較兩種方法,並測試它們的效能,做出總結。
1.一維陣列形式
主要程式碼:
public class Matrix implements CloneableObject{
int rows,cols;
Object [] element;
public Matrix(int theRows,int theCols){
rows=theRows;
cols=theCols;
element=new Object[rows*cols];
}
public Object clone(){
Matrix x=new Matrix(rows,cols);
for(int i=0;i<rows*cols;i++){
x.element[i]=((CloneableObject)element[i]).clone();
}
return x;
}
public void copy(Matrix m){
if(this!=m){
rows=m.rows;
cols=m.cols;
element=new Object[rows*cols];
for(int i=0;i<rows*cols;i++){
element[i]=((CloneableObject)m.element[i]).clone();
}
}
}
public Object get(int i,int j){
checkIndex(i,j);
return element[(i-1)*cols+(j-1)];
}
private void checkIndex(int i, int j) {
// TODO Auto-generated method stub
if(i<1||j<1||i>rows||j>cols){
throw new IndexOutOfBoundsException("");
}
}
public void set(int i,int j,Object newValue){
checkIndex(i,j);
element[(i-1)*cols+(j-1)]=((CloneableObject)newValue).clone();
}
public Matrix add(Matrix m){
if(rows!=m.rows||cols!=m.cols){
throw new IllegalArgumentException("can not add");
}
Matrix w=new Matrix(rows,cols);
int n=rows*cols;
for(int i=0;i<n;i++){
w.element[i]=((Computable)element[i]).add(m.element[i]);
}
return w;
}
public Matrix sub(Matrix m){
if(rows!=m.rows||cols!=m.cols){
throw new IllegalArgumentException("can not subtract");
}
Matrix w=new Matrix(rows,cols);
int n=rows*cols;
for(int i=0;i<n;i++){
w.element[i]=((Computable)element[i]).subtract(m.element[i]);
}
return w;
}
public Matrix multiply(Matrix m){
if(cols!=m.rows){
throw new IllegalArgumentException("can not multiply");
}
Matrix w=new Matrix(rows,m.cols);
int ct=0,cm=0,cw=0;
for(int i=1;i<=rows;i++){
for(int j=1;j<=m.cols;j++){
Computable sum=(Computable) (((Computable)element[ct]).multiply(m.element[cm]));
for(int k=2;k<=cols;k++){
ct++;
cm+=m.cols;
sum.increment(((Computable)element[ct]).multiply(m.element[cm]));
}
w.element[cw++]=sum;
ct-=cols-1;
cm=j;
}
ct+=cols;
cm=0;
}
return w;
}
public Matrix transpose(){
Matrix w=new Matrix(cols,rows);
for(int i=1;i<=rows;i++){
for(int j=1;j<=cols;j++){
w.element[(j-1)*cols+i-1]=element[(i-1)*rows+j-1];
}
}
return w;
}
public Matrix decrement(Object x){
Matrix w=new Matrix(rows,cols);
int n=rows*cols;
for(int i=0;i<n;i++){
w.element[i]=((Computable)element[i]).decrement(x);
}
return w;
}
public Matrix increment(Object x){
Matrix w=new Matrix(rows,cols);
int n=rows*cols;
for(int i=0;i<n;i++){
w.element[i]=((Computable)element[i]).increment(x);
}
return w;
}
public Matrix multiplyByConstant(Object x){
Matrix w=new Matrix(rows,cols);
int n=rows*cols;
for(int i=0;i<n;i++){
w.element[i]=((Computable)element[i]).multiply(x);
}
return w;
}
public Matrix dividedByConstant(Object x){
Matrix w=new Matrix(rows,cols);
int n=rows*cols;
for(int i=0;i<n;i++){
w.element[i]=((Computable)element[i]).divide(x);
}
return w;
}
public String toString(){
StringBuilder s=new StringBuilder();
int n=rows*cols;
for(int i=0;i<n;i++){
s=s.append("\t"+element[i].toString()+" ");
if((i+1)%cols==0){
s.append("\n");
}
}
return s.toString();
}
}
這裡的介面CloneableObject只有一個clone方法
public interface CloneableObject extends Cloneable
{public Object clone();}
Computable介面程式碼:
public interface Computable
{
/** @return this + x */
public Object add(Object x);
/** @return this - x */
public Object subtract(Object x);
/** @return this * x */
public Object multiply(Object x);
/** @return quotient of this / x */
public Object divide(Object x);
/** @return remainder of this / x */
public Object mod(Object x);
/** @return this incremented by x */
public Object increment(Object x);
/** @return this decremented by x */
public Object decrement(Object x);
/** @return the additive zero element */
public Object zero();
/** @return the multiplicative identity element */
public Object identity();
}
這裡的Matrix類有三個資料成員rows,cols,element[],分別表示矩陣的行數,列數和矩陣的內容。並定義了構造方法public Matrix(int theRows,int theCols),矩陣建構函式的複雜度是O(rows*cols),如果我們假設複製一個矩陣項,兩個矩陣項想家以及將一個矩陣項轉換為字串的時間為θ(1),那麼方法clone,copy,add,toString的漸進複雜度也都是O(rows*cols)。矩陣乘法的複雜度是O(rows*cols*m.cols)。
2.二維陣列形式
主要程式碼
public class MatrixAs2DArray implements CloneableObject{
Object [][] element;
int rows,cols;
public MatrixAs2DArray(int rows,int cols){
element=new Object[rows][cols];
this.rows=rows;
this.cols=cols;
}
public Object clone(){
MatrixAs2DArray w=new MatrixAs2DArray(rows,cols);
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
w.element[i][j]=((CloneableObject)element[i][j]).clone();
}
}
return w;
}
public void copy(MatrixAs2DArray m){
rows=m.rows;
cols=m.cols;
element=new Object[rows][cols];
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
element[i][j]=((CloneableObject)m.element[i][j]).clone();
}
}
}
public Object get(int i,int j){
checkIndex(i,j);
return element[i-1][j-1];
}
private void checkIndex(int i, int j) {
// TODO Auto-generated method stub
if(i<1||j<1||i>rows||j>cols){
throw new IndexOutOfBoundsException("");
}
}
public void set(int i,int j,Object newValue){
checkIndex(i,j);
element[i-1][j-1]=((CloneableObject)newValue).clone();
}
public MatrixAs2DArray add(MatrixAs2DArray m){
if(rows!=m.rows||cols!=m.cols){
throw new IllegalArgumentException("can not add");
}
MatrixAs2DArray w=new MatrixAs2DArray(rows,cols);
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
w.element[i][j]=((Computable)element[i][j]).add(m.element[i][j]);
}
}
return w;
}
public MatrixAs2DArray subtract(MatrixAs2DArray m){
if(rows!=m.rows||cols!=m.cols){
throw new IllegalArgumentException("can not add");
}
MatrixAs2DArray w=new MatrixAs2DArray(rows,cols);
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
w.element[i][j]=((Computable)element[i][j]).subtract(m.element[i][j]);
}
}
return w;
}
public MatrixAs2DArray multiply(MatrixAs2DArray m){
if(cols!=m.rows){
throw new IllegalArgumentException("can not multiply");
}
MatrixAs2DArray w=new MatrixAs2DArray(rows,m.cols);
for (int i = 0; i < rows; i++)
for (int j = 0; j < w.cols; j++)
{// compute [i][j] term of result
// compute first term of w(i,j)
Computable sum = (Computable) ((Computable)element[i][0])
.multiply(m.element[0][j]);
// add in remaining terms
for (int k = 1; k < cols; k++)
sum.increment(((Computable) element[i][k]).multiply
(m.element[k][j]));
w.element[i][j] = sum;
}
return w;
}
public String toString(){
StringBuilder s=new StringBuilder();
for(int i=0;i<rows;i++){
for(int j=0;j<cols;j++){
s.append("\t"+element[i][j].toString()+" ");
}
s.append("\n");
}
return s.toString();
}
}
這裡為了以示區別類名稱用的是MatrixAs2DArray,依然是三個資料成員rows,cols,element[][],只是這裡element變成二維陣列了。其各個方法的複雜度與Matrix類是一樣的。
3.比較
設要表示的矩陣大小為m*n的,假設元素都是int型別的,如果用一維陣列(x[mn])儲存要佔用4mn+4個位元組,其中4mn個位元組用來儲存資料,4個位元組用來儲存陣列長度。如果用二維陣列(x[m][n])來儲存要佔用4mn+8m+4個位元組,其中4m個位元組用來儲存x[]指標,4個位元組用來儲存x[]指標長度,每一個x[]陣列要4n+4個位元組來儲存,共有m個。從記憶體角度上來說,一維陣列形式所佔記憶體較少更有優勢。
接下來對矩陣加法操作和乘法操作進行試驗來比較效能。
程式碼:
public class MatrixPerformanceTest {
public static void main(String args[]){
for(int n=30;n<1000;n=n*2){
Matrix m1=new Matrix(n,n);
Matrix m2=new Matrix(n,n);
MatrixAs2DArray ma1=new MatrixAs2DArray(n,n);
MatrixAs2DArray ma2=new MatrixAs2DArray(n,n);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
MyInteger q1 = new MyInteger(2 * i + j);
MyInteger q2 = new MyInteger(2 *j+3*i);
m1.set(i, j, q1);
m2.set(i, j, q2);
ma1.set(i, j, q1);
ma2.set(i, j, q2);
}
long startTime1=System.currentTimeMillis();
int count1=0;
do{
m1.add(m2);
count1++;
}while((System.currentTimeMillis()-startTime1)<1000);
long elapsedTime1=(System.currentTimeMillis()-startTime1)/count1;
System.out.print("n="+n+"時:1方法加法耗時:"+elapsedTime1+" ");
long startTime2=System.currentTimeMillis();
int count2=0;
do{
ma1.add(ma2);
count2++;
}while((System.currentTimeMillis()-startTime2)<1000);
long elapsedTime2=(System.currentTimeMillis()-startTime2)/count2;
System.out.println("n="+n+"時:2方法加法耗時:"+elapsedTime2);
long startTime3=System.currentTimeMillis();
int count3=0;
do{
m1.multiply(m2);
count3++;
}while((System.currentTimeMillis()-startTime3)<1000);
long elapsedTime3=(System.currentTimeMillis()-startTime3)/count3;
System.out.print("n="+n+"時:1方法乘法耗時:"+elapsedTime3+" ");
long startTime4=System.currentTimeMillis();
int count4=0;
do{
ma1.multiply(ma2);
count4++;
}while((System.currentTimeMillis()-startTime4)<1000);
long elapsedTime4=(System.currentTimeMillis()-startTime4)/count4;
System.out.println("n="+n+"時:2方法乘法耗時:"+elapsedTime4);
}
System.out.println("測試結束");
}
}
結果:
n=30時:1方法加法耗時:0 n=30時:2方法加法耗時:0
n=30時:1方法乘法耗時:0 n=30時:2方法乘法耗時:0
n=60時:1方法加法耗時:0 n=60時:2方法加法耗時:0
n=60時:1方法乘法耗時:4 n=60時:2方法乘法耗時:3
n=120時:1方法加法耗時:0 n=120時:2方法加法耗時:0
n=120時:1方法乘法耗時:35 n=120時:2方法乘法耗時:35
n=240時:1方法加法耗時:1 n=240時:2方法加法耗時:2
n=240時:1方法乘法耗時:362 n=240時:2方法乘法耗時:378
n=480時:1方法加法耗時:5 n=480時:2方法加法耗時:8
n=480時:1方法乘法耗時:3752 n=480時:2方法乘法耗時:3952
n=960時:1方法加法耗時:44 n=960時:2方法加法耗時:34
n=960時:1方法乘法耗時:37277 n=960時:2方法乘法耗時:39904
測試結束
從結果可以看到對於加法,二維陣列形式表現更好,但差別不大,對於乘法一維陣列形式表現更好。
4.總結
實際上以上兩種方法矩陣的乘法還可以進一步改進。這裡以對二維形式表示的矩陣類的乘法為例進行改進(一維陣列形式可以類似進行修改)
程式碼:
public MatrixAs2DArray multiply(MatrixAs2DArray m){
if(cols!=m.rows){
throw new IllegalArgumentException("can not multiply");
}
MatrixAs2DArray w=new MatrixAs2DArray(rows,m.cols);
for(int i=0;i<rows;i++){
for(int j=0;j<m.cols;j++){
w.element[i][j]=((Computable)element[i][0]).multiply(m.element[0][j]);
}
}
for(int i=0;i<rows;i++){
for(int k=1;k<cols;k++){
for(int j=0;j<m.cols;j++){
Object temp=((Computable)element[i][k]).multiply(m.element[k][j]);
w.element[i][j]=((Computable) w.element[i][j]).add(temp);
}
}
}
}
實際上這裡只是對乘法裡面的三個巢狀的for迴圈順序做了修改,但是由於將相乘的兩個矩陣都行優先進行讀取計算,使得快取遺漏減少,增加了運算效率。下面是用改進後的二維陣列形式進行的與之前一樣的測試。
結果:
n=30時:1方法加法耗時:0 n=30時:2方法加法耗時:0
n=30時:1方法乘法耗時:0 n=30時:2方法乘法耗時:1
n=60時:1方法加法耗時:0 n=60時:2方法加法耗時:0
n=60時:1方法乘法耗時:3 n=60時:2方法乘法耗時:8
n=120時:1方法加法耗時:0 n=120時:2方法加法耗時:0
n=120時:1方法乘法耗時:34 n=120時:2方法乘法耗時:71
n=240時:1方法加法耗時:1 n=240時:2方法加法耗時:1
n=240時:1方法乘法耗時:331 n=240時:2方法乘法耗時:568
n=480時:1方法加法耗時:5 n=480時:2方法加法耗時:8
n=480時:1方法乘法耗時:3827 n=480時:2方法乘法耗時:4051
n=960時:1方法加法耗時:31 n=960時:2方法加法耗時:20
n=960時:1方法乘法耗時:36809 n=960時:2方法乘法耗時:31258
測試結束
可以看到乘法明顯比修改之前更快,甚至超過了一維陣列形式的乘法。不過如果對一維陣列形式的乘法也進行改進,其乘法運算速度還是會超過二維陣列形式的。
相關文章
- 【矩陣乘法】Matrix Power Series矩陣
- Cellular Matrix 蜂窩矩陣(一)矩陣
- 張量(Tensor)、標量(scalar)、向量(vector)、矩陣(matrix)矩陣
- 動手畫混淆矩陣(Confusion Matrix)(含程式碼)矩陣
- flutter佈局-5-Matrix4矩陣變換Flutter矩陣
- THREE 矩陣優先原則和平移旋轉矩陣矩陣
- 非科班程式設計師才不知道的矩陣Matrix程式設計師矩陣
- 利用瓊斯矩陣求解一般偏振問題矩陣
- SciTech-Matrix Analysis of Management+Theory-管理科學的“矩陣式分析”矩陣
- POJ 3233 Matrix Power Series (矩陣快速冪+等比數列二分求和)矩陣
- 巨大的矩陣(矩陣加速)矩陣
- 鄰接矩陣、度矩陣矩陣
- 奇異矩陣,非奇異矩陣,偽逆矩陣矩陣
- 資料結構:陣列,稀疏矩陣,矩陣的壓縮。應用:矩陣的轉置,矩陣相乘資料結構陣列矩陣
- 矩陣矩陣
- 求任意矩陣的伴隨矩陣矩陣
- 矩陣和陣列矩陣陣列
- Flutter 45: 圖解矩陣變換 Transform 類 (二)Flutter圖解矩陣ORM
- 矩陣乘法矩陣
- 螺旋矩陣矩陣
- 8.6 矩陣?矩陣
- 找矩陣矩陣
- 海浪矩陣矩陣
- 矩陣相乘矩陣
- 稀疏矩陣矩陣
- 矩陣分解矩陣
- 理解矩陣矩陣
- 快手矩陣管理平臺,矩陣管理有方法矩陣
- 機器學習中的矩陣向量求導(五) 矩陣對矩陣的求導機器學習矩陣求導
- 演算法學習:矩陣快速冪/矩陣加速演算法矩陣
- 矩陣:如何使用矩陣操作進行 PageRank 計算?矩陣
- 矩陣求逆矩陣
- 雅可比矩陣矩陣
- leetcode:螺旋矩陣LeetCode矩陣
- 矩陣置0矩陣
- 矩陣快速冪矩陣
- 隨機矩陣隨機矩陣
- 矩陣求導矩陣求導
- 矩陣計算矩陣