正互反矩陣的最大特征值與特向求解-java


1、正互反矩陣

首先說一下什么是正互反矩陣,見下圖,一看圖其實就知道什么是正互反矩陣。
image

1.1 使用場景

當我們現在有一堆參數,分了好幾個層次,每個層次里面又有好多參數,那么每個層次的每個參數權重如何設定,這時候,會用到這種類型的矩陣。為方便理解,可以將矩陣A看成下面的表格

image

  • 對角線元素:自己和自己相比,同等重要,所以都為1
  • A[0][1]:參數2比參數1重要,所以是2(或其他的數字,這是有標准的)
  • A[0][2]:參數3和參數1相比,參數3重要,同時也比參數2重要,所以參數3的重要程度取值至少要比A[0][1]的值大

至於比較兩個參數之間的重要程度的取值,自行尋找標准。下面是標准之一:1-9標度法

相對重要性 定義 說明
1 同等重要 兩個目標同樣重要
3 略微重要 經驗或判斷,認為一個目標比另一個略微重要
5 相當重要 經驗或判斷,認為一個目標比另一個重要
7 明顯重要 深感一個目標比另一個重要,並且這種重要性已經有實踐證明
9 絕對重要 強烈的感到一個目標比另一個重要的多
2,4,6,8 兩個相鄰判斷的中間值 折中時采用

1.2 矩陣計算

https://wenku.baidu.com/view/d70e0c746cdb6f1aff00bed5b9f3f90f76c64df3.html
可以使用平常手算時的那種計算方式,通過|λE-A| = 0得到特征值,再代入(λE-A)中求解該特征值的特征向量。其中,我們只用到最大特征值、及其特向。
這種算法很精確,但是很麻煩,(如果你要是用Matlab當我沒說。。。),由於參數之間的重要程度關系,都是由經驗判斷,主觀性很強,所以對精度要求其實不高,大概即可,故有三種快速計算最大特征值及其特向的方法:根法、和法、冪法

2 計算

三個方法公共的代碼

/**
 * 計算方陣的最大特征根及其特征向量
 *
 * @author silverbeats
 * @date 2021/8/13 013 20:26
 */
public class MatrixUtil {
    // 保留小數的位數
    private final int BIT = 6;
    
    /**
     * 矩陣乘法
     *
     * @param a 矩陣a
     * @param b 矩陣b
     */
    public double[][] matrixMult(double[][] a, double[][] b) {
        int arows = a.length,
            acols = a[0].length,
            brows = b.length,
            bcols = b[0].length;
        // 判斷兩個矩陣是否能夠進行相乘
        if (acols != brows) {
            throw new RuntimeException("矩陣a的列數與矩陣b的行數不等,無法進行矩陣相乘");
        }
        // 保存矩陣相乘的結果
        double[][] res = new double[arows][bcols];
        for (int i = 0; i < bcols; ++i) {
            // j,k 定位行乘列
            for (int j = 0; j < arows; ++j) {
                double sum = 0.0;
                for (int k = 0; k < acols; ++k) {
                    sum += a[j][k] * b[k][i];
                }
                res[j][i] = sum;
            }
        }
        return res;
    }

    /**
     * 矩陣的列進行歸一化,某元素在其所在列的比例
     *
     * @param matrix
     */
    public void normalizedColumn(double[][] matrix) {
        int rows = matrix.length,
            cols = matrix[0].length,
            row, col;
        // 求出矩陣每一列之和
        double[] temp = new double[cols];
        for (col = 0; col < cols; ++col) {
            for (row = 0; row < rows; ++row) {
                temp[col] += matrix[row][col];
            }
        }
        // 對matrix進行處理,得出每個元素在其所在列的占比
        for (row = 0; row < rows; ++row) {
            for (col = 0; col < cols; ++col) {
                matrix[row][col] /= temp[col];
            }
        }
    }

    /**
     * 合並矩陣的列,將m x n的矩陣變為m x 1的矩陣
     *
     * @param matrix
     */
    public double[][] mergeColumn(double[][] matrix) {
        int rows = matrix.length, cols = matrix[0].length;
        double[][] res = new double[rows][1];
        for (int row = 0; row < rows; ++row) {
            double sum = 0.0;
            for (int col = 0; col < cols; ++col) {
                sum += matrix[row][col];
            }
            res[row][0] = sum;
        }
        return res;
    }

    /**
     * 合並矩陣的行,將m x n矩陣變為1 x n矩陣
     *
     * @param matrix
     */
    public double[][] mergeRow(double[][] matrix) {
        int rows = matrix.length, cols = matrix[0].length;
        double[][] res = new double[1][cols];
        for (int col = 0; col < cols; ++col) {
            for (int row = 0; row < rows; ++row) {
                res[0][col] += matrix[row][col];
            }
        }
        return res;
    }

