1

I was set with a task to perform matrix multiplication using file input. The actual math for the multiplication process is no issue; it's the storing of data into two-dimensional arrays that left me puzzled.

This is the data file that I have to use to create my two-dimensional arrays:

matrix
row
1
2
-2
0
row
-3
4
7
2
row
6
0
3
1
matrix
row
-1
3
row
0
9
row
1
-11
row
4
-5

The rules are quite simple: The start of a new matrix will be indicated with "matrix" and the start of a new row will be indicated with "row" followed by the numbers assigned to each column of that row.

For context, here is my matrix multiplication method:

static int[][] mult(int[][] a, int[][] b) {

    int aRow = a.length;
    int aCol = a[0].length;

    int bRow = b.length;
    int bCol = b[0].length;

    if (bRow != aCol) {
        throw new IllegalArgumentException("Matrix A is not multipliable by Matrix B");
    }

    int[][] product = new int[aRow][bCol];

    for (int i = 0; i < product.length; i++) {
        for (int j = 0; j < product[i].length; j++) {
            for (int k = 0; k < aCol; k++) {
                product[i][j] += a[i][k] * b[k][j];
            }
        }
    }
    return product;
}

And here is the class with the main method where I'm trying to store data from the above text file into two-dimensional arrays (trying to store the first matrix into the 2d array named "a" and the second matrix into the 2d array named "b"):

public static void main(String[] args) throws FileNotFoundException {

    Scanner scanner = new Scanner(new File("/Users/Krish/IdeaProjects/Lessons/src/Lesson34/MatrixData.txt"));
    String text[] = new String[100];
    int index = -1;

    while (scanner.hasNext()) {
        text[++index] = scanner.nextLine();
    }


    int[][] a = {{}};
    int[][] b = {{}};
    int[][] product = MatrixMult.mult(a, b);

    for (int i = 0; i < product.length; i++) {
        for (int j = 0; j < product[i].length; j++) {
            System.out.print(product[i][j] + "\t");
        }
        System.out.println();
    }

    scanner.close();

}

I know I have to do something like the following, but honestly, I have no idea how, and would really appreciate some help/guidance:

for (int i = 0; i <= index; i++) {

        Scanner line = new Scanner(text[i]);
        int n = 0;

        while (line.hasNextInt()) {
            n = line.nextInt();
            for (int j = 0; j < a.length; j++) {
                for (int k = 0; k < a[j].length; k++) {
                    a[j][k] = n;
                }
            }
        }
}

4 Answers 4

2

I recommend you to use Java Collections instead of arrays and read matrix in this way. For example, you read "matrix" value from the input stream and call this method:

private int[][] readMatrix(final BufferedReader reader) {
    List<List<Integer>> matrix = new ArrayList<>();
    int rowNumber = -1;
    while(reader.hasNext()) {
        String value = reader.readLine();
        if ("row".equals(value)) {
            ++rowNumber;
            matrix.add(new ArrayList<Integer>());
        } else {
            int intValue = Integer.parseInt(value);
            matrix.get(rowNumber).add(intValue);
        }
    }

    // convert to an array
    int[][] array = new int[matrix.size()][];
    for (int i = 0; i < matrix.size(); ++i) {
        List<Integer> row = matrix.get(i);
        array[i] = row.toArray(new int[row.size()]);
    }
    return array;
}
Sign up to request clarification or add additional context in comments.

6 Comments

I would love to use collections and make my code simpler; however, the task specifies that I use two-dimensional arrays. And I'd rather master this sub-topic before I move on to more complex topics such as collections. Thanks for the advice though :)
You cant easily convert two-dimensional list to corresponding array. I will edit my answer
Added conversion to the end of the method, check it out
@Krish how would you know the exact size of the matrix (lines/columns) if you want to use only static primitive arrays ? Since you must initialize them.
@maspinu well that's the thing - you have to use the text file to create the matrix. Each row in the matrix is separated by a "row" keyword, and each matrix is separated by the "matrix" keyword. So you have to find out how many columns the matrix has by finding out how many pieces of data are in each row.
|
0

This should do the trick (implementation using static arrays):

public class Main {
    private static final String MATRIX_WORD = "matrix";
    private static final String ROW_WORD = "row";

