본문 바로가기

[TIL][내일배움캠프]

[내일배움캠프][TIL] 23.12.07 (목) - 피보나치 수, 키오스크 프로그램 (Lv. 1 / Lv. 2)

1. 코드카타

 

오늘 푼 알고리즘 문제는 '피보나치 수'이다.

 

 피보나치 수
ex) n = 3 -> return = 2 / n = 5 -> return = 5

 

오랜만에 문자열에서 벗어난, 완전히 수학적인 문제 되시겠다.

피보나치 수는 너무나도 유명해서 한 번은 나오지 않을까 싶었는데 이번에 드디어 나왔다.

 

 

피보나치 수를 한 번 만들어보자.

class Solution {
    fun solution(n: Int): Int {
        var answer = 0
        var num = mutableListOf(0, 1)
        
        for (i in 2..n) {
            num.add(num[i-1]+num[i-2])
        }
        
        answer = num[n]%1234567
        
        return answer
    }
}

 

원래 유명하기도 하거니와, 이게 딱히 복잡한 식을 요구하지는 않아서 식을 적는 데에는 딱히 큰 어려움이 없었다.

여기서 이번에 특별히 List를 사용했는데 적어도 이 문제에서는 Array를 사용해도 딱히 큰 차이는 없다. 단지 List가 Array와는 다른 문법을 요구하기 때문에 익숙해질 겸 썼을 뿐.

 

하지만, 이 문제에는 함정이 하나 있었다.

 

저대로 제출하면, 특정 케이스에서 오답이 나온다.

 

시간 초과도 아니다.

오답이다.

 

저게 만약 틀린 식이었으면 일부 케이스에서만 오답이 나오는 게 아니라 실행 자체가 되지 않거나 테스트 케이스 때부터 오답이 나왔어야 했다.

 

그러니까 일부 케이스에서 오답이 나왔다는 것은, 바꿔 말해서 '어느 지점에서 허용 수치를 넘은 수가 나왔다'는 것.

즉, 오버플로(Overflow)가 발생했다.

 

이를 해결하기 위해서, 다음의 성질을 사용하기로 했다.

 

F(n)%d = F(n-1)%d + F(n-2)%d

            = { F(n-1) + F(n-2) } % d

 

즉, 어떤 수의 나머지는 각각의 수의 나머지를 더한 값을 나눈 나머지와 같다.

 

class Solution {
    fun solution(n: Int): Int {
        var answer = 0
        var num = mutableListOf(0, 1)
        
        for (i in 2..n) {
            num.add((num[i-1]%1234567)+(num[i-2]%1234567))
        }
        
        answer = num[n]%1234567
        
        return answer
    }
}

 

이렇게 하면 아무리 값이 커져도 1234567 위로는 도달하지 못하기 때문에 오버플로가 발생할 일이 없고, 우리가 구하고자 하는 답 또한 구할 수 있게 된다.

 

 

 

2. 키오스크 프로그램 (Lv. 1 / Lv. 2)

 

이번 주 과제는 키오스크 프로그램 만들기이다.

자신이 원하는 카페 혹은 음식점의 메뉴를 Kotlin으로 구현하면 된다.

 

 

Lv. 1에서 구현해야 할 기능은 다음과 같다.

  • 입력받은 숫자에 따라 다른 로직을 실행하는 코드를 작성
  • if나 when을 활용
  • 반복문을 이용해서 특정 번호가 입력되면 프로그램을 종료

나는 평소에 버거킹을 즐겨먹어서 버거킹으로 정하고, 키오스크의 기본적인 틀을 만들어봤다.

 

fun main() {
    println ("어서오세요 고객님.")
    println ("아래의 메뉴 중에서 원하는 것을 고르세요.")
    println ("")

    mainMenu()

    while (true) {
        var selectNumber = readLine()!!.toInt()

        when (selectNumber) {
            1 -> primiumWapper()
            2 -> wapper()
            3 -> sideMenu()
            4 -> drink()
            9 -> pay()
            0 -> break
            else -> println("유효하지 않은 번호입니다.")
        }
    }

    println ("프로그램을 종료합니다.")
}

fun mainMenu() {
    println ("[ 메인 메뉴판 ]")
    println ("1. 프리미엄 와퍼")
    println ("2. 와퍼")
    println ("3. 사이드메뉴")
    println ("4. 음료")
    println ("9. 결제")
    println ("0. 종료")
}

fun primiumWapper() {
    println ("[ 프리미엄 와퍼 ]")
    println ("1. 콰트로치즈와퍼 | ₩7,900")
    println ("2. 통새우와퍼    | ₩7,900")
    println ("3. 몬스터와퍼    | ₩9,300")
    println ("4. 스태커4 와퍼  | ₩13,300")
    println ("0. 뒤로 가기")

    var selectMenu = readLine()!!.toInt()

    if (selectMenu == 0) mainMenu()
}

