Kotlin 사전캠프 TIL 3일차

노재원·2024년 3월 21일
0

내일배움캠프

목록 보기
3/90

문득 새삼 깨닫는 자동완성의 위대함

프로그래머스가 자동완성을 지원하지 않는건 알고 있었지만 기왕 하는거 VSCode는 옆에 치워두고 웹 상에서만 제출을 감행하고 있다. 대단한 이유가 있는건 아니고 자동완성이 나쁜 기능인 것도 아니지만 재활치료중이니 대충 .찍고 싹 훑어보기보단 한 번 고생을 하자는 취지가 있었다.

그렇게 부딪혀보니 많이 불편하고 뻔한 길을 멀리 돌아갈 때도 있었다. 어차피 거쳐가는 길인데 내일부터는 기술의 힘을 빌리는게 낫지 않을까 생각이 든다.


정수 제곱근 판별

임의의 양의 정수 n에 대해, n이 어떤 양의 정수 x의 제곱인지 아닌지 판단하려 합니다.
n이 양의 정수 x의 제곱이라면 x+1의 제곱을 리턴하고, n이 양의 정수 x의 제곱이 아니라면 -1을 리턴하는 함수를 완성하세요.

문제 링크

fun solution(n: Long): Long {
      var answer: Long = 0
      var sqrtDouble: Double = Math.sqrt(n.toDouble())
      
      // toInt()는 Exception을 만들지 않고 소수점을 버림
      if (sqrtDouble.toInt().toDouble() == sqrtDouble)  {
          answer = Math.pow(sqrtDouble + 1.0, 2.0).toLong()
      } else {
          answer = -1
      }
      
      return answer
  }

제출은 금방 끝났지만 더 나은 해답이 없을까 좀 고민해본 시간이 은근 길었다.
toInt() 로 소수점을 버린 정수 제곱근과 실제 제곱근을 비교할 뿐이다.

마음에 걸리는건 조건문의 캐스팅을 두 번 거치는 부분인데 번뜩이는 아이디어도 없고 테스트 케이스에 따라 Exception을 발생시키는 것도 아니라서 문제의 의도를 잘 이해했다고 생각하고 넘어갔는데 내심 아쉬움이 남긴 한다.


정수 내림차순으로 배치하기

함수 solution은 정수 n을 매개변수로 입력받습니다. n의 각 자릿수를 큰것부터 작은 순으로 정렬한 새로운 정수를 리턴해주세요. 예를들어 n이 118372면 873211을 리턴하면 됩니다.

문제 링크

fun solution(n: Long): Long {
        var answer: Long = 0
        var list: MutableList<Int> = mutableListOf()
        var number = n

        while (number > 0) {
            list.add((number % 10L).toInt())
            number /= 10L
        }
        
        answer = list.sortedDescending().joinToString("").toLong()
        
        return answer
    }

배열을 많이 배워가는 챕터인 건지 자릿수를 이용한 문제도 꽤 많이 만났다. 이 문제를 첫 제출한 후에 다시 리뷰를 해보니 이전에 많이 써먹던 자릿수 구하기를 관성적으로 이용했다는게 눈에 띄었는데 문제를 보면 굳이 자릿수간 연산을 할 것도 아니니 더 줄여보기로 했다.

fun solution(n: Long): Long {
        var answer: Long = 0
        var number = n
        
        // 간략하게 줄인 답안
        answer = number.toString().map { it.toString().toInt() }
            .sortedDescending()
            .joinToString("")
            .toLong()
        
        return answer
    }

역시 짧고 간결하니 마음이 편해졌다.
String map() 에서 나오는 Char을 조금 더 똑똑하게 Int로 바꿀 여지가 있는지 봤지만 Char에 바로 toInt() 를 먹이면 ASCII 값으로 튀어나오는 것 같으니 여러 케이스를 대비했다 치고 이것도 캐스팅을 두 번 적용했다.


