Java实现行列式计算

Java实现行列式计算

前言

最近复习完了线性代数,好久没有碰编程的东西,为了稍微巩固一下,便在闲暇之余,简单写了一个小程序,用于计算行列的值。

基本结构说明

  • 本文显示的计算方法的核心算法思想是迭代。面对多阶行列式,采用按列展开的方式进行计算,确定一个元素之后,将其对应的余子式提取出,视为新的行列式,以此类推,直到余子式的阶数小于三为止(2阶行列式直接按公式计算)。

  • 采用键盘输入的方式,让使用者键入行列式的阶数以及各个元素。对于阶数和各个元素的键入与读取,分为了两个方法。目前阶数暂时限制在20以内,避免资源消耗过多。

  • 一些行列式中可能存在分数,经计算之后会直接输出小数形式的结果,而有些情况,需要输出分数形式。于是便在结尾简单写了一个小数转分数的方法(最后补充的功能,经测试,功能尚不完善,待后续补充)

代码

package com.bk.pojo.pp.yuansu.info;

import java.lang.reflect.Method;
import java.util.Scanner;

/**
 * 计算行列式
 */
public class CountHanglieshi {
    // 设置一个全局的输入流
    private final Scanner scanMain;
    private final Scanner scanBreak;
    // 设置二维数组存储行列式
    private double[][] arr;
    // 储存阶数,用于控制输入规范
    private int size;

    public CountHanglieshi() {
        // 初始化输入流
        // 主要输入流
        scanMain = new Scanner(System.in);
        // 是否提前停止请求输入流
        scanBreak = new Scanner(System.in);
    }

    /**
     * main函数
     *
     * @param args 入参
     */
    public static void main(String[] args) {
        CountHanglieshi countHanglieshi = new CountHanglieshi();
        String method = "rlExpanding";

        if (args != null && args.length > 0 && args[0] != null && "".equals(args[0])) {
            method = args[0];
        }
        try {
            countHanglieshi.service(method);
        } catch (Exception e) {
            // 出现错误进行统一处理
            // 直接判断可能出现的错误为输入的参数有误
            System.out.println("启动失败,请检查输入命令的参数是否有误");
        }
    }

    /**
     * 行列式按行按列展开
     * <p>利用行列式展开原理进行计算
     */
    public void rlExpanding() {
        // 调用递归函数进行计算,要进行多次数的复制,存在较大的优化空间
        double result = core(size, arr);
        System.out.println("行列式的计算结果为: " + transferFs(result));
    }

    /**
     * 核心代码
     * <p>使用行列式展开方法计算行列式(使用递归)
     */
    public double core(int rank, double[][] a) {
        if (rank > 2) {
            double result = 0;
            for (int k = 0; k < rank; k++) {
                // 取出余子式
                double[][] newArr = new double[rank - 1][rank - 1];
                int index = 0;
                // 对数组进行赋值
                for (int i = 0; i < rank; i++) {
                    if (k != i) {
                        for (int j = 1; j < rank; j++) {
                            // 如果该行不等于所在行
                            newArr[index][j - 1] = a[i][j];
                        }
                        // 到第下一行赋值
                        index++;
                    }
                }
                // 重点注意  由于此处取的是  a11 所以 (-1)^(1+1) = 1  可以进行处理
                result += Math.pow(-1, (k + 1 + 1)) * a[k][0] * core(rank - 1, newArr);
            }
            // --------------------------------------
            return result;
        } else {
            // 二级行列式计算
            return a[0][0] * a[1][1] - a[0][1] * a[1][0];
        }
    }

