카테고리 없음

2024-05-23 [Programmers Kotlin 코틀린 행렬의 덧셈 (feat. Kotlin의 Array)]

Glen_check 2024. 5. 23. 23:48

* 문제를 다루기 전 먼저 배열의 사용 방법을 한 번 더 정리하고 가려합니다.

 

Kotlin 배열

var array = arrayOf(1,2,3) // 특정 값을 넣어서 배열을 생성 
var array2 = Array(10,{0}) // 크기만 정해서 배열을 생성

 

배열 생성 원리

arrayOf()는 Array타입의 배열 객체를 반환하는 형태

public inline fun <reified @PureReifiable T> arrayOf(vararg elements: T): Array<T>

Array 를 이용하여 생성한 배열은 Array Class를 이용해서 생성

public class Array<T> {
    public inline constructor(size: Int, init: (Int) -> T)
    ...
}

 

'arrayOf()' 는 특정 타입을 지정하지 않은 상태로 배열을 생성하면 어떤 값이 들어가도 배열을 생성할 수 있습니다.

하나의 배열 내 다음과 같이 다양한 타입의 데이터를 넣을 수 있습니다.

var anyTypeArray = arrayOf(1,"A","안녕하세요",true, 100.13)

특정 타입을 지정하는 경우 제네릭을 사용해서 표현하거,나 코틀린에서 제공하는 기본타입의 배열 함수를 이용할 수 있습니다.

var arrayInt = arrayOf<Int>(10, 20, 30, 40, 1, 2, 3, 4)
var arrayString = arrayOf<String>("하나", "둘", "셋")

 

문제를 풀기 전, Array 의 생성 방법을 예시와 함께 다시 한 번 짚고 넘어가려 합니다.

// 생성방법

var array = Array(사이즈,{들어갈 값})
OR
var array2 = Array(사이즈){들어갈 값}

// 예시

var arr = Array(10,{0}) // 사이즈는 10이고 각 자리에는 현재 빈 값이라 할 수 있는 0이 들어가 있습니다.
var array2 = Array(10,{i->i*5}) //사이즈는 10이고 각 자리에 인덱스*5의 값이 들어가 있습니다.
var array3 = Array(5,{""}) //사이즈는 5이고 각 자리에는 공백이 들어가 있습니다.

 

 

* Array(size) { index -> index }와 같이 람다식을 통해 생성할 수 있습니다.

 

CASE (행렬의 덧셈)

문제설명

  • 행렬의 덧셈은 행과 열의 크기가 같은 두 행렬의 같은 행, 같은 열의 값을 서로 더한 결과가 됩니다. 2개의 행렬 arr1과 arr2를 입력받아, 행렬 덧셈의 결과를 반환하는 함수, solution을 완성해주세요.

제한사항

  • 행렬 arr1, arr2의 행과 열의 길이는 500을 넘지 않습니다.

입출력 예

 

* 2차원 배열이다 보니, 실제 배열의 형태를 단번에 이해하기 어려워 다음과 같이 그림으로 풀어보았습니다.

흠, 너무 허접해서 이해를 돕고자 Chat GPT 친구에게 "행렬을 이해하기 쉽게 그려줘!" 부탁했습니다.

 

다음은 2x3 행렬의 예입니다.

A = | 1  2  3 |
    | 4  5  6 |

이 행렬 A는 다음과 같이 읽을 수 있습니다.

  • 첫 번째 행: 1, 2, 3
  • 두 번째 행: 4, 5, 6

각 요소는 다음과 같이 위치합니다.

  • = 1
  • = 2
  • = 3
  • = 4
  • = 5
  • = 6

예시의 각 arr1, arr2 를 변수로 표현하면 다음과 같습니다.

val arr1 = arrayOf(intArrayOf(1, 2), intArrayOf(2, 3))
val arr2 = arrayOf(intArrayOf(3, 4), intArrayOf(5, 6))

 

문제 풀이

class Solution {
    fun solution(arr1: Array<IntArray>, arr2: Array<IntArray>): Array<IntArray> {
        return Array(arr1.size) { row -> IntArray(arr1[0].size) { col -> arr1[row][col] + arr2[row][col] } }
    }
}

 

풀이를 뜯어보며 이해해보겠습니다.

 

1. 결국 우리는 문제 내 주어진 두 개의 매개 변수(IntArray 타입을 가진 Array)를 더한 새로운 배열을 만들어 내야 합니다.

2. 새롭게 만들 Array 의 사이즈는 arr1 사이즈와 동일하니 사이즈를 arr1.size 로 지정해줍니다. (사이즈 2를 가진 배열이 만들어집니다.)