콜라츠 추측

1937년 Collatz란 사람에 의해 제기된 이 추측은, 주어진 수가 1이 될 때까지 다음 작업을 반복하면, 모든 수를 1로 만들 수 있다는 추측입니다. 작업은 다음과 같습니다.

1-1. 입력된 수가 짝수라면 2로 나눕니다. 
1-2. 입력된 수가 홀수라면 3을 곱하고 1을 더합니다. 
2. 결과로 나온 수에 같은 작업을 1이 될 때까지 반복합니다. 

예를 들어, 주어진 수가 6이라면 6 → 3 → 10 → 5 → 16 → 8 → 4 → 2 → 1 이 되어 총 8번 만에 1이 됩니다. 위 작업을 몇 번이나 반복해야 하는지 반환하는 함수, solution을 완성해 주세요. 단, 주어진 수가 1인 경우에는 0을, 작업을 500번 반복할 때까지 1이 되지 않는다면 –1을 반환해 주세요.

문제 링크

fun solution(num: Int): Int {
        var answer = num
        var count = 0
        
        if (num == 1) { return 0 }
        
        while (answer != 1 && count < 501) {
            if (answer % 2 == 1) {
                answer *= 3
                answer += 1
            } else { 
                answer /= 2   
            }
            
            count++ 
        }
        
        if (count == 501) { return -1 }
        
        return count
    }

문제를 보자마자 재귀가 떠오르긴 했지만 제일 뻔하고 간단한 방법부터 시도했다.
다행히도 첫 제출에서 퍼포먼스도 나쁘진 않았지만 코드가 너무 불필요하게 긴 건 느껴졌다.

실무에서 재귀로 연산을 딱 한 번 호기심에 처리해본 적이 있는데 Log를 너무 많이 찍었던 기억이 있어서 부족한 머리 능력으로는 가급적 시도 안해보는게 좋다고 느끼고 잘 건드린 적이 없다.
그래서 추가로 두뇌 자극을 할 겸 처음으로 다른 사람들의 풀이를 훑어봤는데 그 중 가장 인상깊은 해설이 있어서 추가로 가져왔다.

fun solution(num: Int): Int = collatzAlgorithm(num.toLong(),0)

    tailrec fun collatzAlgorithm(n:Long, c:Int):Int =
        when{
            c > 500 -> -1
            n == 1L -> c
            else -> collatzAlgorithm(if( n%2 == 0L ) n/2 else (n*3)+1, c+1)
        }

tailrec 은 진짜 처음 보는데 꼬리재귀(tail recursive) 라고 한다.
재귀함수 중에서도 자기 자신만을 호출하고 추가적인 연산이 없을 때 사용하면 컴파일러가 최적화 하는데에 도움이 되고 실제로 자바 코드로 변환된 함수는 루프문을 사용하고 있으며 스택 오버플로우 방지에도 효과가 있다고 한다.

콜라츠 추측의 정석적인 코드가 아닐까 싶을 정도로 똑똑하고 간결하게 짜여져 있어서 보기만 해도 기분이 좋아지는 느낌이다.

when 의 조건은 반복 횟수가 > 500 인지, 숫자가 1인지, 그 외의 경우는 재귀 호출을 진행하는데 복잡한 연산도 없고 조건도 까다롭지 않아 머릿속에서 쉽게 돌아가지 않는 재귀함수와 달리 쉽게 판독 가능하다.

이 문제를 보고 깨달은 건데 실컷 써봐놓고 여태 까먹어서 when 을 사용 안하고 있었다. 다른 알고리즘 문제에서는 써먹을 여지가 보이는 구석에 잘 써먹어야겠다.

1개의 댓글

comment-user-thumbnail
2024년 3월 25일

코틀린에 관한 문법 정리 강의를 지급해드리지 않았지만, 짬바가 어디 안 간다는 게 느껴지는 글이었습니다 하핳

답글 달기