Java_ sparse matrix fast transposition algorithm (+ debugging experience)

Main category

/*
* @Description:
* @Version: 2.0
* @Author: xuchaoxin
* @Date: 2021-03-09 19:27:00
 * @LastEditors: xuchaoxin
 * @LastEditTime: 2021-03-10 20:47:09
*/

import java.util.ArrayList;
import java.util.TreeMap;

/**
 * @Author xuchaoxin
 * @Date 2021/3/9 21:00
 * @Version 1.0 the detail and improved version(if I update it ) to see: the
 * github repository (search the
 * article):https://github.com/xuchaoxin1375/LearnJava
 */
public class main {
    
    
    /**
     * 主函数,测试这个系统的功能*/
public static void main(String[] args) {
    
    
    /*创建可以读取矩阵的对象*/
    InputMatrix inputMatrix = new InputMatrix();
    /*读取矩阵*/
    SparseMatrix sparseMatrix1 = inputMatrix.getSparseMatrix();
    /*执行转置,并打印矩阵*/
    translate(sparseMatrix1).print();

}
/**调度各个功能方法,得到以行优先顺序的稀疏矩阵的元素(三元组)所构成的列表所构成的对象:SparseMatrix*/
static SparseMatrix translate(SparseMatrix sparseMatrix1) {
    
    
    /**
     *
     * 快速转置算法充分利用了Triple结构中的坐标成员 实质是对三元组列表中的元素进行调序(某种规则下的重排序),以得到一个等长度的新列表
     *
     * 目的是求得转置后的三元组列表(即计算该列表中的(非零)元素是怎么排列的),而不是直接区计算得到二维分布(届时写一个解压函数来将位置分布呈现出来)
     */
    SparseMatrix sparseMatrix2 = new SparseMatrix();
    setProfile(sparseMatrix1, sparseMatrix2);
    /*定义计数器(注意,有必要指明该计数器中保存的值表示什么:保存的是转置矩阵各行所具有的非零元素个数
    * 那么,第一行是从0计还是从1计数?
    * 这里就规定,从0行开始计数,那么统计过程中各个元素的坐标也要从0开始计数*/
    int[] elementNumOfEachRow2 = new int[sparseMatrix1.ncol];
    /*初始换计数器*/
    for (int i = 0; i < sparseMatrix1.ncol; i++) {
    
    
        elementNumOfEachRow2[i] = 0;
    }
    /* 统计转置后的矩阵的各个行中具有的非零元素的个数 */
    for (int i = 0; i < sparseMatrix1.num; i++) {
    
    
        /*调试需要:*/

        elementNumOfEachRow2[sparseMatrix1.getTriple(i).col - 1]++;
        System.out.println(sparseMatrix1.getTriple(i));
    }
    /*  */
    int[] startInsertArray = divideZone(sparseMatrix1, elementNumOfEachRow2);
    getResortTripleList(startInsertArray, sparseMatrix1, sparseMatrix2);

    return sparseMatrix2;

}
/**设置转置矩阵总体SparseMatrix对象包含的信息参数*/
static void setProfile(SparseMatrix sparseMatrix1, SparseMatrix sparseMatrix2) {
    
    
    sparseMatrix2.ncol = sparseMatrix1.nrow;
    sparseMatrix2.nrow = sparseMatrix1.ncol;
    sparseMatrix2.num = sparseMatrix1.num;
}

/** 将转置矩阵的三元组数组以各个行中具有的非零元素数目为标准,做相应分区划段,来存储各行中的元素;需要划分的段数为转置前矩阵的列数 */
static int[] divideZone(SparseMatrix sparseMatrix, int[] elementNumOfEachRow2) {
    
    

    int[] startInsertArray = new int[sparseMatrix.ncol];
    startInsertArray[0] = 0;// 初始化第一个值,手动初始化指明(从[0]个位置开始插入),为地推做准备
    for (int i = 1; /* i表示为转置矩阵的每一行计算其区间段的宽度(以存放对应行的非零元素) */
            i < sparseMatrix.ncol; i++) {
    
    
        startInsertArray[i] = startInsertArray[i - 1] + elementNumOfEachRow2[i - 1];
    }

    return startInsertArray;
}
/** 调序插入到转置矩阵中的三元组列表中(核心步骤) */
static void getResortTripleList(int[] startInsertArray, SparseMatrix sparseMatrix1, SparseMatrix sparseMatrix2) {
    
    
    /*提取常用表达式的计算结果,简化代码*/
    int position;
    Triple elementPoint, srcElementPoint;
    /*批量生成Triple对象*/
    for (int j = 0; j < sparseMatrix2.num; j++) {
    
    
        sparseMatrix2.tripleList.add(new Triple());
    }
    for (int i = 0; i < sparseMatrix1.num; i++) {
    
    
        /* 先计算当前非零元素在转置三元组列表中会落在那个区间(左边界) */
        position = getPosition(i, startInsertArray, sparseMatrix1);
        /*检查position的值:  */
        System.out.println(position);

        elementPoint = sparseMatrix2.getTriple(position);

        srcElementPoint = sparseMatrix1.getTriple(i);
        /* 以下该值操作不影响原矩阵中的三元组 */
        elementPoint.setRow(srcElementPoint.getCol());
        elementPoint.setCol(srcElementPoint.getRow());
        elementPoint.setData(srcElementPoint.getData());
        /* 修改该元素所在行的下一个元素要插入的位置(虽然不一定还会有) */
        updateStartInsertArray(i, startInsertArray, sparseMatrix1);
    }

}

/** 用于计算原始矩阵(三元组列表)中各个元素转置到新矩阵中时会在第几行,并依此计算在该元素所在的行在转置矩阵对象中的三元组列表的起始插入位置索引
* 应当配和updateStartInsertArray()方法,来确保当同一行的非零元素有多个时的情况能够正确计算索引并插入
* 当然这两个方法不是必须的,可以都嵌在translate方法中,这里只是为例划分逻辑*/
static int getPosition(int index, int[] startInsertArray, SparseMatrix sparseMatrix1) {
    
    
    return startInsertArray[sparseMatrix1.getTriple(index).getCol()-1];

}
/**更新插入到转置矩阵三元组列表中索引值(针对当同一行非零元素不知1个时所作出的索引引导修正)*/
static void updateStartInsertArray(int index, int[] startInsertArray, SparseMatrix sparseMatrix1) {
    
    
    startInsertArray[sparseMatrix1.tripleList.get(index).col-1]++;
}

}
/**三元组中各个非零元素的类*/
class Triple {
    
    
/**
 * 稀疏矩阵中的单个元素包含的信息
 */
int row; /* 横坐标 */
int col; /* 纵坐标 */
int data;/* 值 */

@Override
/**用于调试打印元素信息使用:*/
public String toString() {
    
    
    return "Triple{" +
            "row=" + row +
            ", col=" + col +
            ", data=" + data +
            '}';//+"注意这里坐标是从1计数";

}

/**javaBean 基本操作系列*/
public int getRow() {
    
    
    return this.row;
}

public void setRow(int row) {
    
    
    this.row = row;
}

public int getCol() {
    
    
    return this.col;
}

public void setCol(int col) {
    
    
    this.col = col;
}

public int getData() {
    
    
    return this.data;
}

public void setData(int data) {
    
    
    this.data = data;
}

}

