본문 바로가기
♞ | 공부일지

[Android] 공부일지(2024-06-06)

by immgga 2024. 6. 7.
오늘 공부한 내용 정리(2024년 6월 6일)

 

하늘로 계속 올라가는 열기구처럼 꾸준히 정진해 삶의 목표를 위해 나아가는 사람이 되자.

 

1. 알고리즘 문제풀이

A. 듣보잡(백준, S4, 1764번)

문제 내용

 

문제 풀이 방법

  • 첫째 줄에 듣도 못한 사람의 수가 주어지고, 보도 못한 사람의 수가 주어질 때, 둘째 줄부터 듣도 못함 사람 이름부터 보도 못한 사람 이름 순서대로 입력된다.(듣도 못한 사람이 3이고, 보도 못한 사람이 4일 때 2번째 줄부터 7줄에 걸쳐서 입력이 주어짐)
  • 입력받은 사람들 중 듣보잡의 수를 출력(듣도 못한 사람과 보도 못한 사람에 모두 포함되는 사람)

 

해결 코드(스포 주의)

더보기
import java.io.BufferedReader
import java.io.BufferedWriter
import java.io.InputStreamReader
import java.io.OutputStreamWriter
import java.util.*

fun main() = with(BufferedReader(InputStreamReader(System.`in`))) {
    val people = readLine().split(" ").map { it.toInt() }

    val noListen = mutableSetOf<String>()
    for (i in 0 until people.first()) {
        noListen.add(readLine())
    }

    val noSeen = mutableSetOf<String>()
    for (i in 0 until people.last()) {
        noSeen.add(readLine())
    }

    val bw = BufferedWriter(OutputStreamWriter(System.out))
    val noListenSeen = noListen.intersect(noSeen).toTypedArray()
    Arrays.sort(noListenSeen)
    bw.write("${noListenSeen.size}\n")
    for (i in noListenSeen.indices) {
        bw.write("${noListenSeen[i]}\n")
    }

    bw.flush()
    bw.close()
}

 

풀이 과정

  • people로 첫째 줄의 입력을 처리하고, for문 2개를 이용해 연속된 입력을 처리(noListen, noSeen) 해 주었다.
  • noListen, noSeen은 MutableSet 타입이고, noListen과 noSeen의 교집합(중복되는 데이터만) 연산을 수행한다.
  • 그러면 중복되는 데이터들만 남게 되는데, 이 데이터들의 size와 데이터를 반복을 돌려서 출력.

 

문제 해결 과정

  • 문제 해결에 어려움은 없었다. 이전에 한 번 접했던 교집합 함수를 사용하면 매우 쉽게 해결할 수 있는 문제. 

 

B. 조합(백준, S3, 2407번)

문제 내용

 

문제 풀이 방법

  • n과 m이 주어질 때, nCm(집합)을 수행해 출력.
    집합 공식은 n! / (n-m)! * r!이다(! 는 팩토리얼을 뜻함)

 

해결 코드(스포 주의)

더보기
import java.util.*

fun main() = with(Scanner(System.`in`)) {
    val n = nextInt()
    val m = nextInt()

    var factorialN = 1L.toBigInteger()
    for (i in n-m+1 .. n) {
        factorialN *= i.toBigInteger()
    }

    var factorialM = 1L.toBigInteger()
    for (i in 1 .. m) {
        factorialM *= i.toBigInteger()
    }

    println(factorialN / factorialM)
}

 

풀이 과정

  • n과 m을 입력받고, m+1부터 n까지의 팩토리얼을 수행한다.
    n이 6이고 m이 2일 때, 집합 공식을 이용하면 6! / 4! * 2! 이 되게 되는데, 약분으로 4! 을 지울 수 있다. 4! 을 지우고 나면
    6 * 5 / 2 * 1이 남게 된다. 지금 현재 우리는 분자에 해당하는 데이터를 구하고 있기 때문에 6-2+1=5부터 6까지의 팩토리얼을 구하면 되기 때문에 분자 데이터는 30이다.
  • 분모 부분은 factorialM으로 처리한다. 이제 분모에는 m! 밖에 남지 않았기 때문에 평범하게 1부터 m까지 for를 돌려서 분모를 구하면 된다. 분모 데이터는 2
  • 집합 공식에 따라 factorialN과 factorialM을 나눠준다. 결과: 15 

 

문제 해결 과정

  • 최대 숫자 제한이 100이지만, 팩토리얼을 해야 하기 때문에 숫자가 커질 경우에는 Long보다 커지는 경우도 있다.
  • 이럴 때는 BigInteger 자료형을 사용한다. BigInteger는 Long과 달리 제한이 없이 무한이기 때문에 Long보다 큰 수를 다뤄야 할 때 적합하다.
  • BigInteger 사용법을 익히고 적용해 쉽게 해결했다.

 

2. 과제 수행

A. 과제 내용

입력받은 연산식이 올바르지 않았을 때의 처리.

  • 연산식을 입력받을 때 "3+4/"와 같은 연산 기호로 끝나는 경우에는 올바른 계산을 할 수 없으므로 예외처리 print를 출력하고 다시 입력받도록 코드를 변경했다.
    연산 기호로 끝나는 경우 splitFormula가 [3, 4, ]로 받아지므로 splitFormula 중 데이터가 하나라도 비어 있는 경우 예외 처리 print를 출력하도록 했다.