    /**
     * 保留指定個數的小數
     *
     * @param number 數字
     */
    public double toFixed(double number) {
        double temp = Math.pow(10, BIT);
        return Math.round(number * temp) / temp;
    }

    /**
     * arr是一維的最大特征向量,
     * 由於最大特征向量是一個n x 1的二維矩陣,會轉換為一維數組
     * 易知特向的每一個元素以及最大特征根是double類型,會對其保留BIT位的小數
     * 存在四舍五入后,最大特征根的特向元素之和不為1的情況,可能會多或少Math.pow(10, -BIT)
     * 這里做的處理:將這個誤差給到權重最小的參數身上,保證所有參數的權重和為1
     *
     * @param arr
     */
    public void correction(double[] arr) {
        int length = arr.length;
        double bei = Math.pow(10, BIT);
        double sum = 0.0;
        for (double v : arr) {
            sum = sum + v * bei;
        }
        // 四舍五入后沒問題
        if (sum == bei) return;
        // 誤差
        double err = sum - bei;
        // 定位到權重最小值的參數位置
        int minRow = 0;
        for (int i = 1; i < length; ++i) {
            if (arr[i] < arr[minRow]) {
                minRow = i;
            }
        }
        arr[minRow] -= err / bei;
    }

    /**
     * 二維數組轉一維數組
     *
     * @param matrix
     */
    public double[] convertData(double[][] matrix) {
        int rows = matrix.length, cols = matrix[0].length;
        int arrLen = rows * cols;
        double[] res = new double[arrLen];
        int index = 0;
        for (int row = 0; row < rows; ++row) {
            for (int col = 0; col < cols; ++col) {
                res[index++] = matrix[row][col];
            }
        }
        return res;
    }
}

2.1 根法

image

/**
 * 計算方陣的最大特征根及其特征向量
 *
 * @author silverbeats
 * @date 2021/8/13 013 20:26
 */
public class MatrixUtil {
    /**
     * 根法求解矩陣matrix最大特征根、及其特征向量
     *
     * @param matrix
     */
    public Map<String, Object> rootMethod(double[][] matrix) {
        if (matrix.length != matrix[0].length) {
            throw new RuntimeException("必須是方陣");
        }
        // 保存結果用的
        Map<String, Object> resultMap = new HashMap<>();
        final int DIM = matrix.length;
        // 1、計算權重向量(最大特征根的特向)
        // 1.1 矩陣有DIM行,同行的元素進行相乘,之后再開DIM根號,得到DIM x 1矩陣
        double[][] w = new double[DIM][1];
        for (int row = 0; row < DIM; ++row) {
            double mult = 1.0;
            for (int col = 0; col < DIM; ++col) {
                mult *= matrix[row][col];
            }
            w[row][0] = Math.pow(mult, 1.0 / DIM);
        }
        // 1.2 將w矩陣進行歸一化,得到的就是權重向量
        normalizedColumn(w);
        // 2、計算最大特征根
        // 2.1 將matrix按行進行合並相加
        double[][] s = mergeRow(matrix);
        // 2.2 s x w 的結果即為最大特征值,s是1 x DIM矩陣,w是DIM x 1矩陣,得到1 x 1矩陣
        double maxEigenvalue = matrixMult(s,w)[0][0];
        // 3、可選:將DIM x 1矩陣轉一維,對數據進行小數保留,以及保留后權重和不為1的處理
        double[] maxEigenVector = convertData(w);
        for (int i = 0; i < maxEigenVector.length; i++) {
            maxEigenVector[i] = toFixed(maxEigenVector[i]);
        }
        // 4、易知權重和為1,然而在經過小數保留后,可能會導致權重和不為1,進行處理
        correction(maxEigenVector);
        // --------------------------------------------------
        resultMap.put("maxEigenvalue", toFixed(maxEigenvalue));
        resultMap.put("maxEigenVector", maxEigenVector);
        return resultMap;
    }
}

2.2 和法

image
image