    /**
     * 业务处理函数
     */
    public void service(String method) {
        //死循环实现连续计算
        while (true) {
            int row = readRow();
            if (row != 0) {
                // 初始化成功
                // 数据的输入
                if (readData(row)) {
                    // 数据录入成功,可以进行计算
                    // 通过反射,获取method方法进行计算  jdk1.8以上可以使用
                    try {
                        Class<CountHanglieshi> clazz = CountHanglieshi.class;
                        Method m = clazz.getMethod(method);
                        m.invoke(this);
                        System.out.print("是否继续计算(y/n):");
                        String s = scanBreak.nextLine();
                        if ("n".equals(s) || "N".equals(s)) {
                            System.exit(0);
                        }
                    } catch (Exception e) {
                        // 调用反射是出错
                        return;
                    }
                }
            } else {
                System.out.println("行列式阶数不可为0,是否停止服务【y/n】)");
                String s = scanBreak.nextLine();
                if ("n".equals(s) || "N".equals(s)) {
                    System.exit(0);
                }
            }
        }
    }

    /**
     * 输入行列式的阶数(20以内)
     *
     * @return int 阶数
     */
    public int readRow() {
        System.out.println("-------------------------RowLineFormula----------------------------");
        System.out.print("请输入要计算的行列式的阶数:");

        int row = scanMain.nextInt();
        size = row;
        // 限制只能计算20阶以内的行列式
        if (row > 1 && row <= 20) {
            // 初始化  数据
            arr = new double[row][row];
            return row;
        }
        return 0;
    }

    /**
     * 读取数据
     *
     * @return 判断用户是否选择退出程序, 并进行重新操作
     */
    public boolean readData(int row) {
        try {
            System.out.println("输入行列式的数据(输入一行之后回车,每个数据之间使用空格进行分隔):");
            scanMain.nextLine();
            for (int i = 0; i < row; i++) {
                System.out.print("第" + (i + 1) + "行: ");
                String line = scanMain.nextLine();
                String[] data = line.trim().split(" ");
                if (data.length != row) {
                    System.out.println("数据输入格式错误,请重新输入(从第1行开始):");
                    i = -1;
                } else {
                    for (int j = 0; j < row; j++) {
                        // 此处可能会报错,由于用户输入的错误信息
                        String datum = data[j];
                        String[] split = datum.split("/");
                        if (split.length > 1) {
                            arr[i][j] = Double.parseDouble(split[0]) / Double.parseDouble(split[1]);
                        } else {
                            arr[i][j] = Double.parseDouble(datum);
                        }
                    }
                }
            }
            System.out.println("数据输入录入成功,正在计算行列式,请等待...");
        } catch (NumberFormatException e) {
            // 数据输入错误,进行处理
            System.out.println("输入的数据有误,正在重新执行输入函数...");
            return false;
        }
        return true;
    }

    /**
     * 利用辗转相除法求得最大公约数
     *
     * @param xiaoshu 小数部分
     * @param xiaoshuLength 小数部分长度
     * @return 扣除公约数之后的结果
     */
    public long gcd(long xiaoshu, long xiaoshuLength) {
        return xiaoshuLength == 0 ? xiaoshu : gcd(xiaoshuLength, xiaoshu % xiaoshuLength);
    }

    /**
     * 小数转分数
     */
    public String transferFs(double num) {
        String number = String.valueOf(num);
        //定义长度为2的数组,分别存放整数位和小数位。
        //利用.分割整数和小数
        String[] array = number.split("\\.");
        //整数部分
        long zhengshu = Integer.parseInt(array[0]);
        //小数部分
        long xiaoshu = Integer.parseInt(array[1]);
        //小数部分长度对应的位数
        long xiaoshuLength = (int) Math.pow(10, array[1].length());
        //公约数
        long g = gcd(xiaoshu, xiaoshuLength);
        //分母
        long fenmu = xiaoshuLength / g;
        //分母为1则不转化为分数
        if (fenmu == 1) {
            return String.valueOf(zhengshu + xiaoshu/g);
        } else {
            return (zhengshu*(fenmu) + (xiaoshu / g) + "/" + fenmu);
        }
    }
}
posted @ 2023-05-11 00:24  DoubleSails  阅读(286)  评论(0)    收藏  举报