프로그래머스 레벨 1문제 중 카카오 코딩테스트1차 비밀지도 문제를 풀다 알게된 비트 연산에 대해 정리한다.
문제는 아래와 같았다.
비밀지도
네오는 평소 프로도가 비상금을 숨겨놓는 장소를 알려줄 비밀지도를 손에 넣었다. 그런데 이 비밀지도는 숫자로 암호화되어 있어 위치를 확인하기 위해서는 암호를 해독해야 한다. 다행히 지도 암호를 해독할 방법을 적어놓은 메모도 함께 발견했다.
입력 형식
입력으로 지도의 한 변 크기 n 과 2개의 정수 배열 arr1, arr2가 들어온다.
1 ≦ n ≦ 16
arr1, arr2는 길이 n인 정수 배열로 주어진다.
정수 배열의 각 원소 x를 이진수로 변환했을 때의 길이는 n 이하이다. 즉, 0 ≦ x ≦ 2n - 1을 만족한다.
출력 형식
원래의 비밀지도를 해독하여 '#', 공백으로 구성된 문자열 배열로 출력하라.
내가 처음 생각한 방법은 bin()함수로 arr1과 arr2를 더해 각 자릿수가 0보다 크면 #로 치환하고 0이면 공백으로 치환하는 방법을 사용했다.
def solution(n, arr1, arr2):
list_1 = [int(bin(i)[2::]) for i in arr1]
list_2 = [int(bin(i)[2::]) for i in arr2]
answer = [str(x+y).zfill(n) for x,y in zip(list_1, list_2)]
res = []
for i in answer:
i = str(i).replace( '1' , "#")
i = str(i).replace( '2' , "#")
res.append(str(i).replace( '0' , " "))
return res
이따위로 밖에 생각 하지 못하는 내가 싫다는 생각을 하면서 다른사람들의 풀이를 봤다.
그러다 아래 코드를 보게되었는데...
????????????????????
def solution(n, *maps):
return [line(n, a | b) for a, b in zip(*maps)]
def line(n, x):
return ''.join(' #'[int(i)] for i in f'{x:016b}'[-n:])
다른 사람들의 풀이는 어느정도 이해를 하고 '아~이런 방법도 있구나~' 정도였지만 위 코드는 뭔지 모르겠다. 그냥 모르겠다.
나는 위 코드가 좋은 코드인지 실행시간면에서 유리한 코드인지 보다 저 코드 자체가 너무 궁금했다. 왜 저게 돌아가는지. 어떻게 이런 생각을 할 수 있는지.
특히 a | b이게 line함수에 인자로 넘어가는데 이게 뭔소린지....
여기까지 내가 비트연산에서 | 가 무슨 역할을 하는지 찾게된 과정이다.
비트연산자
0b110011001
0b100110101
비트 연산이란 위와 같은 2진수의 숫자열을 같은 자리끼리 &, |, ^, ~ 등의 비트 연산자로 연산하는것을 말한다.& 연산자
AND 연산자로 두 조건 모두 만족할때 참인 연산자인데 비트 연산에서는 각자리수 가 일치하면 1(True)또는 0(False)으로 연산한다.즉 and연산자의 1 & 1 = 1, 1 & 0 = 0, 0 & 1 = 0, 0 & 0 = 0의 규칙에 따라서 계산해주면 된다.
| 연산자
OR 연산자이다. 두조건 중 하나라도 만족하면 참이다. 비트연산에선? 둘중 하나라도 1이면 1이라는 말이다.
^ 연산자
0b1100 ^ 0b1001 = 0b0101
^연산자는 XOR연산자 라고 하는데 두 수가 서로 다른 값을 가질 때, 1을 반환한다.~ 연산자
~0b1100 = 0b0011 = 0b11
~연산자는 not과 같은 일을 한다.
~연산자는 모든 비트를 반전시킨다. 이 때, 앞 자리의 0은 생략할 수 있다.
다시 코드를 보자.
def solution(n, *maps):
return [line(n, a | b) for a, b in zip(*maps)]
def line(n, x):
return ''.join(' #'[int(i)] for i in f'{x:016b}'[-n:])
solution 함수는 n과 *maps 를 인자로 받는데 *maps는 사실 arr1, arr2 이다. arr1, arr2의 요소가 a와 b로 전달된다. 여기서 OR 비트 연산자로 계산된 값이 10진수로 x로 전달 된다.
이제 2진수 포매팅이다. 파이썬 3.6부터 f-string 이라고 불리는 문자열을 좀 더 쉽게 포멧팅하기 위한 새로운 방법이 추가되었다.f'{x:016b}'[-n:1] 으로 전달된 x를 16자리의 2진수로 바꿔준다. f'{x:016b}'가 무슨 말인지도 몰라서 찾아봤다. 파이썬 문자열 포메팅은 표현식을 지원하기 때문에 f'{x:016b}'와같이 사용할 수 있고 016b에서 16b는 16자리 2진수로 표현하겠다는 말이고 0은 표현한 2진수가 16자리가 안될때 0으로 채워주겠다는 말이다.
이제 2진수로 변환한 문자열을 반복하며 int(i)에 넣어준다 0이라면 " #"에 index[0]인 빈 공백이 들어갈 것이고 1이라면 index[1]에 해당하는 #이 들어간다.
코드이해 하는데 너무 오래 걸렸다. 이해는 했지만 아직 저런 생각을 할 수 있을지 자신이 없다. 잘 활용 할 수 있을때 까지 자주 봐야겠다.