def rotate(key, N):
def getNewValue(i, j, x, y):
key[j , N-i-1] = key[i ,j]
if (i == x and j == y): return
getNewValue(N-j-1, i, x, y)
for i in range(0, int(N/2)):
for j in range(i, N-i-1):
print(i,j )
tmp = key[i,j]
getNewValue(N-1-j, i, i, j)
key[j, N-1-i] = tmp
코딩테스트 문제를 풀다가 배열을 회전해야하는 문제가 있었다. 그래서 이 악물고 직접 코딩했다. 짧지만 알아보기 어렵다. 이 코드를 작성할 때 다음과 같은 규칙을 발견하여 코딩했다.
오리지널 배열 회전 후 배열
(0,0)(0,1)(0,2) (2,0)(1,0)(0,0)
(1,0)(1,1)(1,2) (2,1)(1,1)(0,1)
(2,0)(2,1)(2,2) (2,2)(1,2)(0,2)
규칙 1. 회전 후 행의 위치 = 회전하기 전 열의 위치
규칙 2. 회전 후 열의 위치 = 배열 길이 - 회전 전 행 위치 - 1
위의 규칙을 사용하여 한 자리씩 수정해나갔다. 그런데, 아무리 생각해도 내가 아닌 다른 사람이 저 코드를 보면 이해하기 까다로울 것 같다. 그래서 조금 더 깔끔한 코드가 없을까 찾아보았다.
python에는 역시 없는게 없었다.
def zip_rotate(original):
rotated = np.array(list(zip(*original[::-1])))
return rotated
단 한줄로 끝났다. 대단하다. python.
하나씩 살펴보자!
기본 형식은 A[a:b:c] 이다. 이는 A배열의 a위치부터 b위치 미만까지 c의 간격으로 선택한다는 것을 의미한다.
A = [0, 1, 2, 3, 4, 5]
print(A[0:4:2])
따라서 위의 코드를 실행하면 [0,2]
가 출력된다.
original = [[1,2],[2,3],[3,4]]
original = original[::-1]
print(original)
즉, 위의 코드는 original의 모든 범위를 -1간격으로 선택하라는 의미이다. 1을 한다면 0 1 2 3 순으로 가겠지만, -1을 한다면 -1 -2 -3 -4 순으로 가는 것이다.
결국 위의 코드 결과 [[3,4],[2,3],[1,2]]
가 출력된다.
zip은 파라미터로 입력받은 iterable elements 들을 순서대로 묶어주는 내장함수이다. 라고 말하지만, 직접 코드로 이해하는 것이 훨씬 빠르니 바로 예시 코드로 넘어가겠다.
iterable elements 란 반복 가능한 객체를 의미한다. 즉 배열과 같이 반복문을 사용하여 데이터를 순회하며 접근할 수 있는 객체이다.
동일한 크기의 배열
number_list = [1, 2, 3]
str_list = ['one', 'two', 'three']
# Two iterables are passed
result = zip(number_list, str_list)
# Converting itertor to set
result_set = set(result)
print(result_set)
number_list와 str_list는 동일한 개수이다. 이 두 배열을 zip 함수의 파라미터로 넘겨주면 아래와 같은 결과값이 나온다.
[(1, 'one'), (2, 'two'), (3, 'three')]
즉, number_list[i] 와 str_list[i] 를 묶어 튜플 형태로 반환해준 것이다.
zip에서 반환된 값을 그대로 출력하면 정상적으로 출력되지 않는다. 때문에 set 함수를 통해 iterable element 형태로 바꿔주는 것이다.
서로 다른 크기의 배열
numbersList = [1, 2, 3]
str_list = ['one', 'two']
numbers_tuple = ('ONE', 'TWO', 'THREE', 'FOUR')
# Notice, the size of numbersList and numbers_tuple is different
result = zip(numbersList, numbers_tuple)
# Converting to set
result_set = set(result)
print(result_set)
result = zip(numbersList, str_list, numbers_tuple)
# Converting to set
result_set = set(result)
print(result_set)
위 코드의 결과값은 다음과 같다.
{(1, 'ONE'), (2, 'TWO'), (3, 'THREE')}
{(1, 'one', 'ONE'), (2, 'two', 'TWO')}
서로 다른 크기의 배열을 파라미터로 넣었을 때는 최소 크기에 맞춰서 묶음 후 반환한다.
주의!
zip이 반환해주는 튜플에는 순서가 없다. 때문에 같은 코드일지라도 실행할 때 마다 튜플의 순서가 뒤바뀌어 있다. 만약, 일정한 값을 원한다면, set 함수 대신 list를 사용하면 된다.result_set = list(result)
첫 번째 줄 출력은 set으로 변환하여서 순서가 계속 변한다.
반면, 두 번째 출력은 list로 변환하여 순서가 변하지 않는다.
coordinate = ['x', 'y', 'z']
value = [3, 4, 5]
result = zip(coordinate, value)
result_list = list(result)
print(result_list)
c, v = zip(*result_list)
print('c =', c)
print('v =', v)
위 코드의 결과값은 다음과 같다.
[('x', 3), ('y', 4), ('z', 5)]
c = ('x', 'y', 'z')
v = (3, 4, 5)
즉, 파라미터 앞에 * 를 붙이면 다시 여러 개의 iterable elements로 나누어지는 것이다. 만약 unzip을 계속한다면?
coordinate = ['x', 'y', 'z']
value = [3, 4, 5]
result = zip(coordinate, value)
result_list = list(result)
for i in range(0,4):
result_list= list(zip(*result_list))
print(result_list)
위 코드의 결과는 아래와 같다.
[('x', 'y', 'z'), (3, 4, 5)]
[('x', 3), ('y', 4), ('z', 5)]
[('x', 'y', 'z'), (3, 4, 5)]
[('x', 3), ('y', 4), ('z', 5)]
rotated = np.array(list(zip(*original[::-1])))
이 한줄이면 배열 회전이 된다. 설명을 위해 조금 나눠서 코드를 작성해보자.
original = [[1, 2, 3],[4, 5, 6],[7, 8, 9]]
for i in range(1, 5):
print("iterate : ", i)
tailToHead=original[::-1]
print(tailToHead)
original = list(zip(*tailToHead))
print("rotate")
print(original)
위 코드의 결과값은 다음과 같다.
iterate : 1
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
[::-1]
[[7, 8, 9], [4, 5, 6], [1, 2, 3]]
rotate
[(7, 4, 1), (8, 5, 2), (9, 6, 3)]
iterate : 2
[(7, 4, 1), (8, 5, 2), (9, 6, 3)]
[::-1]
[(9, 6, 3), (8, 5, 2), (7, 4, 1)]
rotate
[(9, 8, 7), (6, 5, 4), (3, 2, 1)]
iterate : 3
[(9, 8, 7), (6, 5, 4), (3, 2, 1)]
[::-1]
[(3, 2, 1), (6, 5, 4), (9, 8, 7)]
rotate
[(3, 6, 9), (2, 5, 8), (1, 4, 7)]
iterate : 4
[(3, 6, 9), (2, 5, 8), (1, 4, 7)]
[::-1]
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
rotate
[(1, 2, 3), (4, 5, 6), (7, 8, 9)]
이건 진짜 본인이 스스로 이해해야 한다. 위에서 언급한 코드의 개념과 위의 과정을 잘 살펴서 이해하자 ! :-)!!!
+) 위의 표현은 회전이 되었음을 잘 확인하기 어려울 수 있으니 배열의 형태로 나타내었다.
iterate : 1
[[1 2 3]
[4 5 6]
[7 8 9]]
[::-1]
[[7 8 9]
[4 5 6]
[1 2 3]]
rotate
[[7 4 1]
[8 5 2]
[9 6 3]]
iterate : 2
[[7 4 1]
[8 5 2]
[9 6 3]]
[::-1]
[[9 6 3]
[8 5 2]
[7 4 1]]
rotate
[[9 8 7]
[6 5 4]
[3 2 1]]
iterate : 3
[[9 8 7]
[6 5 4]
[3 2 1]]
[::-1]
[[3 2 1]
[6 5 4]
[9 8 7]]
rotate
[[3 6 9]
[2 5 8]
[1 4 7]]
iterate : 4
[[3 6 9]
[2 5 8]
[1 4 7]]
[::-1]
[[1 4 7]
[2 5 8]
[3 6 9]]
rotate
[[1 2 3]
[4 5 6]
[7 8 9]]