Remove zeros in an array without creating new method - java

Im trying to remove zeros from an arrays. I could do a separate method to remove the zeros but i want it in idiomatic way.
public int[] commonFactors(int m, int n) {
int[] numbers = new int[n + 1];
for(int i = 1; i < n + 1; i++) {
if ((m % i == 0) && (n % i == 0)) {
numbers[i] = i;
}
}
return numbers;//or return DelZero(numbers);
}
And here is the method to remove all the zeros:
public int[] DelZero(int numbers[]) {
int zeroCount=0;
for (int i = 0; i < numbers.length; i++) {
if (numbers[i] == 0) {
zeroCount++;
}
}
int[] newNumber = new int[numbers.length-zeroCount];
int j =0;
for (int i=0; i<numbers.length; i++) {
if (numbers[i] != 0) {
newNumber[j++] = numbers[i];
}
}
return newNumber;
}
So the crux of my question is this: Is there a way to remove the zeros without creating a new method? (In this case without DelZero.)

With Java8, you can exploit Stream to do it very easily:
int[] arr = new int[]{0,0,7,6,5,0,1,0,4};
int[] arr_without_zeros = IntStream.of(arr).filter(i -> i != 0).toArray();

Here's a quick example from which you should be able to answer your question:
import java.util.Arrays;
public class RemoveZeros {
public static void main(String[] args) {
int[] zeroArray = { 0, 1, 0, 2, 0, 3, 0, 4, 0, 5 };
System.out.println("Original array: " + Arrays.toString(zeroArray));
int nonZeros = 0;
for (int i = 0; i < zeroArray.length; i++) {
if (zeroArray[i] != 0) {
nonZeros++;
}
}
int[] tempArray = new int[nonZeros];
int tempIndex = 0;
for (int j = 0; j < zeroArray.length; j++) {
if (zeroArray[j] != 0) {
tempArray[tempIndex] = zeroArray[j];
tempIndex++;
}
}
zeroArray = tempArray;
System.out.println("Modified array: " + Arrays.toString(zeroArray));
}
}
Output:
Original array: [0, 1, 0, 2, 0, 3, 0, 4, 0, 5]
Modified array: [1, 2, 3, 4, 5]

I'd consider using java.util.List instead of primitive array. Suddenly everything is a lot easier...
public List<Integer> commonFactors(int m, int n) {
List<Integer> numbers = new ArrayList<Integer>();
for (int i = 1; i < n + 1; i++) {
if ((m % i == 0) && (n % i == 0) && i != 0) {
numbers.add(i);
}
}
return numbers;
}

Related

Error when setting entry of int[] array to null

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();

assigning an element to an empty array

How do I assign the same elements from two arrays to another empty array in Java?
I can't figure out how to select the same elements from two arrays and assign them to another empty array. I don't know how to do without using ArrayList.
public class Main {
public static void main(String[] args) {
findDuplicate();
}
public static void findDuplicate() {
try {
int[] array1 = {12, 15, 6, 3, 9, 8, 1, 3, 99};
int[] array2 = new int[]{1, 5, 3, 3, 8, 99};
int[] sameElements = {};
int count = 0;
for (int i = 0; i < array1.length; i++) {
for (int j = 0; j < array2.length; j++) {
if (array1[i] == array2[j]) {
count++;
for (int k = 0; k < count; k++) {
sameElements[k] = array1[i];
System.out.println("sameElements[k] = " + sameElements[k]);
}
}
}
}
} catch (Exception e) {
System.out.println("Error: " + e);
}
}
}
Do it as follows:
public class Main {
public static void main(String[] args) {
findDuplicate();
}
public static void findDuplicate() {
int[] array1 = { 12, 15, 6, 3, 9, 8, 1, 3, 99 };
int[] array2 = { 1, 5, 3, 3, 8, 99 };
// Array of the smaller length (out of the lengths of array1 and array2)
int[] sameElements = new int[array1.length < array2.length ? array1.length : array2.length];
int i, j, k, count = 0;
for (i = 0; i < array1.length; i++) {
for (j = 0; j < array2.length; j++) {
if (array1[i] == array2[j]) {
// If 0 is present in both, array1[] and array2[]
if (array1[i] == 0) {
count++;
continue;
}
// Search if array1[i] exists in sameElements[]
for (k = 0; k < count; k++) {
if (sameElements[k] == array1[i]) {
break;
}
}
// If array1[i] is not already in sameElements[], put it into sameElements[]
if (k == count) {
sameElements[count++] = array1[i];
}
}
}
}
System.out.print("Duplicates: ");
for (i = 0; i < count; i++) {
System.out.print(sameElements[i] + " ");
}
}
}
Output:
Duplicates: 3 8 1 99

Two Sum II - Input array is sorted

