Javascript | Level2. 행렬의 곱셈

임하스·2021년 11월 2일
0

1. 문제
2. 풀이
3. 결과
4. 참고

1. 문제


행렬의 곱셈

문제 링크

문제 설명

2차원 행렬 arr1과 arr2를 입력받아, arr1에 arr2를 곱한 결과를 반환하는 함수, solution을 완성해주세요.


제한 사항

  • 행렬 arr1, arr2의 행과 열의 길이는 2 이상 100 이하입니다.
  • 행렬 arr1, arr2의 원소는 -10 이상 20 이하인 자연수입니다.
  • 곱할 수 있는 배열만 주어집니다.

입출력 예

arr1arr2return
[[1, 4], [3, 2], [4, 1]][[3, 3], [3, 3]][[15, 15], [15, 15], [15, 15]]
[[2, 3, 2], [4, 2, 4], [3, 1, 4]][[5, 4, 3], [2, 4, 1], [3, 1, 1]][[22, 22, 11], [36, 28, 18], [29, 20, 14]]


2. 풀이


arr1과 arr2를 이용하여 행렬 곱셈을 수행한다.

행렬의 곱셈은 아래와 같이 2X3, 3X2 배열이 예시로 있을 경우

  • 배열1
    2X3[0][1][2]
    [0]abc
    [1]def
  • 배열2
    3X2[0][1]
    [0]AD
    [1]BE
    [2]CF

다음과 같이 계산한다.

2X2[0][1]
[0]aA + bB + cCaD + aE + aF
[1]dA + eB + fCdD + eE + fF

결과로 나오는 행렬은 (배열1의 세로(행) 길이 X 배열2의 가로(열) 길이) 크기로 생성된다.

  1. 우선 행렬의 결과를 담을 2차원 배열(answer)을 초기화 해준다.
let answer = new Array(arr1.length);

for(let i = 0; i < answer.length; i++) {
  answer[i] = new Array(arr2[0].length).fill(0);
}
  1. 반복되어야 하는 순서가 중요하다.

    우선 반복되어야 하는 것은 다음과 같다.

    • arr1의 세로 ⇒ arr1 한 줄씩
    • arr2의 가로 ⇒ arr2 한 줄씩, 각각의 내용
    • arr1의 가로 ⇒ arr1 한 줄씩, 각각의 내용

    첫 번째 반복문(i)은 arr1의 세로 인덱스를 제어할 수 있고

    두 번째 반복문(j)은 arr2의 가로 인덱스를 제어할 수 있다.

    그렇다면 마지막으로 arr1의 가로 인덱스와 arr2의 세로 인덱스를 제어해 줄 반복문(k)을 하나 더 추가한다.

    arr1 가로 길이와 arr2 세로 길이가 같아야 식이 성립할 수 있는데, 제한 조건으로 항상 계산할 수 있는 식이 오기 때문에 따로 처리할 필요는 없다.

for(let i = 0; i < arr1.length; i++) { // arr1의 세로만큼 반복 : answer의 세로, arr1의 세로 인덱스 제어
  for(let j = 0; j < arr2[0].length; j++) { // arr2의 가로만큼 반복 : answer의 가로, arr2의 가로 인덱스 제어
    for(let k = 0; k < arr1[0].length; k++) { // arr1의 가로만큼 반복 : arr1의 가로, arr2의 세로 인덱스 제어
      answer[i][j] = "계산한 값";
    }
  }
}
  1. arr1의 가로와 arr2의 세로를 제어하기 위해서 k를 이용한다.

    앞서 말했듯이, 각각이 제어할 수 있는 인덱스가 정해져 있다.

    • i : arr1의 세로 인덱스
    • j : arr2의 가로 인덱스
    • k : arr1의 가로, arr2의 세로 인덱스

    그대로 식에 넣기만 하면 된다.

    answer[i][j]에 계속 더해주는 이유는 아래 예시에서 알 수 있다.

    answer[0][0]에 저장되는 값은 arr1[0][0] * arr2[0][0], arr1[0][1] * arr2[1][0], arr1[0][2] * arr2[2][0] 이 세 가지 값을 더한 값이기 때문이다.