fun wapper() {
    println ("[ 와퍼 ]")
    println ("1. 와퍼         | ₩7,100")
    println ("2. 치즈와퍼      | ₩7,700")
    println ("3. 불고기와퍼    | ₩7,400")
    println ("4. 와퍼주니어    | ₩4,700")
    println ("0. 뒤로 가기")

    var selectMenu = readLine()!!.toInt()

    if (selectMenu == 0) mainMenu()
}

fun sideMenu() {
    println ("[ 사이드메뉴 ]")
    println ("1. 감자튀김     | ₩2,100")
    println ("2. 너겟킹       | ₩2,200")
    println ("3. 코울슬로     | ₩2,100")
    println ("4. 바삭킹       | ₩3,000")
    println ("0. 뒤로 가기")

    var selectMenu = readLine()!!.toInt()

    if (selectMenu == 0) mainMenu()
}

fun drink() {
    println ("[ 음료 ]")
    println ("1. 콜라         | ₩2,000")
    println ("2. 스프라이트    | ₩2,000")
    println ("3. 오렌지쥬스    | ₩2,800")
    println ("4. 아메리카노    | ₩1,500")
    println ("0. 뒤로 가기")

    var selectMenu = readLine()!!.toInt()

    if (selectMenu == 0) mainMenu()
}

fun pay() {
    println ("결제수단을 선택하세요.")
    println ("1. 현금")
    println ("2. 카드")
    println ("0. 뒤로 가기")

    var selectMenu = readLine()!!.toInt()

    if (selectMenu == 0) mainMenu()
}

 

아직은 와퍼, 사이드메뉴, 음료를 선택했을 때 상호작용은 구현하지 않고 숫자를 입력했을 때 해당 화면으로 넘어가는 것까지만 구현시켰다.

 

그런데 적다보니 엄청 기네.

아직 클래스로 구현 안 해서 그런가.

 

 

그 다음 Lv. 2.

  • 필요한 클래스들을 설계 (버거, 아이스크림, 음료, 맥주, 주문, 공통 등)
  • 클래스들의 프로퍼티와 메소드 정의
  • 메소드를 이용해서 Lv1의 코드 개선

벌써부터 클래스 설계가 들어갔다. 아직 상속하라는 말은 없으니 어디까지 클래스를 잡아서 구현할지가 관건이다.

 

fun main() {
    println ("어서오세요 고객님.")
    println ("아래의 메뉴 중에서 원하는 것을 고르세요.")
    println ("")

    var menu = List()

    menu.menuList()

    while (true) {
        var name: String = ""
        var price: Int = 0
        var selectNumber = readLine()!!.toInt()

        when (selectNumber) {
            1 -> primiumWapper(menu, name, price)
            2 -> wapper(menu, name, price)
            3 -> sideMenu(menu, name, price)
            4 -> drink(menu, name, price)
            9 -> pay(menu)
            0 -> break
            else -> println("유효하지 않은 번호입니다.")
        }
    }

    println ("프로그램을 종료합니다.")
}


fun primiumWapper(menu: List, name: String, price: Int) {
    menu.primiumList()

    val myMenu = PrimiumWapper(name, price)
    val selectMenu = readLine()!!.toInt()

    when(selectMenu){
        1 -> myMenu.displayInfo("콰트로치즈와퍼", 7900)
        2 -> myMenu.displayInfo("통새우와퍼", 7900)
        3 -> myMenu.displayInfo("몬스터와퍼", 9300)
        4 -> myMenu.displayInfo("스태커4 와퍼", 13300)
        0 -> menu.menuList()
        else -> println("유효하지 않은 번호입니다.")
    }
}

fun wapper(menu: List, name: String, price: Int) {

    menu.wapperList()

    val myMenu = Wapper(name, price)
    val selectMenu = readLine()!!.toInt()

    when(selectMenu){
        1 -> myMenu.displayInfo("와퍼", 7100)
        2 -> myMenu.displayInfo("치즈와퍼", 7700)
        3 -> myMenu.displayInfo("불고기와퍼", 7400)
        4 -> myMenu.displayInfo("와퍼주니어", 4700)
        0 -> menu.menuList()
        else -> println("유효하지 않은 번호입니다.")
    }
}

fun sideMenu(menu: List, name: String, price: Int) {

    menu.sideList()

    val myMenu = SideMenu(name, price)
    val selectMenu = readLine()!!.toInt()

    when(selectMenu){
        1 -> myMenu.displayInfo("감자튀김", 2100)
        2 -> myMenu.displayInfo("너겟킹", 2200)
        3 -> myMenu.displayInfo("코울슬로", 2100)
        4 -> myMenu.displayInfo("바삭킹", 3000)
        0 -> menu.menuList()
        else -> println("유효하지 않은 번호입니다.")
    }
}

