출처: Programmers > 코딩테스트 연습 > 연습문제 > 하노이의 탑
문제 설명:
하노이 탑(Tower of Hanoi)은 퍼즐의 일종입니다. 세 개의 기둥과 이 기동에 꽂을 수 있는 크기가 다양한 원판들이 있고, 퍼즐을 시작하기 전에는 한 기둥에 원판들이 작은 것이 위에 있도록 순서대로 쌓여 있습니다. 게임의 목적은 다음 두 가지 조건을 만족시키면서, 한 기둥에 꽂힌 원판들을 그 순서 그대로 다른 기둥으로 옮겨서 다시 쌓는 것입니다.
- 한 번에 하나의 원판만 옮길 수 있습니다.
- 큰 원판이 작은 원판 위에 있어서는 안됩니다.
하노이 탑의 세 개의 기둥을 왼쪽 부터 1번, 2번, 3번이라고 하겠습니다. 1번에는 n개의 원판이 있고 이 n개의 원판을 3번 원판으로 최소 횟수로 옮기려고 합니다.
1번 기둥에 있는 원판의 개수 n이 매개변수로 주어질 때, n개의 원판을 3번 원판으로 최소로 옮기는 방법을 return하는 solution를 완성해주세요.
제한사항
- n은 15이하의 자연수 입니다.
입출력 예
n | result |
---|---|
2 | [ [1,2], [1,3], [2,3] ] |
입출력 예 설명
입출력 예 #1
다음과 같이 옮길 수 있습니다.
코드:
1
2
3
4
5
6
def move(n, start, end, mid):
if n == 1:
return [[start, end]]
return move(n-1, start, mid, end) + move(1, start, end, mid) + move(n-1, mid, end, start)
def solution(n):
return move(n, 1, 3, 2)
코드 설명:
재귀를 사용하여 각 단계에서 하노이 타워의 원반을 옮기는 과정을 리스트로 반환했다
move
함수는 4개의 매개변수(원반의 개수(n
), 시작점(start
), 종착점(end
), 중간점(mid
))을 받는다n
이 1일 경우, 원반을 바로start
에서end
로 옮긴다n
이 1보다 큰 경우,n-1
개의 원반을start
에서mid
로 옮긴 다음, 1개의 원반을start
에서end
로 옮기고 마지막으로n-1
개의 원반을mid
에서end
로 옮긴다
우수답변자 코드:
- 문제가 개편되어 좀 다르지만
yield from
을 처음 봤고, 그 활용이 마음에 들어서 가져와 보았다 yield from
을 활용해 창의적이지만,,,개인적으로는+ 를 쓰는게 더 가독성도 좋고 이해하기 편한듯해 보인다
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 문제가 개편되었습니다. 이로 인해 함수 구성이나 테스트케이스가 변경되어, 과거의 코드는 동작하지 않을 수 있습니다.
# 새로운 함수 구성을 적용하려면 [코드 초기화] 버튼을 누르세요. 단, [코드 초기화] 버튼을 누르면 작성 중인 코드는 사라집니다.
def hanoi(n):
def _hanoi(m, s, b, d):
if m == 1:
yield [s, d]
else:
yield from _hanoi(m-1, s, d, b)
yield [s, d]
yield from _hanoi(m-1, b, s, d)
ans = list(_hanoi(n, 1, 2, 3))
return ans # 2차원 배열을 반환해 주어야 합니다.
# 아래는 테스트로 출력해 보기 위한 코드입니다.
print(hanoi(2))
yield
yield
는 하나의 함수에서generator
을 통해 여러개의 리턴값을 순차적으로 리턴 할 수 있다yield from
yield from
은 뒤에 따라오는 함수로부터yield
를 한다
yield와 yield from의 차이
yield
와yield from
은 모두 Python의 제너레이터(generator) 기능을 사용하는 키워드이다. 제너레이터는 이터레이터를 생성하는 함수로, 한 번에 하나씩 값(value)을 생성(yield)하고, 메모리에 모든 값을 저장하지 않기 때문에 메모리 효율성이 높다
yield
와yield from
의 주요 차이점은 다음과 같다:
- yield: 이 키워드는 제너레이터 함수에서 값을 생성하는 데 사용된다. 함수가
yield
를 만나면, 현재 상태를 저장하고 생성한 값을 반환한다. 다음 번 호출 시에는 그 상태를 복원하고,yield
다음의 코드를 실행한다.
1
2
3
4
5
6
7
def simple_generator():
yield 1
yield 2
yield 3
for i in simple_generator():
print(i) # Prints 1, then 2, then 3
- yield from: Python 3.3 이후에 추가된 이 키워드는 다른 제너레이터에서 값을 생성하는 데 사용된다. 이를 사용하면, 제너레이터를 다른 제너레이터에 위임(delegate)할 수 있다. 이는 중첩된 제너레이터를 평평하게 만드는 데 유용하다.
1
2
3
4
5
def nested_generator():
yield from simple_generator() # Uses the generator defined above
for i in nested_generator():
print(i) # Prints 1, then 2, then 3
이 예제에서
nested_generator
는simple_generator
를 호출하고, 그 값을 직접 생성한다. 이는 간단히yield from
을 사용하여 이루어진다. 이 코드는simple_generator
의 모든 값을 순차적으로 생성한다.따라서,
yield
는 값을 직접 생성하고,yield from
은 다른 제너레이터에서 값을 생성하는 데 사용된다. 이러한 방식으로,yield from
은 제너레이터의 중첩을 처리하는 데 도움이 된다.
우수답변자2 코드:
나와 비슷한 코드지만 초기 설정이 다르다 ```python def hanoi(f, t, m, n): if n == 0: return []
return hanoi(f, m, t, n-1) + [[f, t]] + hanoi(m, t, f, n-1)
def solution(n): return hanoi(1, 3, 2, n) ```
피드백:
- 처음엔 답도 없어 보였지만, 메모장에 원판 3개까지 옮기는 방법을 적으니 패턴이 보였고, 4개짜리로 확신이 들었다.
- 그래도 재귀함수로 구현하는데 꽤 머리아팠는데, 패턴 찾을때와 마찬가지로 3번째까지 일단 코드로 적고보니 점차 답이 보였다.
- 레벨2치고 굉장히 오래걸렸지만 재귀함수가 생각하기 힘든편이었는데 연습이 되어 좋은것 같다.