answer[i][j] += arr1[i][k] * arr2[k][j];

// 계산 예시
// answer[0][0] = 
// 1:	(arr1[0][0] * arr2[0][0]) + 
// 2:	(arr1[0][1] * arr2[1][0]) + 
// 3:	(arr1[0][2] * arr2[2][0]);

// answer[0][1] = 
//	(arr1[0][0] * arr2[0][1]) + 
//	(arr1[0][1] * arr2[1][1]) + 
//	(arr1[0][2] * arr2[2][1]);

// answer[1][0] = 
//	(arr1[1][0] * arr2[0][0]) + 
//	(arr1[1][1] * arr2[1][0]) + 
//	(arr1[1][2] * arr2[2][0]);

// answer[1][1] = 
//	(arr1[1][0] * arr2[0][1]) + 
//	(arr1[1][1] * arr2[1][1]) + 
//	(arr1[1][2] * arr2[2][1]);

제출

function solution(arr1, arr2) {
  let row1 = arr1.length;
  let col1 = arr1[0].length;
  let col2 = arr2[0].length;
  
  let answer = new Array(arr1.length);

  for(let i = 0; i < answer.length; i++) {
    answer[i] = new Array(arr2[0].length).fill(0);
  }

  // let count = 0;

  for(let i = 0; i < row1; i++) { // arr1의 세로만큼 반복 : answer의 세로, arr1의 세로 인덱스 제어
    for(let j = 0; j < col2; j++) { // arr2의 가로만큼 반복 : answer의 가로, arr2의 가로 인덱스 제어
      for(let k = 0; k < col1; k++) { // arr1의 가로만큼 반복 : arr1의 가로, arr2의 세로 인덱스 제어
        answer[i][j] += arr1[i][k] * arr2[k][j];
        // console.log(`arr1[${i}][${k}] : ${arr1[i][k]}`);
        // console.log(`arr2[${k}][${j}] : ${arr2[k][j]}`);
        // console.log(`answer[${i}][${j}] : ${answer[i][j]}`);
        // count += 1;
      }

      // console.log();
    }
    
    // console.log();
  }

  // console.log("전체 계산 횟수", count);

  return answer;
}

다른 사람의 풀이 및 해석

function solution(arr1, arr2) {
  return arr1.map((row) => // arr1의 세로 반복
    arr2[0].map((x, y) => // arr2의 가로 반복
      // reduce(콜백 함수, 초기값)
      row.reduce(
        (a, b, c) => {
          return a + b * arr2[c][y];
        }, 0)
        // 콜백으로 arr1의 가로 요소 반복
          // a : 누적 값
          // b : arr1의 가로 요소
          // c : arr2 세로 인덱스 제어
          // => b * arr2[c][y] : 계산한 값
    )
  );
}


3. 결과


테스트 1통과 (3.37ms, 33.3MB)
테스트 2통과 (6.23ms, 34.4MB)
테스트 3통과 (7.45ms, 34.8MB)
테스트 4통과 (3.30ms, 33.1MB)
테스트 5통과 (6.61ms, 34.6MB)
테스트 6통과 (7.50ms, 34.3MB)
테스트 7통과 (1.67ms, 30.5MB)
테스트 8통과 (0.44ms, 30.7MB)
테스트 9통과 (0.37ms, 30.6MB)
테스트 10통과 (5.81ms, 34.1MB)
테스트 11통과 (3.44ms, 33.1MB)
테스트 12통과 (0.47ms, 30.6MB)
테스트 13통과 (7.29ms, 34.2MB)
테스트 14통과 (7.61ms, 34.5MB)
테스트 15통과 (4.39ms, 32.9MB)
테스트 16통과 (5.48ms, 33.6MB)

- 정확성 : 100.0

- 합계 : 100.0 / 100.0



4. 참고


profile
예비 프론트엔드 개발자입니다! 피드백 대환영!! 질문 대환영!!

0개의 댓글