public class MatrixUtil {
    /**
     * 和法求解矩陣matrix的最大特征根、及其特征向量
     *
     * @param matrix
     */
    public Map<String, Object> sumMethod(double[][] matrix) {
        if (matrix.length != matrix[0].length) {
            throw new RuntimeException("必須是方陣");
        }
        Map<String, Object> resultMap = new HashMap<>();
        final int DIM = matrix.length;
        // 判斷矩陣w,拷貝一份matrix,目前與matrix一致,后面會對w進行修改
        double[][] w = new double[DIM][DIM];
        for (int row = 0; row < DIM; ++row) {
            for (int col = 0; col < DIM; ++col) {
                w[row][col] = matrix[row][col];
            }
        }
        // 1、權重向量(最大特征根的特向)
        // 1.1 將矩陣的每一列進行歸一化處理,得到判斷矩陣w
        normalizedColumn(w);
        // 1.2 判斷矩陣w的所有列進行相加,變成n x 1的矩陣后進行歸一化處理
        // 此時得到的n x 1矩陣就是matrix最大特征根的特征向量
        double[][] t = mergeColumn(w);
        normalizedColumn(t);
        // 2、計算最大特征根
        // 2.1 將原矩陣matrix(m x m)與最大特向t(m x 1)進行矩陣乘法(m x 1)
        double[][] mx = matrixMult(matrix, t);
        // 2.2 將mx和maxEigenVector這兩個m x 1的列矩陣對應位置進行相除
        for (int row = 0; row < DIM; ++row) {
            mx[row][0] /= t[row][0];
        }
        // 2.3 將mx這一列矩陣的所有元素求和,最后取個均值就是最大特征根
        double maxEigenValue = 0.0;
        for (int row = 0; row < DIM; ++row) {
            maxEigenValue += mx[row][0];
        }
        maxEigenValue /= DIM;
        // 3、可選:對數據進行小數保留,以及保留后權重和不為1的處理
        double[] maxEigenVector = convertData(t);
        for (int i = 0; i < maxEigenVector.length; i++) {
            maxEigenVector[i] = toFixed(maxEigenVector[i]);
        }
        correction(maxEigenVector);
        // --------------------------------------------------
        resultMap.put("maxEigenvalue", toFixed(maxEigenValue));
        resultMap.put("maxEigenVector", maxEigenVector);
        return resultMap;
    }
}

2.3 冪法

image

public class MatrixUtil {
    /**
     * 冪法
     *
     * @param matrix 矩陣
     * @param times 最大迭代次數
     * @param accept 可接受的誤差
     */
    public Map<String, Object> powMethod(double[][] matrix, int times, double accept) {
        final int DIM = matrix.length;
        // 1、初始化
        // 1.1 初始正列向量Xk,用來迭代(k代表是第幾次迭代)
        // 1.2 初始化mk(mk是Xk這個列向量中最大的值)和yk(Xk/mk)
        double mk = 1.0;
        double[][] Xk = new double[DIM][1];
        double[][] yk = new double[DIM][1];
        for (int i = 0; i < DIM; ++i) {
            yk[i][0] = Xk[i][0] = 1.0;
        }
        // 2、迭代計算
        int cnt = 0;
        while(true) {
            double oldMk = mk;
            // 2.1 迭代Xk
            Xk = matrixMult(matrix, yk);
            // 2.2 更新mk
            mk = Xk[0][0];
            for (int i = 1; i < DIM; ++i) {
                if(Xk[i][0] > mk)
                mk = Xk[i][0];
            }
            // 2.3 迭代yk
            for (int i = 1; i < DIM; ++i) {
                yk[i][0] = Xk[i][0] / mk;
            }
            ++cnt;
            // 2.4 精度檢查,accept是可接受的精度誤差
            // 2.5 迭代次數檢查,times是最大迭代次數
            if(Math.abs(mk - oldMk) < accept || cnt >= times)
                break;
        }
        // 3、求最大特征值,以及特向
        // 3.1 最大特征值: 即mk
        // 3.2 特向:對yk進行歸一化后即為所求
        normalizedColumn(yk);
        // 4、可選:對mk和yk進行小數取舍
        double[] maxEigenVector = convertData(yk);
        for(int i = 0; i < DIM; ++i) {
            maxEigenVector[i] = toFixed(maxEigenVector[i]);
        }
        correction(maxEigenVector);
        // ---------------------------------------------
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("maxEigenvalue", toFixed(mk));
        resultMap.put("maxEigenVector", maxEigenVector);
        return resultMap;
    }
}

2.4 測試

public static void main(String[] args) {
    double[][] arr = {
        {1, 2, 6},
        {0.5, 1, 4},
        {1 / 6.0, 0.25, 1}
    };
    int maxTimes = 10000;
    double accept = 1e-7;
    MatrixUtil matrixUtil = new MatrixUtil();
    Map<String, Object> rootMethodResultMap = matrixUtil.rootMethod(arr);
    Map<String, Object> sumMethodResultMap = matrixUtil.sumMethod(arr);
    Map<String, Object> powMethodResultMap = matrixUtil.powMethod(arr, maxTimes, accept);
    System.out.println("----根法----");
    rootMethodResultMap.forEach((k, v) -> {
        System.out.println(k);
        if (v instanceof double[])
            System.out.println(Arrays.toString((double[]) v));
        else
            System.out.println(v);
        });
        System.out.println("----和法----");
        sumMethodResultMap.forEach((k, v) -> {
        System.out.println(k);
        if (v instanceof double[])
            System.out.println(Arrays.toString((double[]) v));
        else
            System.out.println(v);
        });
        System.out.println("----冪法----");
        powMethodResultMap.forEach((k, v) -> {
            System.out.println(k);
            if (v instanceof double[])
                System.out.println(Arrays.toString((double[]) v));
            else
                System.out.println(v);
        });
    }

image


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2026 CODEPRJ.COM