AI 웹 개발 과정/알고리즘

알고리즘 - 1주차

만 기 2022. 7. 12. 09:39

 

1. 최댓값 찾기

숫자들로 이루어진 배열이 있을 때, 이 배열에서 가장 큰 수를 반환해라.

 

방법1. 가장 큰 수를 저장할 변수를 만들고 for문으로 배열을 돌아가며 변수와 비교한다. if 값이 더 크다면 큰수변수에 대입

 

def find_max_num(array):
    max_num = array[0]
    for num in array:
        if num > max_num:
            max_num = num
    return max_num


print("정답 = 6 / 현재 풀이 값 = ", find_max_num([3, 5, 6, 1, 2, 4]))
print("정답 = 6 / 현재 풀이 값 = ", find_max_num([6, 6, 6]))
print("정답 = 1888 / 현재 풀이 값 = ", find_max_num([6, 9, 2, 7, 1888]))

 


 

2. 최빈값 찾기

입력받은 문자열에서 가장 많이 들어간 알파벳 출력하기

 

input = "hello my name is sparta"


def find_max_occurred_alphabet(string):
    alphabet_occurrence_array = [0] * 26

    for char in string:
        if char.isalpha():
            numberth = (ord(char) - ord('a'))
            alphabet_occurrence_array[numberth] += 1
        else:
            pass

    # 가장 많이 발생했던 빈도수를 저장하는 변수
    max_occurrence = 0
    # 가장 많이 나왔던 알파벳의 인덱스를 저장하는 변수
    max_alphabet_index = 0
    for index in range(len(alphabet_occurrence_array)):
        alphabet_occurrence = alphabet_occurrence_array[index]

        if alphabet_occurrence > max_occurrence:
            max_occurrence = alphabet_occurrence
            max_alphabet_index = index

    max_alphabet = chr(max_alphabet_index + ord('a'))
    return max_alphabet


result = find_max_occurred_alphabet(input)
print(result)

1) 리스트에 초기값 0으로 알파벳 갯수만큼 생성

2) 입력 받은 문자열을 for문 돌려서 알파벳 하나씩 뽑아낸다.

3) .isalpha() 를 사용해서 띄어쓰기와 알파벳 구분

4) ord() 를 사용하면 ASCII 코드대로 문자를 숫자로 바꿔준다.

5) (for문의 현재 알파벳 아스키값) - (a의 아스키값) = (배열안에서 현재 알파벳 인덱스 번호)

6) 해당 인덱스 +1 

7) alphabet_occurrence_array 안에는 입력 받은 문자열의 알파벳마다 해당하는 인덱스에 중복되는 수 만큼 값이 들어가있게된다.

8) alphabet_occurrence_array 를 for문으로 인덱스 하나씩 비교해서 더 큰 값을 저장

  가장 많이 발생했던 빈도수를 저장하는 변수 ,  그때의 인덱스 값을 저장하는 변수 정의

9) max_alphabet_index 에 가장많이나온 알파벳의 인덱스가 들어가고 a의 아스키값을 더해서 해당 알파벳의 아스키 값으로 만든다.

10) chr() 을 사용해서 다시 문자로 바꾸어서 출력

 


 

시간 복잡도

입력값과 문제를 해결하는 데 걸리는 시간과의 상관관계

입력값이 늘어났을때 걸리는 시간은 몇 배로 늘어나는가

    for num in array:              # array 의 길이만큼 아래 연산이 실행
        for compare_num in array:  # array 의 길이만큼 아래 연산이 실행
            if num < compare_num:  # 비교 연산 1번 실행
                break
        else:
            return num

array의 길이 X array의 길이 X 비교 연산 1번

