[Algorithm] Dijkstra (다익스트라 알고리즘) 이론 설명

이미지
다익스트라 알고리즘 Overview 그래프 최단 경로를 찾는 알고리즘으로서, 특정한 하나의 정점 노드에서 다른 모든 정점 노드로 가는 최단 경로를 구하는 알고리즘입니다. ( 시간복잡도: $O(V^{2}$) ) 다익스트라 알고리즘은 그리디와 동적 프로그래밍 알고리즘 모두로 보고 있습니다.  왜 둘다로 여기는지는 참고 사이트 와 아래 Introduce를 확인해주세요. * 다익스트라 알고리즘은 음의 간선이 존재하지 않는 경우 사용할 수 있습니다. Introduce 카카오나 네이버 맵 등에서 출발지부터 도착지까지 가장 빠른 경로를 어떻게 찾아서 알려줄까요? 이럴 때 다익스트라 알고리즘을 사용합니다. 다익스트라 알고리즘은 컴퓨터 과학자인 Edsger W.Dijkstra로부터 1956년에 만들어진 그래프에서 단일 최단 경로를 찾는 알고리즘입니다. 이는 특정 정점으로부터 연결된 모든 정점의 최단 경로를 찾는 알고리즘이란 뜻입니다. 이 과정에서 도착 정점 뿐만 아니라 다른 모든 정점의 최단 경로까지 찾습니다.  즉, 매번 최단 경로의 정점을 선택해 탐색 반복하며(그리디 알고리즘 성격), 정점과 정점 사이 경로 갱신을 위해 이전에 구했던 경로 값을 사용합니다. (다이나믹 알고리즘 성격)  다익스트라 알고리즘은 유방향(directed)과 무방향(undirected)에서 양의 간선인 경우 동작 가능합니다. 유방향 (directed): 방향이 있는 간선을 가지는 그래프 (간선은 단방향 관계를 나타냄) 무방향 (undirected): 방향이 없는 간선을 가지는 그래프 (간선은 양방향 관계를 나타냄) Algorithm 1. 출발 정점 설정 (출발 정점 최소 비용은 0으로 설정, 자기자신으로 이동하는 비용이기 때문) 2. 정점 방문 여부를 체크하는 리스트 생성 (visited list) 3. 최단 거리 리스트 생성 (dijkstra list) 4. 선택된 정점의 인접한 정점들 중, 아직 방문하지 않았고 비용이 가장 적은 정점 선택 5. 4번

[프로그래머스] 3xn 타일링 (python)

이미지
 문제 설명 문제 링크:  https://school.programmers.co.kr/learn/courses/30/lessons/12902 Idea 풀이 방법이 직관적으로 떠오르지 않아 정의된 문제의 N 값에 따른 답안을 구하며 규칙을 찾아보기로 했습니다. 우선 2x1인 직사각형 타일을 이용하여 3xN인 바닥을 채워야 합니다.  다만, 3xN 바닥의 세로 길이가 3이기 때문에 2x1 직사각형은 반드시 누워져서 들어가는 경우가 발생합니다. 이는 N이 홀수인 경우엔 2x1 직사각형 타일을 이용하여 3xN 바닥을 채울 수 없음을 의미합니다. (아래 그림 참고) 3x3, 3x5인 바닥의 경우 위 이유로 규칙은 N이 짝수인 경우에만 찾도록 하겠습니다. 1. 규칙 찾기 N=0: 0개, 타일을 둘 수 없음 N=2: 3개, 아래 사진 참고 N=4: 11개, (3*3 + 2)   - N=2일 때 있었던 3개의 경우에 다시 3가지의 경우의 수들 추가.   - N=4일 때 특수한 형태의 모양 생성 (그림 맨 하단 참고)  N=6: 41개, (3*11 + 2*3 + 2)   - N=4일 때 있었던 경우에 다시 3가지의 경우의 수들 추가.   - N=4일 때 있었던 특수한 형태의 모양에 대한 예외 경우의 수 추가 (그림 우측 참고)      > 특수한 형태의 모양은 "앞, 뒤"에 따라 다른 경우의 수를 만들 수 있습니다.    - N=6일 때 특수한 형태의 모양 생성 (그림 맨 우하단 참고)  N=8: 153개, (3*41 + {6*3+2*2} + 2*3 + 2)   - N=6일 때 있었던 경우에 다시 3가지의 경우의 수들 추가.   - N=6일 때 있었던 특수한 형태의 모양에 대한 예외 경우의 수 추가 (그림 우상단 참고)      > 특수한 형태의 모양은 "앞, 뒤"에 따라 다른 경우의 수를 만들 수 있습니다.    - N=4일 때 있었던 특수한 형태의 모양에 대한 예외 경우의 수 추가 (그림 우중