    public static void main(String[] args) throws FileNotFoundException {
        int[][][] allMatrix = getAllMatrix(args[0]);

        for (int[][] currentMatrix : allMatrix) {
            for (int i = 0 ; i < currentMatrix.length; i++) {
                for (int j = 0; j < currentMatrix[i].length; j++) {
                    System.out.print(currentMatrix[i][j] + " ");
                }
                System.out.println();
            }
            System.out.println("\n\n");
        }
    }

    private static int[][][] getAllMatrix(String fileName) throws FileNotFoundException {
        int[][][] allMatrix = new int[0][0][0];
        int[][] currentMatrix = new int[0][0];
        String line;

        try (BufferedReader br = new BufferedReader(new FileReader(fileName))) {
            while ((line = br.readLine()) != null) {

                switch (line) {
                    case MATRIX_WORD:
                        allMatrix = Arrays.copyOf(allMatrix, allMatrix.length + 1);
                        allMatrix[allMatrix.length - 1] = currentMatrix;
                        currentMatrix = new int[0][0];
                        break;
                    case ROW_WORD:
                        currentMatrix = Arrays.copyOf(currentMatrix, currentMatrix.length + 1);
                        currentMatrix[currentMatrix.length - 1] = new int[0];
                        break;
                    default:
                        currentMatrix[currentMatrix.length - 1] = Arrays.copyOf(currentMatrix[currentMatrix.length - 1],
                                currentMatrix[currentMatrix.length - 1].length + 1);
                        currentMatrix[currentMatrix.length - 1][currentMatrix[currentMatrix.length - 1].length - 1] = Integer.parseInt(line);
                        break;
                }
            }

            allMatrix = Arrays.copyOf(allMatrix, allMatrix.length + 1);
            allMatrix[allMatrix.length - 1] = currentMatrix;

        } catch (IOException e) {
            e.printStackTrace();
        }

        return allMatrix;
    }
}

I've used Arrays.copyof() to extend the current array (let it allow more space for the elements).

For your input file the output is :

1 2 -2 0 
-3 4 7 2 
6 0 3 1 


-1 3 
0 9 
1 -11 
4 -5 

I'm sure there is space for improvements in this algorithm, however it should give the right results.

Comments

0
public static void main(String[] args) throws FileNotFoundException {

    /*
     * -3   43
     * 18   -60
     * 1    -20
     */

    Scanner scanner = new Scanner(new File("/Users/Krish/IdeaProjects/Lessons/src/Lesson34/MatrixData"));
    String[] text = new String[100];
    int index = -1;

    while (scanner.hasNext()) {
        text[++index] = scanner.nextLine();
    }

    scanner.close();

    int matrixCount = 0;
    int rowCount = 0, colCount = 0;
    int aRows = 0, aCols = 0;
    int bRows, bCols;

    for (int i = 0; i <= index; i++) {
        switch (text[i]) {
            case "matrix":
                if (++matrixCount == 2) {
                    aRows = rowCount;
                    aCols = colCount;
                }
                rowCount = 0;
                colCount = 0;
                break;
            case "row":
                rowCount++;
                colCount = 0;
                break;
            default:
                colCount++;
                break;
        }
    }

    bRows = rowCount;
    bCols = colCount;

    int[][] a = new int[aRows][aCols];
    int[][] b = new int[bRows][bCols];

    matrixCount = 0;
    int rowIndex = -1, colIndex = -1;

    for (int i = 0; i <= index; i++) {
        switch (text[i]) {
            case "matrix":
                matrixCount++;
                rowIndex = -1;
                colIndex = -1;
                break;
            case "row":
                rowIndex++;
                colIndex = -1;
                break;
            default:
                colIndex++;
                if (matrixCount == 1) {
                    a[rowIndex][colIndex] = Integer.parseInt(text[i]);
                } else {
                    b[rowIndex][colIndex] = Integer.parseInt(text[i]);
                }
                break;
        }
    }

    int[][] product = MatrixMult.mult(a, b);

    for (int i = 0; i < product.length; i++) {
        for (int j = 0; j < product[i].length; j++) {
            System.out.print(product[i][j] + "\t");
        }
        System.out.println();
    }

}

Comments

0