/**稀疏矩阵整体的数据结构(保存的是压缩成三元组数组/列表的形式) */
class SparseMatrix {
    
    
public void setTripleList(ArrayList<Triple> tripleList) {
    
    
    this.tripleList = tripleList;
}

public void setNrow(int nrow) {
    
    
    this.nrow = nrow;
}

public void setNcol(int ncol) {
    
    
    this.ncol = ncol;
}

public void setNum(int num) {
    
    
    this.num = num;
}

ArrayList<Triple> tripleList = new ArrayList<>();
int nrow, /* 矩阵的行数 */
        ncol, /* 矩阵的列数 */
        num;

Triple getTriple(int index) {
    
    
    return tripleList.get(index);
}
/**解压打印稀疏矩阵(中的三元组列表),非静态方法不需要设置参数*/
void print(/*SparseMatrix sparseMatrix*/) {
    
    
    int[][] matrix = new int[nrow][ncol];

    /*填充数组*/
    for (int i = 0; i < num; i++) {
    
    
        Triple triple = getTriple(i);
        if (triple.getData() != 0) {
    
    
            matrix[triple.getRow()-1][triple.getCol()-1] = triple.getData();
        }
    }
    /*打印矩阵(二维数组)*/
    for (int i = 0; i < nrow; i++) {
    
    
        for (int j = 0; j < ncol; j++) {
    
    
            System.out.print(matrix[i][j]+" ");
        }
        System.out.println();
    }
}
}

Auxiliary function class (read in a matrix of any specification)