[프로그래머스] 유사 칸토어 비트열 (python)

이미지
  문제 설명 문제 링크:  https://school.programmers.co.kr/learn/courses/30/lessons/148652 Idea 풀이 방법이 직관적으로 떠오르지 않아 정의된 문제에 대한 규칙을 찾아보기로 했습니다. 1. 규칙 찾기 N=0 : 1 (1개) N=1 : 1 / 1 / 0 / 1 / 1 (5개) N=2 : 11011 / 11011 / 00000 / 11011 / 11011 (25개) N=3 : 1101111011000001101111011 / 1101111011000001101111011 / 000000000000000000  / 1101111011000001101111011 / 1101111011000001101111011 (125개) N-1 값들이 앞뒤*2로 생기며 중간은 N-1의 길이의 0이 생기는 규칙을 발견할 수 있습니다. Ex. N=2라면, 11011(N=1)이 앞뒤*2로 생기며 중간에는 11011의 길이인 5개의 0이 생김       -> 11011 / 11011 / 00000 / 11011 / 110111 2. 규칙 정리 1. 규칙 찾기에서 찾은 규칙을 정리하면 아래와 같습니다. (1만 세서 정리) N cantor_bit total_1 len 1 1/1/0/1/1 4 5 2 4/4/0/4/4 16 25 3 16/16/0/16/16 64 125 4 64/64/0/64/64 256 625 5 256/256/0/256/256 1024 3125 N이 증가함에 따라 1의 수는 $4^{N}$으로, 길이는 $5^{N}$으로 증가하는 규칙을 발견할 수 있습니다. Ex. N=2라면, 1의 수는 총 16개 (4 / 4 / 0 / 4 / 4), 유사 칸토어 길이는 25 3. 수식 정리   1. 총 5개의 그룹으로 나눔 (X / X /

[프로그래머스] 연속 부분 수열 합의 개수 (python)

이미지
  문제설명 문제 링크:  https://school.programmers.co.kr/learn/courses/30/lessons/131701 Idea 행의 %N으로 원형 리스트 인덱스를 추출하여 탐색 후 값을 더해줍니다.  중복된 값이 없어야 하기 때문에 set을 이용하여 부분 수열 합의 값을 저장합니다. 이 때 부분 수열의 합을 구한 값을 재사용합니다. (구했던 값을 굳이 또 다시 구할 필요는 없습니다) Solution 01 def solution (elements): N = len (elements) for i in range (N- 1 ): elements.extend([elements[i*N+j] + elements[(i+j+ 1 )%N] for j in range (N)]) return len ( set (elements)) 부분 수열의 합을 구한 값들을 elements 변수에 추가하며 추가된 변수 값과 기존에 제공된 elements의 값을 더해줍니다. 이후 set으로 변경하여 중복되지 않은 부분 수열 합의 개수를 구합니다. Solution 02 def solution (elements): N = len (elements) ans = set (elements) add = [e for e in elements] for i in range ( 1 , N): for j in range (N): add[j] += elements[(i+j)%N] ans.add(add[j]) return len (ans) Solution 01의 접근법과 동일합니다. 다만 01에서 사용한 elements 리스트에 계산된 부분 수열의 합을 추가하는 형식이 아닌, add라는 리스트를 생성하여 부분 수열의 합을 누적하여 계산합니다. 계산된 값은 set에 바로바로 추가하며 중복되지 않은 부분 수열 합의 개수를 구합니다. So

[프로그래머스] 혼자 놀기의 달인 (python)

이미지
문제설명 문제 링크:  https://school.programmers.co.kr/learn/courses/30/lessons/131130 Idea 특정 인덱스를 기반으로 cards를 탐색해 해당 리스트 값을 새 인덱스로 설정하며 cards를 탐색합니다. 이미 탐색된 cards에 접근한 경우 탐색을 종료하고, 이 때까지 탐색된 수량을 저장합니다. 전체 cards를 모두 탐색할 때까지 위를 반복합니다. 탐색이 종료된 경우 탐색된 수량이 2개 이상이면 최대 수량 2개의 곱을, 1개 이하면 0을 반환합니다. Solution 01 def solution (cards): ans = [] for i in range ( len (cards)): cnt = 0 pts = i while cards[pts]: cnt += 1 temp = cards[pts] cards[pts] = 0 pts = temp - 1 if cnt: ans.append(cnt) ans.sort() return 0 if len (ans) < 2 else ans[- 1 ] * ans[- 2 ] 0~N-1까지 돌며, 탐색하지 않은 cards[pts]라면 해당 값을 pts 변수에 저장하고 방문한 수량(cnt)을 증가합니다. 이미 방문한 cards라면 그 전까지의 수량을 ans 변수에 저장합니다. ans 변수를 오름차순으로 정렬한 뒤, 길이가 2 이상이면 -1 번째와 -2번째 길이의 곱을 반환합니다. 전체 코드는 깃허브 에서 확인 가능합니다.

