Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
문제
백준 1629번 곱셈
문제 설명
A를 B번 곱해 C로 나눈 나머지 값을 구하여라.
입력
출력
시간 제한
해답 1 - 정직한 반복
제일 먼저 했던 방식은 정말 순수하게 A를 B번 곱해주고, C로 나눈 나머지를 구하는 방법입니다.
입력 조건과 시간 제한에 따라 결과는 당연하게도 시간 초과
그렇다면 어떻게 수정을 해줘야할까요?
해답 2 - 일반 재귀(분할 정복)
코드를 구현에서는 제일 먼저 multiplication이라는 함수를 만들어주었습니다.
multiplication에서는 b의 절반을 나누어 재귀반복을 해주었습니다.
다음은 재귀한 결과 값을 이제 C로 나머지를 구해 반환해줍니다.
이때 b가 홀수였다면 A를 한번 더 곱해줍니다.
풀이 과정 1
B가 10이라고 할때, A^10은 A^5 * A^5와 같은 지수 법칙을 이용한 방법입니다.
예시 : B = 10일때
B를 2로 나누어 주다가 홀수라면 A를 한번 더 곱해주는 과정을 거칩니다.
10에서 나누어지며 쭉 내려왔다가 A에 대한 값이 반환되며 결과적으로 A^B를 얻을 수 있습니다.
1과의 차이점은 수행시간으로 보면 큰 차이가 나는데,
기존 10번 반복해야하는 작업(A * A * A ... A)이 크게 3번의 연산작업(4->3->2->1)만 거치면 됩니다.
풀이 과정 2
나머지 연산에서는 곱한 후에 나눈 나머지는 각각 나눈 나머지를 곱한 후 다시 나눈 나머지와 같다.
(A*A) % C = ((A%C) * (A%C)) % C
예시: A = 9, B = 4일때
(9 * 9) % 4 = 1
((9%4) * (9%4)) % 4 = (1 * 1) % 4 = 1
A, B, C의 입력 조건을 생각해보았을 때, 연산과정에서 오버플로우가 발생할 확률이 높으므로 과정마다 C로 나누는 작업을 진행해 C 이하로 유지되도록 제한한다.
trailrec이란?
이 문제에서는 trailrec을 사용하지 않아도 괜찮지만, trailrec을 사용해보고 싶어 이번 주제에 재귀를 다루어 보게 되었습니다.
그냥 요런 것도 있더라 정도로만 알아두면 좋을 것 같아요.
trailrec의 컴파일 과정
코틀린에서 trailrec은 StackOverflowError가 발생하지 않도록 컴파일러 차원에서 반복문으로 최적화해주는 기능입니다.
즉, trailrec은 코드 작성 시에는 재귀로 작성을 했지만, 컴퓨터는 컴파일 과정에서 while 반복문으로 실행하도록 합니다.
위와 같이 함수 앞에 tailrec키워드를 붙여 사용할 수 있습니다.
tailrec을 사용할 경우 앞서 말했듯이 내부적으로 while 반복문으로 바꾸어 스택 메모리를 낭비하지 않게 됩니다.
저는 처음엔 반복문으로 바뀌더라도 결국 재귀도 반복이 되는데 같은거 아냐?라고 생각을 했는데 아니더라구요.
O(n)일때, 반복문의 경우 메모리를 사용하는 과정이 O(1)과 같이 조금만 사용하게 됩니다.
하지만 재귀 함수의 경우 함수를 호출할 때마다 스택 프레임이라는 메모리 공간을 새로 만들게 되어 변수, 돌아갈 주소들을 저장하게 됩니다.
즉, O(n)의 크기에 따라 무수히 많은 공간이 필요하게 됩니다.
이것이 바로 처음에 StackOverflowError를 발생하지 않도록 최적화해주는 기능이라고 설명한 이유입니다.
trailrec을 사용하는 경우
일반적인 반복문을 사용할 때는 trailrec을 사용하는 것이 성능적으로 좋을 수 있습니다.
하지만 우리가 일반적으로 사용하는 재귀의 경우 반복을 하며 해야하는 일이 남아있기 때문입니다.
위 코드 중 일부인데, 결과에 대한 result를 b가 홀수인지 짝수인지 파악하고 추가 작업을 해준 뒤에 반환하게 됩니다.
이처럼 단순히 반복하는 작업에서 끝나는 것이 아니라 돌아와서 기억해야할 변수가 생기게 되는거죠.
tailrec은 최적화 과정에서 현재 함수의 스택 프레임을 지우고 다음 함수로 점프하는 방식이기에 뒤에 작업이 남아있다면, 변수가 덮어씌워지며 반복문이 예상하지 않은 방향으로 흘러갈 것입니다.
해답 3 - trailrec 사용해보기
이전 함수와 전체적인 구조는 같지만, 나머지 값을 파라미터로 함여 함께 반복해준다.
tailrec...사실 main안에 while로 직접 만들면 되는거 아닐까요?ㅎㅎ
그래도 보기 편하니까~