/*
 * @Description:
 * @Version: 2.0
 * @Author: xuchaoxin
 * @Date: 2021-03-10 16:24:37
 * @LastEditors: xuchaoxin
 * @LastEditTime: 2021-03-10 17:35:31
 */

import java.io.Reader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Scanner;

public class InputMatrix {
    
    
    /**
     * 读入矩阵(任意长度和宽度) 以两个连续的enter键来结束矩阵的输入
     */
    public static int[][] ReadMatrix() {
    
    
        /* 代开输入流(标准输入流) */
        Scanner sc = new Scanner(System.in);
        /* 建立二重列表 */
        List<List<String>> lists = new ArrayList<>();
        /* 定义一个字符串行 */
        String Line = null;
        /* 循环读入并处理每一行数据 */
        while ((Line = sc.nextLine()).length() != 0) {
    
    
            /**
             * 当读入的内容不为空(可以用字符串长度来判断),则读入并添加到list中
             */
            lists.add(Arrays.asList(Line.trim().split(" ")));
            /**
             * 这里String对象的 split()方法返回String[]数组
             * 将数组Line.trim().split()作为Arrays.asList()方法作为参数,返回一个List public static <T>
             * List<T> asList(T... a) { return new ArrayList<>(a); }
             */
        }
        /* 读取完毕,关闭输入流 */
        sc.close();
        /* 转为二维数组 */
        int m = lists.size();// 相当于行数
        int n = lists.get(0).size();// 相当于列数:lists.get(0)为第一行输入
        int[][] matrix = new int[m][n];
        // 将字符串变为int类型
        for (int i = 0; i < m; i++) {
    
    
            for (int j = 0; j < n; j++) {
    
    
                matrix[i][j] = Integer.parseInt(lists.get(i).get(j));
            }
        }
        return matrix;
    }

    /**矩阵压缩*/
    public SparseMatrix getSparseMatrix(/*int[][] matrix*/) {
    
    
        int[][] matrix = ReadMatrix();
        int nrow = matrix.length;
        int ncol = matrix[0].length;
        SparseMatrix sparseMatrix = new SparseMatrix();
        int count = 0;
        /*注意,这里索引变量从0开始*/
        for (int i = 0; i < nrow; i++) {
    
    
            for (int j = 0; j < ncol; j++) {
    
    
                if (matrix[i][j] != 0) {
    
    
                    count++;
                    Triple triple = new Triple();
                    /*转化为从1开始计数的坐标(根据需要)*/
                    triple.setRow(i+1);
                    triple.setCol(j+1);
                    triple.setData(matrix[i][j]);
                    sparseMatrix.tripleList.add(triple);
                }
            }
        }
        sparseMatrix.setNum(count);
        sparseMatrix.setNrow(nrow);
        sparseMatrix.setNcol(ncol);
        return sparseMatrix;
    }

    // 打印验证
    public static void main(String[] args) {
    
    
//        int[][] matrix = ReadMatrix();
        System.out.println("test:...");
/*        for (int i = 0; i < matrix.length; i++) {
            for (int j = 0; j < matrix[0].length; j++) {
                System.out.print(matrix[i][j] + " ");
            }
            System.out.println("\n");
        }*/
        InputMatrix inputMatrix=new InputMatrix();
        inputMatrix.getSparseMatrix().print();

    }
}

Writing and debugging experience

We should be clear that although debugging is very important, it should not be easy. It always depends on debugging. It is easy to cause sloppy coding. After all, debugging (this is time-consuming)
We must be as accurate as possible when coding. the definition of any variable or object, or even every function and methods, must have a very clear purpose, without the slightest ambiguity, the documentation comments must keep up this idea for the algorithm checks are provided to traverse even when debugging
which It is especially important that the same measurement system (if it cannot be globally the same, it must be the same as the module, and the necessary measurement system/reference system is added). For
example, in the calculation problem of two-digit array/matrix, the coordinates of a single element are counted from 1. In a two-digit array, the following table starts counting from 0. Don't confuse the two places. You
should develop the habit of describing clearly the requirements and then writing the corresponding function. If the purpose of the
method/function
is more complicated, it should not be fully reflected in The method name
can be abbreviated, but remember to write clearly the usage documentation of the function.
If you just write a small program, don't be too constrained to outsource the function.
You can divide the code function into blocks (functional task allocation is reflected in the way of document comments to reflect the logic In the
same way, you can do this when writing a large method.
By creating variables to save longer expressions, you can reduce repetitive calculations, and the code can be more concise, intuitive, and efficient.

Guess you like

Origin blog.csdn.net/xuchaoxin1375/article/details/114649656