[python] 16진법 사용 이유

이미지
  16진법이란 16진법은 10개의 숫자(0~9)와 6개의 문자 (A~F)를 사용해서 값을 표현합니다. 문자는 A(10), B(11), C(12), D(13), E(14), F(15)이며 아래 표를 참고하세요. 위 그림에서 2진법은 15라는 숫자를 4자리로 표현하는 반면 16진수는 1자리로 표현할 수 있습니다. (2의 4승을 표현할 수 있기 때문) 16진법은 4개의 비트 값을 "한번"에 표현할 수 있다는 점을 확인해주세요. 16진법을 사용하는 이유 바이트가 커질 때 2, 10, 16진법 범위를 보게되면 아래와 같습니다. 2진법은 바이트가 늘어남에 따라 자릿수가 기하급수적으로 늘어나는 단점이 존재하며, 사람은 10진수에 익숙하기 때문에 2진수로 적힌 값이 10진수로 얼마인지 직관적으로 파악하기 어렵습니다. 0b1111101000은 10진수로 얼마인가? -> 1000 반대로 10진법은 바이트마다 떨어지는 단위가 깔끔하지 않습니다.(255, 65535, 4294967295...) 또한 1000이라는 수는 2 바이트로 저장되는데, 만약 바이트 단위로 나눠 10진수로 표현할 때 문제가 발생합니다. (10진수는 바이트 단위로 나눠 떨어지는 값이 아니여서) 물론 각 바이트에 저장된 1000을 표현하기 위해 256(1바이트 범위)으로 나눠 몫과 나머지로 표현할 수 있습니다. (256 * 3 + 244 = 1000) 다만, 2바이트를 몫과 나머지로 표현한 그림을 보고 1000이라는 것을 직관적으로 이해할 사람은 거의 없을 겁니다. 마지막으로 16진법은 1바이트(8비트)마다 FF 단위로 증가하고 자릿수 또한 다른 진법보다 적게 사용합니다.  1000을 2진수로 표현할 때 사람이 직관적으로 변환 할 수 없고 계산을 통해  0b1111101000임을  알 수 있습니다. 하지만 16진법은 0x3e8(1000)로 훨씬 짧게 표현할 수 있으며, 16진법은 자릿수가 4비트를 의미하기 때문에 아래와 같이 직관적인 2진법 변환 및 해

[Algorithm] Union-Find 정리

이미지
Union-Find 대표적인 그래프 알고리즘으로 상호 배타적 집합(Disjoint Set)을 표현할 때 사용합니다. 상호배타집합이란? 서로 중복으로 포함된 원소가 없는 집합들을 상호배타집합 혹은 서로소 집합이라 부릅니다. A = {1, 2}, B = {3, 4} = Disjoint Set A = {1, 2}, B = {2, 3, 4} = Not Disjoint Set (2가 중복되기 때문에) 그래프 문제에서 여러 개체를 집합으로 "합치거나" 개체가 어떤 집합에 있는지 "찾을 때" 사용하기 때문에, Union-Find라고 부르며, 이 때 주어진 개체들이 중복된 값을 가지지 않으면 상호배타집합이라 부를 수 있습니다. Union-Find 알고리즘은 단독으로 사용되는 경우는 별로 없고, 다른 알고리즘을 구현하는 과정에서 많이 사용됩니다. 대표적으로 최소비용 신장트리 (MST)를 만드는 크루스칼 알고리즘에서 Union-Find를 이용합니다. 1. Union-Find 연산 Union-Find 지원 연산은 크게 Init(초기화), Union(합치기), Find(찾기) 연산으로 나뉩니다. 위와 같이 여러 개의 개체(노드)가 있다고 가정하겠습니다. Init(초기화) 개체가 어떤 집합에 속해있는지 확인하기 위한 lookup 테이블을 만듭니다. 최초 각 개체는 자기를 제외한 다른 개체들과 집합을 이루지 않기 때문에 자기 자신이 집합의 대표자가 됩니다. 대표자란 각 집합마다 1개씩 있는, Union-Find에서 집합을 구분하는 데 사용되는 원소를 의미합니다. Union (합치기) union(x, y)를 하면 x, y 원소가 속한 2개의 집합을 하나의 집합으로 합칩니다. 여기서 x, y 원소를 합치는게 아니라 x, y가 속한 "집합"을 합친다는 것에 유의해주세요. "집합"을 합치기 위해 초기화 과정에서 생성한 lookup 테이블의 "대표자"를 이용합니다.