일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
Tags
- 머신러닝
- matplotlib
- 순열
- pandas filter
- INSERT
- Slicing
- Machine Learning
- 조합
- maplotlib
- 파이썬
- 문제풀이
- 재귀함수
- 등비수열
- plt
- 스터디노트
- SQL
- 등차수열
- 기계학습
- barh
- 자료구조
- python
- 통계학
- MacOS
- numpy
- 리스트
- DataFrame
- pandas 메소드
- Folium
- pandas
- tree.fit
Archives
- Today
- Total
코딩하는 타코야끼
[스터디 노트] Week4_2일차 [unit19 ~ 34] - 알고리즘 본문
728x90
반응형
1. 알고리즘_5 [unit 19 ~ 24]
📍 근삿값
- 특정 값(참값)에 가장 가까운 값을 근삿값이라고 한다.
import random
nums = random.sample(range(0, 50), 20)
print(f"nums : {nums}")
input_num = int(input("input num: "))
print(f"input num: {input_num}")
near_num = 0
min_num = 50
for n in nums:
abs_num = abs(n - input_num)
if abs_num < min_num:
min_num = abs_num
near_num = n
print(f"near_num: {near_num}")
>>>
nums : [15, 27, 0, 1, 32, 31, 6, 36, 35, 39, 37, 33, 4, 41, 24, 46, 21, 22, 13, 25]
input num: 11
input num: 11
near_num: 13
📍 평균
- 여러 수나 양의 중간값을 갖는 수를 평균이라고 한다.
# 다음은 어떤 체조선수의 점수이다.평균을 구하고 순위를 정하는 알고리즘을 만들어보자.
score_list = [8.9, 7.6, 8.2, 9.1, 8.8, 8.1, 7.9, 9.4, 7.2, 8.7]
sum_score = 0
grade_score = {}
score_list.sort(reverse = True)
for idx, value in enumerate(score_list):
grade_score[idx+1] = value
sum_score += value
print(f"전체 평균: {sum_score}")
for num in grade_score:
print(f"{num}위 -> {grade_score[num]}")
>>>
전체 평균: 83.9
1위 -> 9.4
2위 -> 9.1
3위 -> 8.9
4위 -> 8.8
5위 -> 8.7
6위 -> 8.2
7위 -> 8.1
8위 -> 7.9
9위 -> 7.6
10위 -> 7.2
📍 재귀
- 재귀 함수(recursive function)란 함수 내에서 자기 자신을 다시 호출하는 함수를 의미합니다.
- 재귀 함수는 일반적으로 문제를 더 작은 하위 문제(sub-problems)로 분해하고, 이 하위 문제를 해결함으로써 원래 문제를 해결하는 방식으로 작동합니다.
- 재귀 함수를 이해하고 설계하는 데는 두 가지 주요 요소가 있습니다.
- 기본 케이스(base case)
- 재귀 케이스(recursive case)
- 재귀 함수를 사용하면 복잡한 알고리즘을 간결하게 표현할 수 있지만, 잘못 사용하면 스택 오버플로우(stack overflow)와 같은 문제를 일으킬 수 있으므로 주의가 필요합니다.
- 따라서 재귀 함수를 사용할 때는 항상 기본 케이스가 존재하도록 하여 재귀가 끝날 수 있도록 해야 합니다
⚡️ 팩토리얼 함수는 재귀 함수를 사용하여 쉽게 구현할 수 있는 고전적인 예입니다.
- 팩토리얼 n!은 n * (n-1) * (n-2) * ... * 1 이며, 재귀적으로 정의할 수 있습니다.
- factorial(n - 1) 호출은 함수가 자기 자신을 호출하는 재귀적 부분입니다.
- n이 0이나 1이 되면 재귀 호출이 중지되고 1이 반환되는데, 이 부분이 기본 케이스입니다.
def star_print(n):
if n == 0 :
return 1
else:
print("*"*n)
return star_print(n - 1)
star_print(10)
>>>
**********
*********
********
*******
******
*****
****
***
**
*
⚡️ 유클리드 호제법
- 유클리드 호제법의 기본 아이디어는 두 수 a, b (a > b)의 최대 공약수는 'a를 b로 나눈 나머지'와 'b'의 최대 공약수와 같다는 것입니다
def gcd(a, b):
if b == 0:
return a
else:
return gcd(b, a % b)
print(gcd(96, 40))
>>>
8
2. 알고리즘_6 ~ 7 [unit 25 ~ 30]
📍 하노이의 탑
- 세 개의 기둥과 이 기둥 중 하나에 꿴 원반이 있다고 가정합니다. 이 원반들은 크기 순서대로 꿰어져 있으며 가장 큰 원반이 가장 아래에, 가장 작은 원반이 가장 위에 있습니다. 목표는 모든 원반을 다른 기둥으로 옮기는 것입니다. 그러나 원반을 옮길 때는 다음과 같은 규칙을 따라야 합니다.
⚡️ 문제 해결 방법
- 위의 각 단계 역시 재귀적으로 실행할 수 있습니다. 각 단계에서 원반의 개수가 1개인 경우는 기본 경우(base case)로, 해당 원반을 바로 목표 기둥으로 옮기면 됩니다
def hanoi(n, source, target, auxiliary):
if n == 1:
print(f"{n}블럭: {source}에서 {target}(으)로 이동!!")
else:
# 1. n-1개의 원반을 보조 기둥으로 옮깁니다.
hanoi(n - 1, source, auxiliary, target)
# 2. 남아있는 원반을 목표 기둥으로 옮깁니다.
print(f"{n}블럭: {source}에서 {target}(으)로 이동!!")
# 3. 보조 기둥에 있는 원반을 목표 기둥으로 옮깁니다.
hanoi(n - 1, auxiliary, target, source)
# 3개의 원반을 사용하는 하노이의 탑 문제를 해결합니다.
'''
원반을 옮기는 순서가 출력됩니다.
처음에는 3개의 원반이 'A' 기둥에 있고,
최종적으로 모든 원반이 'C' 기둥으로 옮겨지는 과정을 보여줍니다.
'''
hanoi(3, 'A', 'C', 'B')
>>>
1블럭: A에서 C(으)로 이동!!
2블럭: A에서 B(으)로 이동!!
1블럭: C에서 B(으)로 이동!!
3블럭: A에서 C(으)로 이동!!
1블럭: B에서 A(으)로 이동!!
2블럭: B에서 C(으)로 이동!!
1블럭: A에서 C(으)로 이동!!
📍 병합 정렬
- 병합 정렬(Merge Sort)은 분할 정복(Divide and Conquer) 전략을 사용하는 효율적인 정렬 알고리즘입니다.
- 병합 정렬은 원래 배열을 두 부분으로 분할하고 각 부분을 정렬한 다음 병합하는 과정을 반복하여 작동합니다.
- 이 알고리즘은 O(n log n)의 시간 복잡도를 가지며, 안정적인 정렐 알고리즘으로 알려져 있습니다.
⚡️ 병합 정렬의 주요 단계
import random
def merge_sort(array):
# 재귀 종료 조건: 배열의 길이가 1 이하일 때
if len(array) <= 1:
return array
# 분할
mid = len(array) // 2
left = merge_sort(array[:mid]) # 왼쪽 반을 재귀적으로 정렬
right = merge_sort(array[mid:]) # 오른쪽 반을 재귀적으로 정렬
# 정렬된 두 배열을 병합
merged = []
l = r = 0
while l < len(left) and r < len(right):
if left[l] < right[r]:
merged.append(left[l])
l += 1
else:
merged.append(right[r])
r += 1
# 남아있는 원소들을 결과 리스트에 추가
merged.extend(left[l:])
merged.extend(right[r:])
return merged
num_r = random.sample(range(1, 11), 10)
print(num_r)
sorted_array = merge_sort(num_r)
print(sorted_array)
>>>
[8, 7, 1, 6, 4, 9, 2, 3, 5, 10]
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
📍 퀵 정렬
- 퀵 정렬(Quick Sort)은 효율적이면서도 간단한 분할 정복(divide and conquer) 정렬 알고리즘입니다.
- 이 알고리즘의 기본 아이디어는 배열의 한 요소를 선택합니다. (이를 '피벗'이라고 부릅니다)
- 퀵 정렬의 평균 시간 복잡도는 O(n log n) 입니다.
- 그러나 피벗 선택이 잘못되었을 때(예: 이미 정렬된 배열에서 맨 처음이나 맨 마지막 요소를 피벗으로 선택한 경우) 최악의 경우 시간 복잡도는 O(n^2)가 됩니다.
⚡️ 문제 해결 방법
def partition(array, low, high):
pivot = array[high]
i = low - 1
for j in range(low, high):
if array[j] < pivot:
i += 1
array[i], array[j] = array[j], array[i]
array[i + 1], array[high] = array[high], array[i + 1]
return i + 1
def quick_sort(array, low=0, high=None):
if high is None:
high = len(array) - 1
if low < high:
pi = partition(array, low, high)
quick_sort(array, low, pi - 1)
quick_sort(array, pi + 1, high)
# 테스트
arr = [10, 7, 8, 9, 1, 5]
quick_sort(arr)
print(arr) # [1, 5, 7, 8, 9, 10]
>>>
[1, 5, 7, 8, 9, 10]
❗️ 주의
- 피벗 선택 전략에 따라 퀵 정렬의 성능이 크게 달라질 수 있습니다.
- 일반적으로 중간값을 피벗으로 선택하거나, 무작위로 피벗을 선택하는 전략이 효과적인 경우가 많습니다.
- 이런 전략을 사용하면 퀵 정렬의 평균적인 성능을 더욱 향상시킬 수 있습니다.
반응형
'zero-base 데이터 취업 스쿨 > 스터디 노트' 카테고리의 다른 글
[스터디 노트] Week5_1일차 [unit1 ~ 11] - EDA(CCTV) (0) | 2023.08.16 |
---|---|
[스터디 노트] Week4_3일차 [unit31 ~ 47] - 알고리즘 문제 풀이 (0) | 2023.07.31 |
[스터디 노트] Week4_1일차 [unit1 ~ 18] - 알고리즘 (0) | 2023.07.25 |
[스터디 노트] Week3_5일차 [unit39 ~ 53] - 자료구조 문제풀이 (0) | 2023.07.23 |
[스터디 노트] Week3_4일차 [unit23 ~ 38] - 자료구조 (0) | 2023.07.23 |