. . .
    // 정규 표현식을 이용한 수식 분할(여러 개의 문자를 기준으로 분할 가능함).
    val splitFormula = formula.split("+", "-", "*", "/")
    val splitFormulaSymbol = formula.filter { it in setOf('+', '-', '*', '/') }.map { it }
    // splitFormula에서 숫자만 포함되도록 하기(정규 표현식 사용).
    if (!splitFormula.all { it.matches(Regex("\\d*")) })
        println("연산식이 문자를 포함하고 있습니다. 다시 시도해 주세요.")
    else if (splitFormula.any { it.isEmpty() })
        // splitFormula의 데이터 중 빈값이 존재함(연산식이 올바르게 끝나지 않음.)
        println("연산식이 올바르게 끝나지 않았습니다. 다시 시도해 주세요.")
    else {
        calculation(
            splitFormula = splitFormula,
            splitFormulaSymbol = splitFormulaSymbol
        )
    }
. . .

 

 

사칙연산 우선순위 부여

  • 사칙 연산을 할 때는 곱하기와 나누기가 우선적으로 실행되어야 하기 때문에 이를 고려한 로직을 짜야한다.
  • 곱하기, 나누기 기호가 있는 경우 반복문을 돌려서 곱하기, 나누기 연산을 따로 해주고 연산에 사용한 연산 기호와 숫자를 제거하고, 연산 결과를 그대로 넣어주는 작업을 해주었다.
    3+4*2를 splitFormula로 나타내면 [3, 4, 2]이고, splitFormulaSymbol로 나타내면 [+, *]가 된다.
    위의 리스트에서 곱하기를 먼저 수행해야 하므로 *기호의 위치를 불러온 다음(index: 1) 해당 위치에 해당하는 splitFormula의 값과 위치의 +1 한 값을 연산해 준다. 그리고 리스트에서 사용한 데이터들을 지워준다.
    [3, 4, 2] => [3], [+, *] => [+]
    여기서 연산 결과를 splitFormula의 *기호 위치의 index에 넣어주면 된다.
    [3] => [3, 8], [+]
    위의 리스트를 바탕으로 더하기, 빼기를 하면 사칙 연산이 완료된다.
private fun calculation(splitFormula: List<String>, splitFormulaSymbol: List<Char>) {
    // 수식을 입력받아 계산하기.
    val calculator = Calculator()
    val mutableSplitFormula = splitFormula.map { it.toInt() }.toMutableList()
    val mutableSplitFormulaSymbol = splitFormulaSymbol.map { it.toString() }.toMutableList()

    println(mutableSplitFormula)
    println(mutableSplitFormulaSymbol)

    // todo :: 계산 우선순위 부여해서 계산하도록 구현(*, /)
    // 곱하기, 나누기를 우선으로 계산하기
    // 먼저 곱하기, 나누기가 존재하는지 indexOf로 확인해야 함(초기값 -2).
    var multiplyIndex = -2
    var divideIndex = -2
    // 곱하기, 나누기 기호가 없어질 때까지 반복.
    while (multiplyIndex != -1 || divideIndex != -1) {
        // 반복마다 곱하기, 나누기의 위치를 구함.
        multiplyIndex = mutableSplitFormulaSymbol.indexOf("*")
        divideIndex = mutableSplitFormulaSymbol.indexOf("/")

        if (multiplyIndex < divideIndex) {
            // 나누기가 더 먼저 있음
            val divideResult = calculator.divide(mutableSplitFormula[divideIndex], mutableSplitFormula[divideIndex+1])

            // 계산에 사용한 숫자 2개를 지운다.
            mutableSplitFormula.removeAt(divideIndex)
            mutableSplitFormula.removeAt(divideIndex)
            // 계산에 사용한 나누기 기호를 지운다.
            mutableSplitFormulaSymbol.removeAt(divideIndex)
            // 나누기 계산 결과를 추가한다.
            mutableSplitFormula.add(divideIndex, divideResult)
        } else if (multiplyIndex > divideIndex) {
            // 곱하기가 더 먼저 있음
            val multiplyResult = calculator.multiply(mutableSplitFormula[multiplyIndex], mutableSplitFormula[multiplyIndex+1])

            mutableSplitFormula.removeAt(multiplyIndex)
            mutableSplitFormula.removeAt(multiplyIndex)
            mutableSplitFormulaSymbol.removeAt(multiplyIndex)
            mutableSplitFormula.add(multiplyIndex, multiplyResult)
        }
    }

    var calculateResult = mutableSplitFormula[0]
    mutableSplitFormula.removeAt(0)

    while (mutableSplitFormula.size > 0) {
        when (mutableSplitFormulaSymbol[0]) {
            "+" -> calculateResult = calculator.add(calculateResult, mutableSplitFormula[0])
            "-" -> calculateResult = calculator.minus(calculateResult, mutableSplitFormula[0])
        }

        mutableSplitFormula.removeAt(0)
        mutableSplitFormulaSymbol.removeAt(0)

    }

    println("계산 결과: $calculateResult")
}

 


 

오늘 공부 내용 정리 및 회고

  • 콘솔 계산기 과제는 lv.4까지 완료하고 추가 기능을 구현할까 생각 중이다.
    아직 괄호에 따른 우선순위 부여를 하지 않았다.
  • Long보다 더 큰 수를 다룰 때 사용할 수 있는 BigInteger를 사용해 보았다. 내일은 백준에서 BigInteger를 사용할 수 있는 문제를 풀어볼 것이다.
  • 코틀린 강의는 어제 4주 차까지 들었다. 5주 차를 수강할지 아니면 복습을 하면서 알고리즘 공부를 할지 고민해 봐야겠다.
728x90

댓글