I am making a project in Java where i have to use BigInteger class to implement an encryption method.
I have square matrices nxn where n can be 200 and i need to calculate the determinant. I did the method using the determinant of submatrices but its taking forever to calculate.
public BigInteger determinant(Matrix matrix){
if (matrix.getColumns()!=matrix.getRows()){
System.out.println("The matrix is not square");
return BigInteger.valueOf(-1);
}
if (matrix.getColumns() == 1) {
return matrix.getMatrix()[0][0];
}
if (matrix.getRows()==2) {
return ((matrix.getValueAt(0, 0).multiply(matrix.getValueAt(1, 1)))).subtract(( matrix.getValueAt(0, 1).multiply(matrix.getValueAt(1, 0))));
}
BigInteger sum = BigInteger.valueOf(0);
for (int i=0; i<matrix.getColumns(); i++) {
sum = sum.add(this.changeSign(BigInteger.valueOf(i)).multiply(matrix.getValueAt(0, i)).multiply(determinant(createSubMatrix(matrix, 0, i))));// * determinant(createSubMatrix(matrix, 0, i));
}
return sum;
}
Is there a non-recursive way to calculate the determinant?
Thanks in advance.
I've posted this as a comment but I think this could actually solve your problem so I'm posting it as an answer as well.
You can use this package: http://math.nist.gov/javanumerics/jama/
A common practice of calculating the deterninat of huge matrices is the use an LUP decomposition. In this case, the decerminant can be calculated with following ideas:
{L, U, P} = LUP(A)
sign = -1 ^ 'number of permutations in P'
det(A) = diagonalProduct(U) * sign
This is how big math packages do that. You should probably implement LU by yourself.
I believe this is exactly what you need.Using This class you can calculate the determinant of a matrix with any dimension
This class uses many different methods to make the matrix triangular and then, calculates the determinant of it. It can be used for matrix of high dimension like 500 x 500 or even more. the bright side of the this class is that you can get the result in BigDecimal so there is no infinity and you'll have always the accurate answer. By the way, using many various methods and avoiding recursion resulted in much faster way with higher performance to the answer. hope it would be helpful.
import java.math.BigDecimal;
public class DeterminantCalc {
private double[][] matrix;
private int sign = 1;
DeterminantCalc(double[][] matrix) {
this.matrix = matrix;
}
public int getSign() {
return sign;
}
public BigDecimal determinant() {
BigDecimal deter;
if (isUpperTriangular() || isLowerTriangular())
deter = multiplyDiameter().multiply(BigDecimal.valueOf(sign));
else {
makeTriangular();
deter = multiplyDiameter().multiply(BigDecimal.valueOf(sign));
}
return deter;
}
/* receives a matrix and makes it triangular using allowed operations
on columns and rows
*/
public void makeTriangular() {
for (int j = 0; j < matrix.length; j++) {
sortCol(j);
for (int i = matrix.length - 1; i > j; i--) {
if (matrix[i][j] == 0)
continue;
double x = matrix[i][j];
double y = matrix[i - 1][j];
multiplyRow(i, (-y / x));
addRow(i, i - 1);
multiplyRow(i, (-x / y));
}
}
}
public boolean isUpperTriangular() {
if (matrix.length < 2)
return false;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < i; j++) {
if (matrix[i][j] != 0)
return false;
}
}
return true;
}
public boolean isLowerTriangular() {
if (matrix.length < 2)
return false;
for (int j = 0; j < matrix.length; j++) {
for (int i = 0; j > i; i++) {
if (matrix[i][j] != 0)
return false;
}
}
return true;
}
public BigDecimal multiplyDiameter() {
BigDecimal result = BigDecimal.ONE;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix.length; j++) {
if (i == j)
result = result.multiply(BigDecimal.valueOf(matrix[i][j]));
}
}
return result;
}
// when matrix[i][j] = 0 it makes it's value non-zero
public void makeNonZero(int rowPos, int colPos) {
int len = matrix.length;
outer:
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
if (matrix[i][j] != 0) {
if (i == rowPos) { // found "!= 0" in it's own row, so cols must be added
addCol(colPos, j);
break outer;
}
if (j == colPos) { // found "!= 0" in it's own col, so rows must be added
addRow(rowPos, i);
break outer;
}
}
}
}
}
//add row1 to row2 and store in row1
public void addRow(int row1, int row2) {
for (int j = 0; j < matrix.length; j++)
matrix[row1][j] += matrix[row2][j];
}
//add col1 to col2 and store in col1
public void addCol(int col1, int col2) {
for (int i = 0; i < matrix.length; i++)
matrix[i][col1] += matrix[i][col2];
}
//multiply the whole row by num
public void multiplyRow(int row, double num) {
if (num < 0)
sign *= -1;
for (int j = 0; j < matrix.length; j++) {
matrix[row][j] *= num;
}
}
//multiply the whole column by num
public void multiplyCol(int col, double num) {
if (num < 0)
sign *= -1;
for (int i = 0; i < matrix.length; i++)
matrix[i][col] *= num;
}
// sort the cols from the biggest to the lowest value
public void sortCol(int col) {
for (int i = matrix.length - 1; i >= col; i--) {
for (int k = matrix.length - 1; k >= col; k--) {
double tmp1 = matrix[i][col];
double tmp2 = matrix[k][col];
if (Math.abs(tmp1) < Math.abs(tmp2))
replaceRow(i, k);
}
}
}
//replace row1 with row2
public void replaceRow(int row1, int row2) {
if (row1 != row2)
sign *= -1;
double[] tempRow = new double[matrix.length];
for (int j = 0; j < matrix.length; j++) {
tempRow[j] = matrix[row1][j];
matrix[row1][j] = matrix[row2][j];
matrix[row2][j] = tempRow[j];
}
}
//replace col1 with col2
public void replaceCol(int col1, int col2) {
if (col1 != col2)
sign *= -1;
System.out.printf("replace col%d with col%d, sign = %d%n", col1, col2, sign);
double[][] tempCol = new double[matrix.length][1];
for (int i = 0; i < matrix.length; i++) {
tempCol[i][0] = matrix[i][col1];
matrix[i][col1] = matrix[i][col2];
matrix[i][col2] = tempCol[i][0];
}
}
}
And then this class receives a matrix of n x n from the user or can generate a random matrix of nxn and then calculates it's determinant. It also shows the solution and the final triangular matrix.
import java.math.BigDecimal;
import java.security.SecureRandom;
import java.text.NumberFormat;
import java.util.Scanner;
public class DeterminantTest {
public static void main(String[] args) {
String determinant;
//generating random numbers
int len = 500;
SecureRandom random = new SecureRandom();
double[][] matrix = new double[len][len];
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
matrix[i][j] = random.nextInt(500);
System.out.printf("%15.2f", matrix[i][j]);
}
}
System.out.println();
/*double[][] matrix = {
{1, 5, 2, -2, 3, 2, 5, 1, 0, 5},
{4, 6, 0, -2, -2, 0, 1, 1, -2, 1},
{0, 5, 1, 0, 1, -5, -9, 0, 4, 1},
{2, 3, 5, -1, 2, 2, 0, 4, 5, -1},
{1, 0, 3, -1, 5, 1, 0, 2, 0, 2},
{1, 1, 0, -2, 5, 1, 2, 1, 1, 6},
{1, 0, 1, -1, 1, 1, 0, 1, 1, 1},
{1, 5, 5, 0, 3, 5, 5, 0, 0, 6},
{1, -5, 2, -2, 3, 2, 5, 1, 1, 5},
{1, 5, -2, -2, 3, 1, 5, 0, 0, 1}
};
double[][] matrix = menu();*/
DeterminantCalc deter = new DeterminantCalc(matrix);
BigDecimal det = deter.determinant();
determinant = NumberFormat.getInstance().format(det);
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix.length; j++) {
System.out.printf("%15.2f", matrix[i][j]);
}
System.out.println();
}
System.out.println();
System.out.printf("%s%s%n", "Determinant: ", determinant);
System.out.printf("%s%d", "sign: ", deter.getSign());
}
public static double[][] menu() {
Scanner scanner = new Scanner(System.in);
System.out.print("Matrix Dimension: ");
int dim = scanner.nextInt();
double[][] inputMatrix = new double[dim][dim];
System.out.println("Set the Matrix: ");
for (int i = 0; i < dim; i++) {
System.out.printf("%5s%d%n", "row", i + 1);
for (int j = 0; j < dim; j++) {
System.out.printf("M[%d][%d] = ", i + 1, j + 1);
inputMatrix[i][j] = scanner.nextDouble();
}
System.out.println();
}
scanner.close();
return inputMatrix;
}
}
The recursive method will take ages to find the determinant of the Matrix of dimension more than 10x10. You will need to do LU decomposition and Gaussian reduction. I used this to find a determinant of 1000x1000 matrix and it produced the correct result within a sec.
You can get this code in Numerical Recipes Book ( use 3rd edition only ): line 52. It is written in C++ but you can easily convert it in Java
or else check ludcmp() in this
https://www.cc.gatech.edu/gvu/people/Phd/warren/matrix.c
Related
Can anyone please point out the mistake, the error its showing is:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 4
at com.company.Grid.pushZero(Grid.java:42)
at com.company.Main.main(Main.java:14)
My Code is
package com.company;
import java.util.Random;
public class Grid {
Random rand = new Random();
int newnumber() {
double rand1 = rand.nextDouble();
if (rand1 < 0.2) {
return 4;
} else {
return 2;
}
}
int[][] array = {{0, 0, 0, newnumber()}, {0, 0, 0, 2}, {0, 0, 0, 0}, {0, 0, 0, 0}};
void display() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < array[i].length; ++j) {
System.out.print(array[i][j] + "\t");
}
System.out.print("\n");
}
System.out.print("\n");
}
void pushZero(int[][] array, int n) {
int count1 = 0;
for (int i = 0; i < n; i++){
for (int j = 0; j < n; j++) {
if (array[i][j] != 1) {
array[count1++][j] = array[i][j];
while (count1 < n) {
array[count1++][j] = 1;
}
} else {
while (count1 < n)
array[count1++][j] = 1;
}
int lastNonOne = 0;
for (int a = n - 1; i >= 0; i--) {
if (array[a][j] == 1)
continue;
if (lastNonOne == 0) {
lastNonOne = a;
}
if (array[i][j] !=0)
array[lastNonOne--][j] = array[i][j];
}
while (lastNonOne >= 0)
array[lastNonOne--][j] = 0;
}
}
}
}
You forgot to reset count1. Or, rather, you've mis-scoped it. It shuold probably be declared 2 nest-levels deeper, right above if (array[i][j]...) - the first loop, count1 is upped to 4 (or rather, upped to whatever n is, but I assume that pushZero is called with n=4), and then next loop, it just carries on where it left off, trying array[4], in effect, and that causes an AIOOBEx exactly as you get it - there is no array[4], there's only array[0] through array[3].
I had the following idea to take an array and remove any duplicates. However, I am getting the error "error: incompatible types: cannot be converted to int" referring to the part of the code where I set temp[i] = null. Is it possible to do this? How can I fix this problem?
public static int[] withoutDuplicates(int[] x) {
int[] temp = new int[x.length];
int count = 0;
for (int i = x.length - 1; i >= 0; i--) {
for (int j = i-1; j >= 0; j--) {
if (temp[i] == x[j]) {
temp[i] = null;
count++;
}
}
}
int size = x.length - count;
int[] a = new int[size];
int pos = 0;
for (int i = 0; i < x.length; i++) {
if (temp[i] != null) {
a[pos] = temp[i];
pos++;
}
}
return a;
}
Stream-based solution is concise but for your task/requirements you could be using just a temporary boolean array to mark positions of duplicates:
public static int[] withoutDuplicates(int[] x) {
boolean[] duplicated = new boolean[x.length];
int count = 0;
for (int i = x.length - 1; i > 0; i--) {
for (int j = i-1; j >= 0; j--) {
if (x[i] == x[j]) {
duplicated[i] = true;
count++;
}
}
}
int size = x.length - count;
int[] a = new int[size];
for (int i = 0, pos = 0; pos < size && i < x.length; i++) {
if (!duplicated[i]) {
a[pos++] = x[i];
}
}
return a;
}
Test:
int[] arr1 = {1, 2, 3, 4, 3, 5, 2};
int[] arr2 = {1, 2, 3, 4, 0, 5, 6};
System.out.println(Arrays.toString(withoutDuplicates(arr1)));
System.out.println(Arrays.toString(withoutDuplicates(arr2)));
output
[1, 2, 3, 4, 5]
[1, 2, 3, 4, 0, 5, 6]
You can't assign a null to a primitive type int but you can assign a null to the wrapper object Integer.
To remove the duplicates of an int array you could use something like this:
myIntArray = Arrays.stream(myIntArray).distinct().toArray();
How to calculate amount of COLUMNS containing zero, in a multidimensional array.
The number of rows containing zeros I was able to calculate, but I can not understand how to calculate amount of columns. Please, help.
public class Main {
public static void main (String[] args) {
int i;
int j;
int count = 0;
int [][] arr = {{3, 0, 0, 6}, {4, 0, 1, 1}};
for (i = 0; i < arr.length; i++) {
System.out.println();
for (j = 0; j < arr[i].length; j++){
System.out.print(arr[i][j] + ", ");
}
}
System.out.println();
//-----------------------------------------------------------
for (i = 0; i<arr.length; i++) {
for (j = 0; j<arr[i].length; j++) {
if (arr[i][j] == 0) {
count++;
break;
}
}
}
System.out.println("the amount of rows containing zeros = " + count);
}
}
Another straightforward way to get the same result for a sqaure matrix:
import java.util.*;
import java.lang.*;
import java.io.*;
/* Name of the class has to be "Main" only if the class is public. */
class Ideone
{
public static void main (String[] args) throws java.lang.Exception
{
int i;
int j;
int count = 0;
int [][] arr = {{3, 0, 0, 6}, {4, 0, 1, 0}};
for (i = 0; i < arr.length; i++)
{
System.out.println();
for (j = 0; j < arr[i].length; j++)
{
System.out.print(arr[i][j] + ", ");} }
System.out.println();
//--------------------------------------------------------------------
for (i = 0; i<arr.length; i++)
for (j = 0; j<arr[i].length; j++)
if (arr[i][j] == 0)
{
count++;
break;
}
System.out.println("the amount of rows containing zeros = " + count);
//--------------------------------------------------------------------
count=0;
for (i = 0; i<arr[0].length; i++)
for (j = 0; j<arr.length; j++)
if (arr[j][i] == 0)
{
count++;
break;
}
System.out.println("the amount of cols containing zeros = " + count);
}
}
Output:
3, 0, 0, 6,
4, 0, 1, 0,
the amount of rows containing zeros = 2
the amount of cols containing zeros = 3
http://ideone.com/VE0qAw
I am trying to calculate the determinant of a matrix (of any size), for self coding / interview practice. My first attempt is using recursion and that leads me to the following implementation:
import java.util.Scanner.*;
public class Determinant {
double A[][];
double m[][];
int N;
int start;
int last;
public Determinant (double A[][], int N, int start, int last){
this.A = A;
this.N = N;
this.start = start;
this.last = last;
}
public double[][] generateSubArray (double A[][], int N, int j1){
m = new double[N-1][];
for (int k=0; k<(N-1); k++)
m[k] = new double[N-1];
for (int i=1; i<N; i++){
int j2=0;
for (int j=0; j<N; j++){
if(j == j1)
continue;
m[i-1][j2] = A[i][j];
j2++;
}
}
return m;
}
/*
* Calculate determinant recursively
*/
public double determinant(double A[][], int N){
double res;
// Trivial 1x1 matrix
if (N == 1) res = A[0][0];
// Trivial 2x2 matrix
else if (N == 2) res = A[0][0]*A[1][1] - A[1][0]*A[0][1];
// NxN matrix
else{
res=0;
for (int j1=0; j1<N; j1++){
m = generateSubArray (A, N, j1);
res += Math.pow(-1.0, 1.0+j1+1.0) * A[0][j1] * determinant(m, N-1);
}
}
return res;
}
}
So far it is all good and it gives me a correct result. Now I would like to optimise my code by making use of multiple threads to calculate this determinant value.
I tried to parallelize it using the Java Fork/Join model. This is my approach:
#Override
protected Double compute() {
if (N < THRESHOLD) {
result = computeDeterminant(A, N);
return result;
}
for (int j1 = 0; j1 < N; j1++){
m = generateSubArray (A, N, j1);
ParallelDeterminants d = new ParallelDeterminants (m, N-1);
d.fork();
result += Math.pow(-1.0, 1.0+j1+1.0) * A[0][j1] * d.join();
}
return result;
}
public double computeDeterminant(double A[][], int N){
double res;
// Trivial 1x1 matrix
if (N == 1) res = A[0][0];
// Trivial 2x2 matrix
else if (N == 2) res = A[0][0]*A[1][1] - A[1][0]*A[0][1];
// NxN matrix
else{
res=0;
for (int j1=0; j1<N; j1++){
m = generateSubArray (A, N, j1);
res += Math.pow(-1.0, 1.0+j1+1.0) * A[0][j1] * computeDeterminant(m, N-1);
}
}
return res;
}
/*
* Main function
*/
public static void main(String args[]){
double res;
ForkJoinPool pool = new ForkJoinPool();
ParallelDeterminants d = new ParallelDeterminants();
d.inputData();
long starttime=System.nanoTime();
res = pool.invoke (d);
long EndTime=System.nanoTime();
System.out.println("Seq Run = "+ (EndTime-starttime)/100000);
System.out.println("the determinant valaue is " + res);
}
However after comparing the performance, I found that the performance of the Fork/Join approach is very bad, and the higher the matrix dimension, the slower it becomes (as compared to the first approach). Where is the overhead? Can anyone shed a light on how to improve this?
Using This class you can calculate the determinant of a matrix with any dimension
This class uses many different methods to make the matrix triangular and then, calculates the determinant of it. It can be used for matrix of high dimension like 500 x 500 or even more. the bright side of the this class is that you can get the result in BigDecimal so there is no infinity and you'll have always the accurate answer. By the way, using many various methods and avoiding recursion resulted in much faster way with higher performance to the answer. hope it would be helpful.
import java.math.BigDecimal;
public class DeterminantCalc {
private double[][] matrix;
private int sign = 1;
DeterminantCalc(double[][] matrix) {
this.matrix = matrix;
}
public int getSign() {
return sign;
}
public BigDecimal determinant() {
BigDecimal deter;
if (isUpperTriangular() || isLowerTriangular())
deter = multiplyDiameter().multiply(BigDecimal.valueOf(sign));
else {
makeTriangular();
deter = multiplyDiameter().multiply(BigDecimal.valueOf(sign));
}
return deter;
}
/* receives a matrix and makes it triangular using allowed operations
on columns and rows
*/
public void makeTriangular() {
for (int j = 0; j < matrix.length; j++) {
sortCol(j);
for (int i = matrix.length - 1; i > j; i--) {
if (matrix[i][j] == 0)
continue;
double x = matrix[i][j];
double y = matrix[i - 1][j];
multiplyRow(i, (-y / x));
addRow(i, i - 1);
multiplyRow(i, (-x / y));
}
}
}
public boolean isUpperTriangular() {
if (matrix.length < 2)
return false;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < i; j++) {
if (matrix[i][j] != 0)
return false;
}
}
return true;
}
public boolean isLowerTriangular() {
if (matrix.length < 2)
return false;
for (int j = 0; j < matrix.length; j++) {
for (int i = 0; j > i; i++) {
if (matrix[i][j] != 0)
return false;
}
}
return true;
}
public BigDecimal multiplyDiameter() {
BigDecimal result = BigDecimal.ONE;
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix.length; j++) {
if (i == j)
result = result.multiply(BigDecimal.valueOf(matrix[i][j]));
}
}
return result;
}
// when matrix[i][j] = 0 it makes it's value non-zero
public void makeNonZero(int rowPos, int colPos) {
int len = matrix.length;
outer:
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
if (matrix[i][j] != 0) {
if (i == rowPos) { // found "!= 0" in it's own row, so cols must be added
addCol(colPos, j);
break outer;
}
if (j == colPos) { // found "!= 0" in it's own col, so rows must be added
addRow(rowPos, i);
break outer;
}
}
}
}
}
//add row1 to row2 and store in row1
public void addRow(int row1, int row2) {
for (int j = 0; j < matrix.length; j++)
matrix[row1][j] += matrix[row2][j];
}
//add col1 to col2 and store in col1
public void addCol(int col1, int col2) {
for (int i = 0; i < matrix.length; i++)
matrix[i][col1] += matrix[i][col2];
}
//multiply the whole row by num
public void multiplyRow(int row, double num) {
if (num < 0)
sign *= -1;
for (int j = 0; j < matrix.length; j++) {
matrix[row][j] *= num;
}
}
//multiply the whole column by num
public void multiplyCol(int col, double num) {
if (num < 0)
sign *= -1;
for (int i = 0; i < matrix.length; i++)
matrix[i][col] *= num;
}
// sort the cols from the biggest to the lowest value
public void sortCol(int col) {
for (int i = matrix.length - 1; i >= col; i--) {
for (int k = matrix.length - 1; k >= col; k--) {
double tmp1 = matrix[i][col];
double tmp2 = matrix[k][col];
if (Math.abs(tmp1) < Math.abs(tmp2))
replaceRow(i, k);
}
}
}
//replace row1 with row2
public void replaceRow(int row1, int row2) {
if (row1 != row2)
sign *= -1;
double[] tempRow = new double[matrix.length];
for (int j = 0; j < matrix.length; j++) {
tempRow[j] = matrix[row1][j];
matrix[row1][j] = matrix[row2][j];
matrix[row2][j] = tempRow[j];
}
}
//replace col1 with col2
public void replaceCol(int col1, int col2) {
if (col1 != col2)
sign *= -1;
System.out.printf("replace col%d with col%d, sign = %d%n", col1, col2, sign);
double[][] tempCol = new double[matrix.length][1];
for (int i = 0; i < matrix.length; i++) {
tempCol[i][0] = matrix[i][col1];
matrix[i][col1] = matrix[i][col2];
matrix[i][col2] = tempCol[i][0];
}
} }
This Class Receives a matrix of n x n from the user then calculates it's determinant. It also shows the solution and the final triangular matrix.
import java.math.BigDecimal;
import java.text.NumberFormat;
import java.util.Scanner;
public class DeterminantTest {
public static void main(String[] args) {
String determinant;
//generating random numbers
/*int len = 300;
SecureRandom random = new SecureRandom();
double[][] matrix = new double[len][len];
for (int i = 0; i < len; i++) {
for (int j = 0; j < len; j++) {
matrix[i][j] = random.nextInt(500);
System.out.printf("%15.2f", matrix[i][j]);
}
}
System.out.println();*/
/*double[][] matrix = {
{1, 5, 2, -2, 3, 2, 5, 1, 0, 5},
{4, 6, 0, -2, -2, 0, 1, 1, -2, 1},
{0, 5, 1, 0, 1, -5, -9, 0, 4, 1},
{2, 3, 5, -1, 2, 2, 0, 4, 5, -1},
{1, 0, 3, -1, 5, 1, 0, 2, 0, 2},
{1, 1, 0, -2, 5, 1, 2, 1, 1, 6},
{1, 0, 1, -1, 1, 1, 0, 1, 1, 1},
{1, 5, 5, 0, 3, 5, 5, 0, 0, 6},
{1, -5, 2, -2, 3, 2, 5, 1, 1, 5},
{1, 5, -2, -2, 3, 1, 5, 0, 0, 1}
};
*/
double[][] matrix = menu();
DeterminantCalc deter = new DeterminantCalc(matrix);
BigDecimal det = deter.determinant();
determinant = NumberFormat.getInstance().format(det);
for (int i = 0; i < matrix.length; i++) {
for (int j = 0; j < matrix.length; j++) {
System.out.printf("%15.2f", matrix[i][j]);
}
System.out.println();
}
System.out.println();
System.out.printf("%s%s%n", "Determinant: ", determinant);
System.out.printf("%s%d", "sign: ", deter.getSign());
}
public static double[][] menu() {
Scanner scanner = new Scanner(System.in);
System.out.print("Matrix Dimension: ");
int dim = scanner.nextInt();
double[][] inputMatrix = new double[dim][dim];
System.out.println("Set the Matrix: ");
for (int i = 0; i < dim; i++) {
System.out.printf("%5s%d%n", "row", i + 1);
for (int j = 0; j < dim; j++) {
System.out.printf("M[%d][%d] = ", i + 1, j + 1);
inputMatrix[i][j] = scanner.nextDouble();
}
System.out.println();
}
scanner.close();
return inputMatrix;
}}
The main reason the ForkJoin code is slower is that it's actually serialized with some thread overhead thrown in. To benefit from fork/join, you need to 1) fork all instances first, then 2) wait for the results. Split your loop in "compute" into two loops: one to fork (storing instances of ParallelDeterminants in, say, an array) and another to collect the results.
Also, I suggest to only fork at the outermost level and not in any of the inner ones. You don't want to be creating O(N^2) threads.
There is a new method of calculating the determinant of the matrix you can read more from here
and I've implemented a simple version of this with no fancy optimization techniques or library in plain simple java and I've tested against methods described previously and it was faster on average by a factor of 10
public class Test {
public static double[][] reduce(int row , int column , double[][] mat){
int n=mat.length;
double[][] res = new double[n- 1][n- 1];
int r=0,c=0;
for (int i = 0; i < n; i++) {
c=0;
if(i==row)
continue;
for (int j = 0; j < n; j++) {
if(j==column)
continue;
res[r][c] = mat[i][j];
c++;
}
r++;
}
return res;
}
public static double det(double mat[][]){
int n = mat.length;
if(n==1)
return mat[0][0];
if(n==2)
return mat[0][0]*mat[1][1] - (mat[0][1]*mat[1][0]);
//TODO : do reduce more efficiently
double[][] m11 = reduce(0,0,mat);
double[][] m1n = reduce(0,n-1,mat);
double[][] mn1 = reduce(n-1 , 0 , mat);
double[][] mnn = reduce(n-1,n-1,mat);
double[][] m11nn = reduce(0,0,reduce(n-1,n-1,mat));
return (det(m11)*det(mnn) - det(m1n)*det(mn1))/det(m11nn);
}
public static double[][] randomMatrix(int n , int range){
double[][] mat = new double[n][n];
for (int i=0; i<mat.length; i++) {
for (int j=0; j<mat[i].length; j++) {
mat[i][j] = (Math.random()*range);
}
}
return mat;
}
public static void main(String[] args) {
double[][] mat = randomMatrix(10,100);
System.out.println(det(mat));
}
}
there is a little fault in the case of the determinant of m11nn if happen to be zero it will blow up and you should check for that. I've tested on 100 random samples it rarely happens but I think it worth mentioning and also using a better indexing scheme can also improve the efficiency
This is a part of my Matrix class which uses a double[][] member variable called data to store the matrix data.
The _determinant_recursivetask_impl() function uses a RecursiveTask<Double> object with the ForkJoinPool to try to use multiple threads for calculation.
This method performs very slow compared to matrix operations to get an upper/lower triangular matrix. Try to compute the determinant of a 13x13 matrix for example.
public class Matrix
{
// Dimensions
private final int I,J;
private final double[][] data;
private Double determinant = null;
static class MatrixEntry
{
public final int I,J;
public final double value;
private MatrixEntry(int i, int j, double value) {
I = i;
J = j;
this.value = value;
}
}
/**
* Calculates determinant of this Matrix recursively and caches it for future use.
* #return determinant
*/
public double determinant()
{
if(I!=J)
throw new IllegalStateException(String.format("Can't calculate determinant of (%d,%d) matrix, not a square matrix.", I,J));
if(determinant==null)
determinant = _determinant_recursivetask_impl(this);
return determinant;
}
private static double _determinant_recursivetask_impl(Matrix m)
{
class determinant_recurse extends RecursiveTask<Double>
{
private final Matrix m;
determinant_recurse(Matrix m) {
this.m = m;
}
#Override
protected Double compute() {
// Base cases
if(m.I==1 && m.J==1)
return m.data[0][0];
else if(m.I==2 && m.J==2)
return m.data[0][0]*m.data[1][1] - m.data[0][1]*m.data[1][0];
else
{
determinant_recurse[] tasks = new determinant_recurse[m.I];
for (int i = 0; i <m.I ; i++) {
tasks[i] = new determinant_recurse(m.getSubmatrix(0, i));
}
for (int i = 1; i <m.I ; i++) {
tasks[i].fork();
}
double ret = m.data[0][0]*tasks[0].compute();
for (int i = 1; i < m.I; i++) {
if(i%2==0)
ret += m.data[0][i]*tasks[i].join();
else
ret -= m.data[0][i]*tasks[i].join();
}
return ret;
}
}
}
return ForkJoinPool.commonPool().invoke(new determinant_recurse(m));
}
private static void _map_impl(Matrix ret, Function<Matrix.MatrixEntry, Double> operator)
{
for (int i = 0; i <ret.I ; i++) {
for (int j = 0; j <ret.J ; j++) {
ret.data[i][j] = operator.apply(new Matrix.MatrixEntry(i,j,ret.data[i][j]));
}
}
}
/**
* Returns a new Matrix that is sub-matrix without the given row and column.
* #param removeI row to remove
* #param removeJ col. to remove
* #return new Matrix.
*/
public Matrix getSubmatrix(int removeI, int removeJ)
{
if(removeI<0 || removeJ<0 || removeI>=this.I || removeJ>=this.J)
throw new IllegalArgumentException(String.format("Invalid element position (%d,%d) for matrix(%d,%d).", removeI,removeJ,this.I,this.J));
Matrix m = new Matrix(this.I-1, this.J-1);
_map_impl(m, (e)->{
int i = e.I, j = e.J;
if(e.I >= removeI) ++i;
if(e.J >= removeJ) ++j;
return this.data[i][j];
});
return m;
}
// Constructors
public Matrix(int i, int j) {
if(i<1 || j<1)
throw new IllegalArgumentException(String.format("Invalid array dimensions: (%d,%d)", i, j));
I = i;
J = j;
data = new double[I][J];
}
}
int det(int[][] mat) {
if (mat.length == 1)
return mat[0][0];
if (mat.length == 2)
return mat[0][0] * mat[1][1] - mat[1][0] * mat[0][1];
int sum = 0, sign = 1;
int newN = mat.length - 1;
int[][] temp = new int[newN][newN];
for (int t = 0; t < newN; t++) {
int q = 0;
for (int i = 0; i < newN; i++) {
for (int j = 0; j < newN; j++) {
temp[i][j] = mat[1 + i][q + j];
}
if (q == i)
q = 1;
}
sum += sign * mat[0][t] * det(temp);
sign *= -1;
}
return sum;
}
I am developing the game that named Lights Out. So for solving this, i have to compute the answer of AX = B in modules 2. So, for this reason i choose jscience library. In this game the size of A is 25x25 matrix, X and B are both 25x1 matrix. I wrote the code such below :
AllLightOut.java class :
public class AllLightOut {
public static final int SIZE = 5;
public static double[] Action(int i, int j) {
double[] change = new double[SIZE * SIZE];
int count = 0;
for (double[] d : Switch(new double[SIZE][SIZE], i, j))
for (double e : d)
change[count++] = e;
return change;
}
public static double[][] MatrixA() {
double[][] mat = new double[SIZE * SIZE][SIZE * SIZE];
for (int i = 0; i < SIZE; i++)
for (int j = 0; j < SIZE; j++)
mat[i * SIZE + j] = Action(i, j);
return mat;
}
public static SparseVector<ModuloInteger> ArrayToDenseVectorModule2(
double[] array) {
List<ModuloInteger> list = new ArrayList<ModuloInteger>();
for (int i = 0; i < array.length; i++) {
if (array[i] == 0)
list.add(ModuloInteger.ZERO);
else
list.add(ModuloInteger.ONE);
}
return SparseVector.valueOf(DenseVector.valueOf(list),
ModuloInteger.ZERO);
}
public static SparseMatrix<ModuloInteger> MatrixAModule2() {
double[][] mat = MatrixA();
List<DenseVector<ModuloInteger>> list = new ArrayList<DenseVector<ModuloInteger>>();
for (int i = 0; i < mat.length; i++) {
List<ModuloInteger> l = new ArrayList<ModuloInteger>();
for (int j = 0; j < mat[i].length; j++) {
if (mat[i][j] == 0)
l.add(ModuloInteger.ZERO);
else
l.add(ModuloInteger.ONE);
}
list.add(DenseVector.valueOf(l));
}
return SparseMatrix.valueOf(DenseMatrix.valueOf(list),
ModuloInteger.ZERO);
}
public static double[][] Switch(double[][] action, int i, int j) {
action[i][j] = action[i][j] == 1 ? 0 : 1;
if (i > 0)
action[i - 1][j] = action[i - 1][j] == 1 ? 0 : 1;
if (i < action.length - 1)
action[i + 1][j] = action[i + 1][j] == 1 ? 0 : 1;
if (j > 0)
action[i][j - 1] = action[i][j - 1] == 1 ? 0 : 1;
if (j < action.length - 1)
action[i][j + 1] = action[i][j + 1] == 1 ? 0 : 1;
return action;
}
}
And the main class is as follow :
public class Main {
public static void main(String[] args) {
double[] bVec = new double[] { 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0 };
SparseMatrix<ModuloInteger> matA = AllLightOut.MatrixAModule2();
SparseVector<ModuloInteger> matB = AllLightOut
.ArrayToDenseVectorModule2(bVec);
ModuloInteger.setModulus(LargeInteger.valueOf(2));
Vector<ModuloInteger> matX = matA.solve(matB);
System.out.println(matX);
}
}
I ran this program for about 30 minutes, but it had not result. Does my code include a fatal error or wrong ? Why it takes too long ?
Thanks for your attention :)
EDIT
The slowdown happening in this line Matrix<ModuloInteger> matX = matA.inverse();. Note that the JScience benchmark result, speed for this library is very high, but i don't know why my program ran too slow!
EDIT2
Please note that when i try to SIZE = 3, i get the answer truly. For example:
MatA :
{{1, 1, 0, 1, 0, 0, 0, 0, 0},
{1, 1, 1, 0, 1, 0, 0, 0, 0},
{0, 1, 1, 0, 0, 1, 0, 0, 0},
{1, 0, 0, 1, 1, 0, 1, 0, 0},
{0, 1, 0, 1, 1, 1, 0, 1, 0},
{0, 0, 1, 0, 1, 1, 0, 0, 1},
{0, 0, 0, 1, 0, 0, 1, 1, 0},
{0, 0, 0, 0, 1, 0, 1, 1, 1},
{0, 0, 0, 0, 0, 1, 0, 1, 1}}
MatB :
{1, 1, 1, 1, 1, 1, 1, 0, 0}
MatC :
{0, 0, 1, 1, 0, 0, 0, 0, 0}
But when i try SIZE = 5, slowdown occurred.
The slowdown happening in this line Matrix<ModuloInteger> matX = matA.inverse();
That would be because the coefficient matrix matA is not invertible for SIZE == 5 (or 4, 9, 11, 14, 16, ...?).
I'm a bit surprised the library didn't detect that and throw an exception. If the library tries to invert the matrix in solve(), that would have the same consequences.
A consequence of the singularity of the coefficient matrix for some sizes is that not all puzzles for these sizes are solvable, and the others have multiple solutions.
Since we're calculating modulo 2, we can use bits or booleans to model our states/toggles, using XOR for addition and & for multiplication. I have cooked up a simple solver using Gaussian elimination, maybe it helps you (I haven't spent much time thinking about the design, so it's not pretty):
public class Lights{
private static final int SIZE = 5;
private static boolean[] toggle(int i, int j) {
boolean[] action = new boolean[SIZE*SIZE];
int idx = i*SIZE+j;
action[idx] = true;
if (j > 0) action[idx-1] = true;
if (j < SIZE-1) action[idx+1] = true;
if (i > 0) action[idx-SIZE] = true;
if (i < SIZE-1) action[idx+SIZE] = true;
return action;
}
private static boolean[][] matrixA() {
boolean[][] mat = new boolean[SIZE*SIZE][];
for(int i = 0; i < SIZE; ++i) {
for(int j = 0; j < SIZE; ++j) {
mat[i*SIZE+j] = toggle(i,j);
}
}
return mat;
}
private static void rotateR(boolean[] a, int r) {
r %= a.length;
if (r < 0) r += a.length;
if (r == 0) return;
boolean[] tmp = new boolean[r];
for(int i = 0; i < r; ++i) {
tmp[i] = a[i];
}
for(int i = 0; i < a.length - r; ++i) {
a[i] = a[i+r];
}
for(int i = 0; i < r; ++i) {
a[i + a.length - r] = tmp[i];
}
}
private static void rotateR(boolean[][] a, int r) {
r %= a.length;
if (r < 0) r += a.length;
if (r == 0) return;
boolean[][] tmp = new boolean[r][];
for(int i = 0; i < r; ++i) {
tmp[i] = a[i];
}
for(int i = 0; i < a.length - r; ++i) {
a[i] = a[i+r];
}
for(int i = 0; i < r; ++i) {
a[i + a.length - r] = tmp[i];
}
}
private static int count(boolean[] a) {
int c = 0;
for(int i = 0; i < a.length; ++i) {
if (a[i]) ++c;
}
return c;
}
private static void swapBits(boolean[] a, int i, int j) {
boolean tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
private static void addBit(boolean[] a, int i, int j) {
a[j] ^= a[i];
}
private static void swapRows(boolean[][] a, int i, int j) {
boolean[] tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
private static void xorb(boolean[] a, boolean[] b) {
for(int i = 0; i < a.length; ++i) {
a[i] ^= b[i];
}
}
private static boolean[] boolBits(int bits, long param) {
boolean[] bitArr = new boolean[bits];
for(int i = 0; i < bits; ++i) {
if (((param >> i) & 1L) != 0) {
bitArr[i] = true;
}
}
return bitArr;
}
private static boolean[] solve(boolean[][] m, boolean[] b) {
// Move first SIZE rows to bottom, so that on the diagonal
// above the lowest SIZE rows, there are unit matrices
rotateR(m, SIZE);
// modify right hand side accordingly
rotateR(b,SIZE);
// clean first SIZE*(SIZE-1) columns
for(int i = 0; i < SIZE*(SIZE-1); ++i) {
for(int k = 0; k < SIZE*SIZE; ++k) {
if (k == i) continue;
if (m[k][i]) {
xorb(m[k], m[i]);
b[k] ^= b[i];
}
}
}
// Now we have a block matrix
/*
* E 0 0 ... 0 X
* 0 E 0 ... 0 X
* 0 0 E ... 0 X
* ...
* 0 0 ... E 0 X
* 0 0 ... 0 E X
* 0 0 ... 0 0 Y
*
*/
// Bring Y to row-echelon form
int i = SIZE*(SIZE-1), j, k, mi = i;
while(mi < SIZE*SIZE){
// Try to find a row with mi-th bit set
for(j = i; j < SIZE*SIZE; ++j) {
if (m[j][mi]) break;
}
if (j < SIZE*SIZE) {
// Found one
if (j > i) {
swapRows(m,i,j);
swapBits(b,i,j);
}
for(k = 0; k < SIZE*SIZE; ++k) {
if (k == i) continue;
if (m[k][mi]) {
xorb(m[k], m[i]);
b[k] ^= b[i];
}
}
// cleaned up column, good row, next
++i;
}
// Look at next column
++mi;
}
printMat(m,b);
boolean[] best = b;
if (i < SIZE*SIZE) {
// We have zero-rows in the matrix,
// check whether the puzzle is solvable at all,
// i.e. all corresponding bits in the rhs are 0
for(j = i; j < SIZE*SIZE; ++j) {
if (b[j]) {
System.out.println("Puzzle not solvable, some lights must remain lit.");
break;
// throw new IllegalArgumentException("Puzzle is not solvable!");
}
}
// Pretending it were solvable if not
if (j < SIZE*SIZE) {
System.out.println("Pretending the puzzle were solvable...");
for(; j < SIZE*SIZE; ++j) {
b[j] = false;
}
}
// Okay, puzzle is solvable, but there are several solutions
// Let's try to find the one with the least toggles.
// We have the canonical solution with last bits all zero
int toggles = count(b);
System.out.println(toggles + " toggles in canonical solution");
int freeBits = SIZE*SIZE - i;
long max = 1L << freeBits;
System.out.println(freeBits + " free bits");
// Check all combinations of free bits whether they produce
// something better
for(long param = 1; param < max; ++param) {
boolean[] base = boolBits(freeBits,param);
boolean[] c = new boolean[SIZE*SIZE];
for(k = 0; k < freeBits; ++k) {
c[i+k] = base[k];
}
for(k = 0; k < i; ++k) {
for(j = 0; j < freeBits; ++j) {
c[k] ^= base[j] && m[k][j+i];
}
}
xorb(c,b);
int t = count(c);
if (t < toggles) {
System.out.printf("Found new best for param %x, %d toggles\n",param,t);
printMat(m,c,b);
toggles = t;
best = c;
} else {
System.out.printf("%d toggles for parameter %x\n", t, param);
}
}
}
return best;
}
private static boolean[] parseLights(int[] lights) {
int lim = lights.length;
if (SIZE*SIZE < lim) lim = SIZE*SIZE;
boolean[] b = new boolean[SIZE*SIZE];
for(int i = 0; i < lim; ++i) {
b[i] = (lights[i] != 0);
}
return b;
}
private static void printToggles(boolean[] s) {
for(int i = 0; i < s.length; ++i) {
if (s[i]) {
System.out.print("(" + (i/SIZE + 1) + ", " + (i%SIZE + 1) + "); ");
}
}
System.out.println();
}
private static void printMat(boolean[][] a, boolean[] rhs) {
for(int i = 0; i < SIZE*SIZE; ++i) {
for(int j = 0; j < SIZE*SIZE; ++j) {
System.out.print((a[i][j] ? "1 " : "0 "));
}
System.out.println("| " + (rhs[i] ? "1" : "0"));
}
}
private static void printMat(boolean[][] a, boolean[] sol, boolean[] rhs) {
for(int i = 0; i < SIZE*SIZE; ++i) {
for(int j = 0; j < SIZE*SIZE; ++j) {
System.out.print((a[i][j] ? "1 " : "0 "));
}
System.out.println("| " + (sol[i] ? "1" : "0") + " | " + (rhs[i] ? "1" : "0"));
}
}
private static void printGrid(boolean[] g) {
for(int i = 0; i < SIZE; ++i) {
for(int j = 0; j < SIZE; ++j) {
System.out.print(g[i*SIZE+j] ? "1" : "0");
}
System.out.println();
}
}
public static void main(String[] args) {
int[] initialLights = new int[] { 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,
1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0 };
boolean[] b = parseLights(initialLights);
boolean[] b2 = b.clone();
boolean[][] coefficients = matrixA();
boolean[] toggles = solve(coefficients, b);
printGrid(b2);
System.out.println("--------");
boolean[][] check = matrixA();
boolean[] verify = new boolean[SIZE*SIZE];
for(int i = 0; i < SIZE*SIZE; ++i) {
if (toggles[i]) {
xorb(verify, check[i]);
}
}
printGrid(verify);
xorb(b2,verify);
if (count(b2) > 0) {
System.out.println("Aww, shuck, screwed up!");
printGrid(b2);
}
printToggles(toggles);
}
}
You almost never want to calculate the actual inverse of a matrix if it can be avoided. Such operations are problematic and highly time consuming. Looking at the docs for JScience have you considered using the solve method? Something along the lines of matX = matA.solve(matB) should give you what you're looking for and I doubt they're using an inverse to calculate that, although I haven't dug that far into JScience so it's not impossible.