3. 람다식을 활용하여 Index를 row(Array의 행 역할을 해주기 때문에)로 이름을 붙여주고, row에 들어올 값이 arr1, arr2 와 동일한 IntArray일테니 IntArray를 넣어줍니다.

4. IntArray도 동일하게 사이즈와 들어갈 값을 지정해줍니다. 사이즈는 arr1 배열의 값 중 하나의 IntArray 사이즈와 동일합니다.

5. IntArray의 인덱스는 행렬의 column 역할을 하며 각 열에 arr1[row][col] + arr2[row][col] 값을 넣어줍니다.

 

아래의 코드를 확인하니 이해가 더욱 수월했으니 참고 부탁드리겠습니다.

println(Array(2) {row -> Array(2) {col -> "$row $col"}}.contentDeepToString())
// 출력 값 : [[0 0, 0 1], [1 0, 1 1]]

val arr = Array(2){row -> IntArray(2){col -> row + col} }
println(arr.contentDeepToString())
// 출력 값 : [[0, 1], [1, 2]]

 

문제를 풀며 새롭게 알게된 내용

1. Array<IntArray> Vs Array<Array<Int>>

 

처음 인텔리제이로 해당 문제를 풀며, arr1 을 다음과 같이 지정해주는데 Type mismatch 오류가 발생하였습니다.

fun main() {
	val arr1 = arrayOf(arrayOf(1, 2), arrayOf(2, 3))
}

// Type mismatch. Required: Array<IntArray> Found: Array<Array<Int>>

class Solution {
    fun solution(arr1: Array<IntArray>, arr2: Array<IntArray>): Array<IntArray> {
    }
}

'intArrayOf 로 지정해주지 않아도, 배열 안 값이 Int Type 이니 동일한 타입 아닐까?' 생각하여 두 가지 타입에 대해 다시 한 번 공부해보았습니다.

두가지 모두 동일하게 Int Type 의 값을 저장해줄 수 있는 배열 타입이지만, 몇가지 중요한 차이가 있습니다.

 

IntArray

  • 기본 자료형 배열 : IntArray는 코틀린에서 제공하는 기본 자료형(원시타입) 배열 타입입니다.
  • 효율성: IntArray는 자바의 int[]와 대응되며, 값만 가져오기 때문에 더 효율적입니다. 이는 기본 자료형인 int를 사용하므로 박싱/언박싱이 발생하지 않기 때문입니다.
val intArray: IntArray = intArrayOf(1, 2, 3)

Array<Int>

  • 제네릭 배열 : Array<Int>는 코틀린의 제네릭 배열 타입으로, 자바의 Integer[]와 대응됩니다.
  • 유연성: 제네릭 타입을 사용하므로, 배열 요소에 대해 더 많은 타입을 지원합니다. 하지만, 기본 자료형 배열에 비해 박싱/언박싱이 필요하여 성능 면에서 조금 덜 효율적일 수 있습니다. (주소 값을 찾아가 값을 가져오는 경우)
val arrayOfInt: Array<Int> = arrayOf(1, 2, 3)

 

차이 요약

  1. 성능: IntArray는 기본 자료형을 사용하기 때문에 성능 면에서 더 효율적입니다. 반면 Array<Int>는 박싱된 정수 객체를 사용합니다.
  2. 타입: IntArray는 기본 자료형 배열이므로 요소가 int 타입입니다. Array<Int>는 제네릭 배열이므로 요소가 Int 객체입니다.
  3. 사용 사례: 일반적으로 성능이 중요한 경우 IntArray를 사용하고, 제네릭의 유연성이 필요한 경우 Array<Int>를 사용합니다.

결론

  • 성능과 메모리 효율성을 중시한다면 IntArray를 사용합니다.
  • 제네릭의 유연성이 필요하거나 여러 타입을 지원하는 배열을 원한다면 Array<Int>를 사용합니다.

자바를 잘 모르는 나를 위해 .. (int와 Integer의 차이)

 

int는 primitive type입니다. 클래스가 아닙니다.

Integer는 클래스입니다. int형의 담당 일진 클래스이므로 int의 Wrapper 클래스라고 불립니다.

 

Integer 변수에 int를 저장하면 int형을 Integer형으로 변환하여서 저장합니다.

이 변환 과정을 boxing(박싱)이라고 합니다.

박싱을 할 때는 Integer.valueOf() 함수를 이용해서 int형을 Integer형으로 변환합니다.

 

즉, Array<Int>형으로 선언한 배열에 (굳이 Integer 형으로 넣는 게 아닌 이상) 숫자를 넣을 때마다 Integer.valueOf() 함수를 호출하는 비용이 발생합니다.