= N x N = N^2

 

		for num in array:      # array 의 길이만큼 아래 연산이 실행
		    if num > max_num:  # 비교 연산 1번 실행
		        max_num = num  # 대입 연산 1번 실행
  1. max_num 대입 연산 1번
  2. array의 길이 X (비교 연산 1번 + 대입 연산 1번)
  3. 2N+1

 

  1. N 과 N^2은 N 이 커질수록 더 큰 차이가 나게된다.
  2. N의 지수를 먼저 비교하면 되겠구나.

매번 코드를 매 실행 단위로 이렇게 몇 번의 연산이 발생하는지 확인하는 건 불가능하다. 따라서 상수는 신경쓰지말고, 입력값에 비례해서 어느 정도로 증가하는지만 파악하면 된다.

즉, 2N+1의 연산량이 나온 첫번째 풀이 방법은 N 만큼의 연산량이 필요하다. N^2 의 연산량이 나온 두번째 풀이 방법은 N^2 만큼의 연산량이 필요하다. 참고로, 만약 상수의 연산량이 필요하다면, 1 만큼의 연산량이 필요하다고 말하면 됩니다.

 


 

공간 복잡도

입력값과 문제를 해결하는 데 걸리는 공간과의 상관관계를 말한다.

저장하는 데이터의 양이 1개의 공간을 사용한다고 계산한다.

 

    alphabet_array = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "x", "y", "z"]
# -> 26 개의 공간을 사용합니다
    max_occurrence = 0 # 1개의 공간을 사용합니다
    max_alphabet = alphabet_array[0]   # 1개의 공간을 사용합니다.

    for alphabet in alphabet_array:
        occurrence = 0  # 1개의 공간을 사용합니다

공간복잡도는 

  1. alphabet_array 의 길이 = 26
  2. max_occurrence, max_alphabet, occurrence 변수 = 3

29 가 된다.

 

대부분의 문제에서는 알고리즘의 성능이 공간에 의해서 결정되지 않는다. 따라서 공간 복잡도보다는 시간 복잡도를 더 신경 써야 한다.

 


 

점근 표기법

알고리즘의 효율성을 평가하여 수학적으로 표기하는 방법.

위에서 이 함수는 N 정도의 시간과 공간이 걸리겠구나 하면서 분석했던 게 점근 표기법의 일종이라고 말할 수 있다.

 

빅오(Big-O)표기법 : 최악의 성능이 나올 때 어느 정도의 연산량이 걸릴것인지에 대해 표기

 

빅 오메가(Big-Ω) 표기법 : 최선의 성능이 나올 때 어느 정도의 연산량이 걸릴것인지에 대해 표기

 

 

def is_number_exist(number, array):
    for find_num in array:
        if number == find_num:
            return True
    return False


result = is_number_exist
print("정답 = True 현재 풀이 값 =", result(3,[3,5,6,1,2,4]))
print("정답 = Flase 현재 풀이 값 =", result(7,[6,6,6]))
print("정답 = True 현재 풀이 값 =", result(2,[6,9,2,7,1888]))

위 코드에서 시간복잡도는 N*1 시간 인것을 알 수 있다.

하지만 입력값을 다르게 비교해보면,

input = [3, 5, 6, 1, 2, 4] 이라는 입력값에 대해서 3을 찾는다면 1번의 연산이 걸리고

input = [4, 5, 6, 1, 2, 3] 이라는 입력값에 대해서 3을 찾으면 가장 마지막 연산에서(input의 길이(N) 만큼) 찾게된다.

따라서

빅오 표기법으로 표시하면 O(N), 빅 오메가 표기법으로 표시하면 Ω(1) 의 시간복잡도를 가진 알고리즘이다.

 

알고리즘에서는 최악의 경우를 대비해야 하기때문에 빅오 표기법으로 분석한다.

 


 

곱하기 or 더하기

Q. 다음과 같이 0 혹은 양의 정수로만 이루어진 배열이 있을 때, 왼쪽부터 오른쪽으로 하나씩 모든 숫자를 확인하며 숫자 사이에 '✕' 혹은 '+' 연산자를 넣어 결과적으로 가장 큰 수를 구하는 프로그램을 작성하시오.