Leetcode #167 is almost same as #1, but why I cannot only add a if condition?
Q: Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number.
The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2.
Note:
Your returned answers (both index1 and index2) are not zero-based.
You may assume that each input would have exactly one solution and you may not use the same element twice.
Example:
Input: numbers = [2,7,11,15], target = 9
Output: [1,2]
The sum of 2 and 7 is 9.
Therefore index1 = 1, index2 = 2.
My code:
class Solution {
public int[] twoSum(int[] numbers, int target) {
for (int i = 1; i < numbers.length; i++) {
for (int j = i + 1; j < numbers.length; j++) {
if (numbers[j] == target - numbers[i]) {
if(numbers[i] < numbers[j])
return new int[] { i, j };
}
}
}
return null;
}
}
Why I always return null? where is my mistake? How to fix it?
Because the question says array starts from 1 does not mean array starts from 1 in java.If you want to return i,j as non-zero you should go from 1 to length+1 and then inside the conditions you should check indexes as i-1,j-1 or just start from 0 and return i+1,j+1.
class Solution {
public int[] twoSum(int[] numbers, int target) {
for (int i = 1; i < numbers.length+1; i++) {
for (int j = i + 1; j < numbers.length+1; j++) {
if (numbers[j-1] == target - numbers[i-1]) {
if(numbers[i-1] < numbers[j-1])
return new int[] { i, j };
}
}
}
return null;
}
}
or you can do,
class Solution {
public int[] twoSum(int[] numbers, int target) {
for (int i = 0; i < numbers.length; i++) {
for (int j = i + 1; j < numbers.length; j++) {
if (numbers[j] == target - numbers[i]) {
if(numbers[i] < numbers[j])
return new int[] { i+1, j+1 };
}
}
}
return null;
}
}
https://leetcode.com/problems/two-sum-ii-input-array-is-sorted/
[question]: 167. Two Sum II - Input array is sorted
Using the two-pointer technique:-
class Solution {
public int[] twoSum(int[] numbers, int target) {
if (numbers == null || numbers.length == 0)
return null;
int i = 0;
int j = numbers.length - 1;
while (i < j) {
int x = numbers[i] + numbers[j];
if (x < target) {
++i;
} else if (x > target) {
j--;
} else {
return new int[] { i + 1, j + 1 };
}
}
return null;
}
}
I have modified your code and added code comments on why your previous code has errors. Refer to code below for details.
public class Main {
public static void main(String[] args) {
int target = 9;
int[] numbers = new int[] { 2, 7, 11, 15 };
int[] result = twoSum(numbers, target);
if (result != null) {
System.out
.println("The sum of " + numbers[result[0]] + " and " + numbers[result[1]] + " is " + target + ".");
System.out.println("Therefore index1 = " + (result[0] + 1) + ", index2 = " + (result[1] + 1));
} else {
System.out.println("No Solution found!");
}
}
public static int[] twoSum(int[] numbers, int target) {
for (int i = 0; i < numbers.length; i++) { // array index starts at 0
for (int j = i + 1; j < numbers.length; j++) {
if (numbers[j] + numbers[i] == target) { // add the current numbers
// if (numbers[i] < numbers[j]) // not needed
return new int[] { i, j };
}
}
}
return null;
}
}
Sample input:
numbers = [2, 7, 11, 15];
Sample output:
The sum of 2 and 7 is 9.
Therefore index1 = 1, index2 = 2
You are starting first for-loop with i = 0, what you should do is start it with i = 1.
Working code:
public class Solution
{
public static void main(String[] args)
{
int[] num = {2,7,11,5};
int n = 13;
int[] answer = new int[2];
answer = twoSum(num,n);
if(answer != null)
for(int i=0;i<2;i++)
System.out.printf( answer[i] +" ");
}
public static int[] twoSum(int[] numbers, int target)
{
for (int i = 0; i < numbers.length; i++)
{
for (int j = i + 1; j < numbers.length; j++)
{
if (numbers[j] == target - numbers[i])
{
if(numbers[i] < numbers[j])
return new int[] { i+1, j+1};
}
}
}
return null;
}
}
Note: I have placed an IF before FOR in main() so that if we find no such integers that adds up to give target integer, it'll not throw a NullPointerException.
This is a better solution as it's much faster and covers all test cases as well:
class Solution {
public int[] twoSum(int[] numbers, int target) {
int l = 0, r = numbers.length - 1;
while (numbers[l] + numbers[r] != target) {
if (numbers[l] + numbers[r] > target)
r--;
else
l++;
if (r == l) return new int[]{};
}
return new int[]{l + 1, r + 1};
}
}
public int[] twoSum(int[] nums, int target) {
int start = 0, end = nums.length -1;
while (start < end){
if (nums[start]+ nums[end]== target)
return new int []{start+1, end+1};
if (nums[start]+ nums[end]> target){
end--;}
else if (nums[start]+ nums[end]< target){
start++;
}
}
return null;
}

Determinant of huge matrix Java

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

Matrix Computing is too slow

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.

Categories

Resources