I suggest a solution without for loops, and full input validation. instead of loops, you can use streams of java 8 The validation phase includes: regex matching, size & dimensions check.

The solution includes the following steps:
- Reading input matrices.
- Validating matrices.
- Converting the input into 3-d int array, with 2 cells.
  (Each cell contains a 2-d int array matrix)
- Multiplying the matrices.

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.stream.IntStream;

import org.apache.commons.io.FileUtils;

/**
 * This class demonstrates multiplication of 2 matrices.
 * Including:
 * - Reading input matrices from file.
 * - Validating matrices input format (using Regex).
 * - Converting input to 3-d array with 2 matrices (using streams).
 * - Validating matrices sizes & dimensions.
 * - multiplication of matrices (using streams).
 */
public class CreateTwo2dArraysFromATextFile {
    final private static String FILE_PATH = "matrices.txt";
    final private static String ENCODING = "UTF-8";
    final private static String INPUT_FORMAT = "^(-?\\s*matrix\\s*(-?\\s+row(\\s+-?(\\d+))+)+){2}$";
    final private static String MATRIX_TITLE = "matrix";
    final private static String ROW_TITLE = "row";
    final private static String MATRIX_DELIMITER = "\r\n";
    final private static String ROW_DELIMITER = "\r\n";

    public static void main(String[] args) throws IOException {
        int[][][] matrices = fetchMatrices();
        validateMatrices(matrices[0], matrices[1]);
        displayMatrices(matrices);
        displayMatricesMultiplicationResult(matrices[0], matrices[1]);
    }

    /**
     * - Read 2 matrices from input file
     * - Validate input format
     * - Extract 2 matrices from the input file
     * @return 2 matrices in 3-d int array format
     * @throws IOException
     */
    private static int[][][] fetchMatrices() throws IOException{
        String input = FileUtils.readFileToString(new File(getFile(FILE_PATH)), ENCODING);
        validateInputFormat(input);
        System.out.println("Input from " + FILE_PATH);
        System.out.println(input);
        return getMatrices(input);
    }

    private static void validateMatrices(int[][] m1, int[][] m2) {
        StringBuilder errors = collectInputErrors(m1, m2);
        if(errors != null) {
            throw new RuntimeException(errors.append("\nCannot multiply matrices, becuase the input is invalid").toString());
        }
    }

    private static void displayMatrices(int[][][] matrices) {
        System.out.println("\nMatrices in 3-d int array format:");
        System.out.println(Arrays.deepToString(matrices));
    }

    private static void displayMatricesMultiplicationResult(int[][] m1, int[][] m2) {
        System.out.println("\nMatrices Multiplication result:");
        int[][] multResult = multiplyMatrices(m1, m2);
        System.out.println(Arrays.deepToString(multResult));
    }

    private static String getFile(String fileName){
        return Thread.currentThread().getContextClassLoader().getResource(fileName).getPath();
    }

    private static boolean isValidInput(String input) {
        return input != null && input.matches(INPUT_FORMAT);
    }

    private static void validateInputFormat(String input) {
        if(!isValidInput(input)) {
            throw new RuntimeException("Invalid input format: " + input);
        }
    }

    /**
     * Attempt to detect the following validation errors:
     * - The number of columns in m1 or m2 is not identical across all of the rows
     *   (There is at least one row with number of columns, which is different than the number of columns of all of the rows)
     * - Matrices multiplication size constraints: the number of columns in m1, must be equals to the number of rows in m2.
     * @param m1 first matrix
     * @param m2 second matrix
     * @return error messages if validation violations are detected.
     *          Otherwise, null will be retrieved.
     */
    private static StringBuilder collectInputErrors(int[][] m1, int[][] m2) {
        StringBuilder errors = new StringBuilder(); 
        int invalidSizeRowIndex1 =  getInValidSizeMatrixRowIndex(m1);
        int invalidSizeRowIndex2 =  getInValidSizeMatrixRowIndex(m2);

        if(invalidSizeRowIndex1 != -1 || invalidSizeRowIndex2 != -1) {
            errors.append("Invalid matrices size detected:");
        }

        if(invalidSizeRowIndex1 != -1) {
            errors.append(getInvalidMatrixMessage(
                    "first",invalidSizeRowIndex1 + 1,
                    m1[invalidSizeRowIndex1].length, m1[invalidSizeRowIndex1 - 1].length));
        }

        if(invalidSizeRowIndex2 != -1) {
            errors.append(getInvalidMatrixMessage(
                    "second",invalidSizeRowIndex2 + 1,
                    m2[invalidSizeRowIndex2].length, m2[invalidSizeRowIndex2 - 1].length));
        }

        int invalidDimensionRowIndex = getDimensionViolationIndex(m1, m2);

        if(invalidSizeRowIndex1 == -1 && invalidSizeRowIndex2 == -1 && invalidDimensionRowIndex == -1) {
            return null;
        }

        if(invalidDimensionRowIndex != -1 ) {
            errors.append("\nInvalid matrices dimensions detected:");
            errors.append(getInvalidMatrixMessage(
                    "first",invalidDimensionRowIndex + 1,
                    m1[invalidDimensionRowIndex].length, m2.length));
        }

        return errors;
    }