단, '+' 보다 '✕' 를 먼저 계산하는 일반적인 방식과는 달리, 모든 연산은 왼쪽에서 순서대로 이루어진다.

input = [0,3,5,6,1,2,4]


def find_max_plus_or_multiply(array):
    cal = 0
    for num in array:
        if (cal <= 1) or (num <= 1):
            cal += num
        else:
            cal *= num

    return cal

result = find_max_plus_or_multiply(input)
print(result)

계산해야할 숫자에 0 또는 1 이 있다면 더해주고 그 외에 상황은 곱해준다.

 

시간복잡도 : O(N)

 


 

숙제 1. 소수 찾기

 

Q. 정수를 입력 했을 때, 그 정수 이하의 소수를 모두 반환하시오.

 20이 입력된다면, [2, 3, 5, 7, 11, 13, 17, 19] 같이 반환해야 한다.

A.

input = 20


def find_prime_list_under_number(number):
    result_list = []
    for num in range(2, number+1):
        for i in range(2, num):
            if num % i == 0:
                break
        else:
            result_list.append(num)
    return result_list


result = find_prime_list_under_number(input)
print(result)

 

풀이.

입력받은 숫자까지 반복문을 돌며, 해당 숫자 이하의 수로 나누어떨어지는가 판별하여 리스트에 추가해준다.

 

소수의 조건 (코드 개선)

1. 자기 자신과 1 외에는 아무것도 나눌 수 없다.

            if num % i == 0:
                break 

     나눈 나머지가 0이면 리스트에 추가하지않고 다음 for문 진행

2. 2 부터 num 까지 모든수로 나누어 떨어지지 않는지 확인할 필요 없다. => 모든 소수로 나누어 떨어지지않으면 된다.

           for i in range(2, num):    =>    for i in range(2, result_list):

3. 수가 수를 나누면 몫이 발생하게 되는데 몫과 나누는 수, 둘 중 하나는 반드시 N의 제곱근 이하이기 때문에 N이 N의 제곱근보다 크지 않은 어떤 소수로도 나눠지지 않는다.

            if num % i == 0:     =>    if num % i == 0 and i * i <= num: 

 


 

숙제 2. 문자열 뒤집기

Q. 0과 1로만 이루어진 문자열이 주어졌을 때, 이 문자를 모두 0, 혹은 모두 1로 같게 만들어야 한다. 할 수 있는 행동은 연속된 하나의 숫자를 잡고 모두 뒤집는 것 이다. 뒤집는 것은 1을 0으로, 0을 1로 바꾸는 것을 의미한다. 주어진 문자열을 모두 0 혹은 모두 1로 같게 만드는 최소 횟수를 반환하시오.

 

A.

input = "011110"


def find_count_to_turn_out_to_all_zero_or_all_one(string):
    count_to_all_zero = 0
    count_to_all_one = 0

    if string[0] == '0':
        count_to_all_one += 1
    elif string[0] == '1':
        count_to_all_zero += 1

    for i in range(len(string) - 1):
        if string[i] != string[i + 1]:
            if string[i + 1] == '0':
                count_to_all_one += 1
            if string[i + 1] == '1':
                count_to_all_zero += 1

    return min(count_to_all_one, count_to_all_zero)


result = find_count_to_turn_out_to_all_zero_or_all_one(input)
print(result)

 

풀이.

모두 0으로 만들때 뒤집는 횟수를 저장하는 변수

모두 1로 만들때 뒤집는 횟수를 저장하는 변수

 

첫 문자가 0 인지 1인지 부터 판단

 

문자열 for문으로 0번째부터 반복하면서

0에서 1로 변하는 순간 혹은 1에서 0으로 변하는 순간 상황에 맞게 카운트를 올린다.

 

min() : 가장 작은 수를 반환한다.