fun drink(menu: List, name: String, price: Int) {

    menu.drinkList()

    val myMenu = Drink(name, price)
    val selectMenu = readLine()!!.toInt()

    when(selectMenu){
        1 -> myMenu.displayInfo("콜라", 2000)
        2 -> myMenu.displayInfo("스프라이트", 2000)
        3 -> myMenu.displayInfo("오렌지쥬스", 2800)
        4 -> myMenu.displayInfo("아메리카노", 1500)
        0 -> menu.menuList()
        else -> println("유효하지 않은 번호입니다.")
    }
}

fun pay(menu: List) {
    println ("결제수단을 선택하세요.")
    println ("1. 현금")
    println ("2. 카드")
    println ("0. 뒤로 가기")

    val selectMenu = readLine()!!.toInt()

    if (selectMenu == 0) menu.menuList()
}
class List {
    fun menuList() {
        println ("[ 메인 메뉴판 ]")
        println ("1. 프리미엄 와퍼")
        println ("2. 와퍼")
        println ("3. 사이드메뉴")
        println ("4. 음료")
        println ("9. 결제")
        println ("0. 종료")
    }

    fun primiumList() {
        println ("[ 프리미엄 와퍼 ]")
        println ("1. 콰트로치즈와퍼 | ₩7,900")
        println ("2. 통새우와퍼    | ₩7,900")
        println ("3. 몬스터와퍼    | ₩9,300")
        println ("4. 스태커4 와퍼  | ₩13,300")
        println ("0. 뒤로 가기")
    }

    fun wapperList() {
        println ("[ 와퍼 ]")
        println ("1. 와퍼         | ₩7,100")
        println ("2. 치즈와퍼      | ₩7,700")
        println ("3. 불고기와퍼    | ₩7,400")
        println ("4. 와퍼주니어    | ₩4,700")
        println ("0. 뒤로 가기")
    }

    fun sideList() {
        println("[ 사이드메뉴 ]")
        println("1. 감자튀김     | ₩2,100")
        println("2. 너겟킹       | ₩2,200")
        println("3. 코울슬로     | ₩2,100")
        println("4. 바삭킹       | ₩3,000")
        println("0. 뒤로 가기")
    }

    fun drinkList() {
        println ("[ 음료 ]")
        println ("1. 콜라         | ₩2,000")
        println ("2. 스프라이트    | ₩2,000")
        println ("3. 오렌지쥬스    | ₩2,800")
        println ("4. 아메리카노    | ₩1,500")
        println ("0. 뒤로 가기")
    }
}
class PrimiumWapper (name: String, price: Int) {
    fun displayInfo(name: String, price: Int) {
        println ("${name} 주문하였습니다. 이 제품의 가격은 ${price} 원 입니다.")
    }
}

class Wapper (name: String, price: Int) {
    fun displayInfo(name: String, price: Int) {
        println ("${name} 주문하였습니다. 이 제품의 가격은 ${price} 원 입니다.")
    }
}

class SideMenu (name: String, price: Int) {
    fun displayInfo(name: String, price: Int) {
        println ("${name} 주문하였습니다. 이 제품의 가격은 ${price} 원 입니다.")
    }
}

class Drink(name: String, price: Int) {
    fun displayInfo(name: String, price: Int) {
        println ("${name} 주문하였습니다. 이 제품의 가격은 ${price} 원 입니다.")
    }
}

일단 리스트 / 프리미엄와퍼 / 와퍼 / 사이드메뉴 / 음료 이런 식으로 크게크게 클래스를 분류했고, 각 카테고리 안에 있는 메뉴들은 값을 직접 주는 식으로 설계했다.

 

…음. 이게 맞나?

작동은 잘 되는데 어째 아까보다 줄이 더 길어진 느낌이다.

 

물론 기능을 추가했으니 당연한 이야기겠지만, 각 클래스마다 쓸데없이 중복되는 메소드를 넣을 필요가 없어보이는데 말이다.

여러모로 개선해야할 점이 눈에 보인다.

 

참고로 원래는 각 메뉴마다 클래스를 만들려고 했는데 생각하다보니 이건 이거대로 클래스가 너무 많아지는 느낌이라서 보류했다.

 

 

그걸 이제 Lv. 3에서 구현해야지.

  • Lv2에서 설계한 클래스들을 상속 관계를 가지도록 변경
  • 하나의 리스트객체로 모든 메뉴들을 관리하도록 수정 (List)

이제 상속으로 하위 메뉴들을 자식 클래스로 만들어서 값을 받게 할 수 있는지 만들어봐야겠다.