    private static String getInvalidMatrixMessage(String matrixTitle, int invalidRowIndex, int columnSize, int expectedColumnSize) {
        return String.format("In the %s matrix, at the %d 'th row, a column with size of %d , is invalid. (expected column size is: %d)",
                matrixTitle, invalidRowIndex, columnSize, expectedColumnSize);

    }

    /**
     * Get the index of the first row in m1, that violates the matrices multiplication size constraints
     * Matrix multiplication is possible iff the number of columns in m1 equals to the number of rows in m2.
     * @param m1 first matrix
     * @param m2 second matrix
     * @return the first row index in m1 with column size
     *          which is different than the number of rows in m2.
     *          If there is no such row, then (-1) will be retrieved.
     *          
     */
    private static int getDimensionViolationIndex(int[][] m1, int[][] m2) {
        return IntStream.range(0, m1.length).filter(i -> m1[i].length != m2.length).findFirst().orElse(-1);
    }

    /**
     * Get the index of the first row with invalid columns size (If exist)
     * @param m matrix
     * @return the first index of row,
     *          which has number of columns that is different than the previous row.
     *          If there is no such row, then (-1) will be retrieved.
     */
    private static int getInValidSizeMatrixRowIndex(int[][] m) {
        return IntStream.range(1, m.length).filter(i -> m[i].length != m[i-1].length).findFirst().orElse(-1);
    }

    /**
     * Extract 2 matrices in 3-d int array format, using streams
     * @param input 
     * @return 3-d int array,
     *          where the first cell is the first 2-d matrix
     *          and the second cell is the second 2-d matrix
     */
    private static int[][][] getMatrices(String input) {
        return Arrays.asList(input.split(MATRIX_TITLE))
                .stream().filter(e -> !e.equals(""))
                .map(k-> Arrays.stream(k.split(MATRIX_TITLE))
                .map(r -> r.split(MATRIX_DELIMITER + ROW_TITLE))
                .flatMap(r -> Arrays.stream(r))
                .filter(e -> !e.equals(""))
                .map(r-> Arrays.stream(r.split(ROW_DELIMITER))
                .filter(e -> !e.equals(""))
                .mapToInt(Integer::parseInt).toArray()
                ).toArray(int[][]::new)).toArray(int[][][]::new);
    }

    /**
     * Multiply 2 matrices
     * @param m1 first matrix
     * @param m2 second matrix
     * @return m1 X m2
     */
    private static int[][] multiplyMatrices(int[][] m1, int[][] m2) {
        return Arrays.stream(m1).map(r -> 
        IntStream.range(0, m2[0].length).map(i -> 
        IntStream.range(0, m2.length).map(j -> r[j] * m2[j][i]).sum()
                ).toArray()).toArray(int[][]::new);
    }
}

Note: if you would like to change your input format. such as delimiters you can change the relevant constants:

For example for input in this format: matrix row -2 0 1 3 row -3 5 1 2 row 0 4 3 1 matrix row -1 3 4 row 0 4 9 row 1 -11 5 row 4 -5 7 use this configuration: MATRIX_DELIMITER = " " ROW_DELIMITER = " "

for input given is separated lines (like the input which is described in your example) use this configuration: MATRIX_DELIMITER = "\r\n" ROW_DELIMITER = "\r\n"

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.