소수 여부를 판별할 수 있는 함수를 작성해 보자.

참고로 숫자 1은 소수가 아니다!

 

def prime_num_classify(n):
    if n == 1:
        return False
    elif n in [2, 3, 5, 7]:
        return True
    elif n % 2 == 0 or n % 3 == 0:
        return False
    else:
        for i in range(3, n // 2):
            if n % i == 0:
                return False
    return True
 

위 코드는 정상 작동하지만 숫자가 커질수록 비효율성이 커진다. chatGPT는 위 코드를 100점 만점에 50점으로 평가하였으며 이를 개선할 수 있는 90점짜리 최적화 코드를 제공하였다.

 

else 이후 부분을 주목해서 보자.

def prime_num_classify(n):
    if n == 1:
        return False
    elif n in [2, 3, 5, 7]:
        return True
    elif n % 2 == 0 or n % 3 == 0:
        return False
    else:
        i = 5
        while i * i <= n:
            if n % i == 0 or n % (i + 2) == 0:
                return False
            i += 6
    return True
 

Breakdown

    else:
        i = 5

먼저 소수판별을 할 n의 인수(나누어줄 수)로 숫자 5부터 점검하기 시작한다. 왜 5부터 시작할까? 인수로 2, 3은 elif구문에서 이미 판별하였고, 4는 짝수라서 역시 elif구문에서 걸러졌기 때문이다.

        while i * i <= n:

인수의 제곱이 나누어지는 수(n)보다 작은 경우에만 이터레이션을 돌린다. 다르게 말하면, n의 제곱근 정수값 int(n ** 0.5) 보다 i가 작거나 같은 경우에만 이터레이션을 돌린다. 왜 그럴까?

 

We only need to test divisibility by potential factors up to the square root of n. This is because if n has a factor larger than its square root, it must also have a corresponding factor smaller than its square root.

 

우리는 i가 주어진 수(n)의 인수가 되는지, 즉 n이 i로 나누어 떨어지는지를 살펴 보며 소수를 판별한다. 이 때 i값은 n의 제곱근 정수값 int(n ** 0.5)까지만 살펴보면 되는데, 그 이유는 인수와 몫의 관계 때문이다. 아래 예시를 확인해 보면, 나눗셈을 할때 제곱근 정수를 기준으로 인수와 몫이 갈라지게 되는 것을 확인할 수 있다.

 

(예)

  1. 숫자 225의 약수는 [1, 3, 5, 9, 15, 25, 45, 75, 225]이다. 225의 제곱근 정수는 15이다. 15를 기준으로(225는 완전제곱수) 9 * 25 = 225와 같이 인수(9)와 몫(25)이 갈라지는 것을 확인할 수 있다.
  2. 숫자 108의 [1, 2, 3, 4, 6, 9, 12, 18, 27, 36, 54, 108] 이다. 108의 제곱근 정수는 10이다. 10을 기준으로 9 * 12 = 108과 같이 인수(9)와 몫(12)이가 갈라지는 것을 확인할 수 있다.

 

따라서 제곱근 정수 이하의 숫자에 인수가 있는지만 확인하면 계산을 단축하여 효율성을 높일 수 있는 것이다. (9 * 12 = 108, 12 * 9 = 108과 같은 중복 계산을 제외하게 되는 것.)

 

만약 n = 119일 경우를 생각해 보자. i = 5부터 시작하면, i * i = 25이므로 True가 되어 if문을 살펴보게 된다.

            if n % i == 0 or n % (i + 2) == 0:
                return False
            i += 6
 

119은 5로 나누어지지 않는다.

119는 7로 나누어 떨어진다. (몫 : 17)

따라서 119는 소수가 아니므로 False를 리턴하고 함수가 종료된다.

 

그렇다면 if문에서는 왜 i + 2만 함께 점검하는 걸까?

  1. 우리가 점검해 줄 i값은 5에서 시작하여 2씩 늘려주면 된다. 왜냐면 홀수값만 판별하면 되니까. 주어진 숫자 n이 짝수로 나누어 떨어지는지는 그 전에 이미 확인을 했다. (elif 구문)
  2. i + 1, i + 3, i + 5 모두 짝수이기 때문에 확인할 필요가 없다.
  3. 그럼 i + 4는 왜 안해도 될까? i + 4의 경우 확인해 보면 3의 배수가 되는 것을 알 수 있다. 따라서 i + 6을 해준 다음 이터레이션을 돌리는 것이다. 이 과정을 통해 계산 시간을 줄이고 효율성을 높이게 되었다.

 

 

ver 01 (메모화 x)

def factor_extractor(n):
    if n == 1:
        return [1]
    if n == 2:
        return [1, 2]
    if n == 3:
        return [1, 3]
        
    factor_list = [1]
    for num in range(2, n):
        if n % num == 0:
            factor_list.append(num)
    factor_list.append(n)
    return factor_list
 

문제점 : 반복되는 계산

개선 방법 : 메모화(Memorization)

 

 

ver 02 (메모화 o)

def factor_extractor(n, memo = {1 : [1], 2 : [1, 2], 3 : [1, 3]}):
    if n in memo:
        return memo[n]

    factor_list = [1, n]
    for num in range(2, int(n**0.5) + 1):
        if n % num == 0:
            factor_list.extend([num, n // num])

    memo[n] = factor_list
    return sorted(list(set(factor_list)))
 
  1. factor list에 인수(num)와 몫(n//num)을 함께 추가하므로 제곱근(n**0.5의 소숫점을 버린 정수값)까지만 이터레이션을 돌려서 계산을 줄일 수 있다.
  2. 마지막 리스트를 리턴할 때 set을 해 주는 이유 : n이 완전제곱수인 경우 중복 숫자가 존재한다. (예 - n이 121인 경우 factor_list에 11이 두 번 들어가므로 하나를 제거해주기 위함이다.)

 

이렇게 메모화를 해 두면 이미 한 번 했던 계산을 다시 할 필요가 없으므로 훨씬 효율적이다.

# First call with memoization
factor_extractor(10)
# Output: Calculating factors [1, 2, 5, 10]

# Second call with memoization
factor_extractor(10)
# Output: Using cached result [1, 2, 5, 10]
 

 

 

 

 

가끔 코딩 테스트에서 이거.. 리스트를 평탄화해주면 쉽게 풀 수 있겠는데? 라는 생각이 들 때가 있습니다. 프로그래머스 0단계에서는 이 평탄화 함수를 작성하는 것 자체가 문제로 출제되기도 했었던거 같기도 하구요. 어쨌든 알고 있으면(또는 외워 두면) 정말 너무나!!!! 큰 도움이 되는!!!!! 평탄화 함수 작성하는 방법!!! 정리해 봅시다.

 

* 설명 없이 함수만 빠르게 확인하고 싶으시다면 글 맨 아래로 가세요! *

 

 

 

 

먼저, 중첩 리스트란 무엇일까요?

normal_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
weird_list = [1, 2, [3], 4, 5, [6], 7, [8, 9, 10]]
crazy_list = [1, 2, [[3], 4, 5], [[6], 7, [8, [9, [10]]]]]

위 세 가지 리스트를 살펴 봅시다.

 

먼저 멀쩡한 리스트 normal_list는 참 보기 좋습니다.

그런데 weird_list는 리스트 안에 리스트를 원소로 가지고 있네요. [3], [6], [8, 9, 10]과 같이 말이죠. 이런 놈들을 중첩 리스트라고 부르겠습니다. 그래도 여기까진 어찌저찌 해체해볼 수 있을 것 같습니다.

 

그런데 crazy_list.. 이 미친놈을 보니까 리스트 안에 리스트가 또 리스트를 가지고 있어요. 중첩에 중첩에 중첩에 중첩에 중...!!!

 

 

평탄화란? (Flatten)

weird_list, crazy_list, crazy_list_and_tuple처럼 중첩된 리스트를

normal_list처럼 1차원의 리스트로 평평(Flat)하게 평탄화(Flatten)해주는 작업을 의미합니다.

 

만약 우리가 리스트 안에 있는 모든 값들 중에서 가장 큰 값을 구해야 된다면 어떻게 할까요?

normal_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
max(normal_list)
# 10

crazy_list = [1, 2, [[3], 4, 5], [[6], 7, [8, [9, [10]]]]]
max(crazy_list)
# ????????

1차원 리스트인 normal_list의 경우 내장함수 max()를 사용하면 아주 쉽게 최댓값을 구해낼 수 있지만, 우리의 미친놈 crazy_list는 그렇게 호락호락하지가 않습니다. 

 

 

평탄화 함수 작업하기

리스트 안에 리스트가 들어가 있는 중첩 리스트를 하나의 리스트로 평탄화(Flatten)하는 회귀함수를 작성해 봅니다. 먼저 weird_list를 가지고 생각을 열어 보겠습니다.

weird_list = [1, 2, [3], 4, 5, [6], 7, [8, 9, 10]]

def easy_flatten(nested_list):
    result = []

    for element in nested_list:
        if isinstance(element, list):
            result.extend(element)
        else:
            result.append(element)

    return result
    
easy_flatten(weird_list)
# [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

 

weird_list의 원소를 하나씩 점검합니다. 만약 원소가 리스트라면 result에 그 리스트를 extend해서 합쳐 주고, 원소가 리스트가 아니라면 해당 원소를 append를 이용해서 result에 추가해 줍니다. weird_list가 예쁘게 평탄화된 것을 확인할 수 있습니다.

 

 

그렇다면 easy_flatten 함수를 가지고 crazy_list도 평탄화 작업이 될까요?

crazy_list = [1, 2, [[3], 4, 5], [[6], 7, [8, [9, [10]]]]]
easy_flatten(crazy_list)
# [1, 2, [3], 4, 5, [6], 7, [8, [9, [10]]]]

 

우리가 원하는 결과가 나오지 않았습니다. 중첩에 중첩이 여러번 반복되는 경우를 생각해주지 못했기 때문입니다. 그렇다면 여러 개의 중첩을 풀어주는 함수를 어떻게 작성할 수 있을까요? 중첩이 몇 번 반복되는지 알 수 없다는 점에 착안하여, 재귀함수(recursion)를 작성해 보겠습니다.

 

 

재귀함수를 적용한 Flatten 함수

def hard_flatten(nested_list):
    result = []

    for element in nested_list:
        if isinstance(element, list):
            # 유일하게 달라진 부분 - element 대신 hard_flatten(element)❤️
            result.extend(hard_flatten(element))
        else:
            result.append(element)

    return result

 

만약 nested_list의 element가 list 객체라면 result에 hard_flatten(element)를 추가해주도록 재귀함수가 작성되었습니다. 이렇게 여러 번 중첩이 반복되는 경우 자기 자신을 호출함으로써 문제를 간단히 해결할 수 있게 되었습니다.

crazy_list = [1, 2, [[3], 4, 5], [[6], 7, [8, [9, [10]]]]]
hard_flatten(crazy_list)
#[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

 

우리가 원하는 결과값을 얻었네요!

이 리스트 평탄화 함수는 여러 번 작성해보시면서 외워 두시면 필요할 때 요긴히 사용하실 수 있을거에요.

 

 

(적용 예시)

 
# 리스트 k에서 하트를 모두 제거한 1차원 리스트를 출력하세요
k = [['a', 'b', ['🩶', 'd']], 'e', 'f', '🩶', [['🩶'], 'i']]

[element for element in hard_flatten(k) if element != '🩶']
# ['a', 'b', 'd', 'e', 'f', 'i']
 

 

감사합니당!

 

 

 

ASAC 빅데이터 분석가 과정 5기 1, 2주 차가 지나고 어느새 4월 1일을 앞두고 있습니다. 3월 26일이 지나면 본 교육과정에 완전 등록이 되어 KDT교육과정을 1회 수료한 것이 되기 때문에, 과정이 맘에 들지 않는다면 그전까지 반드시 드롭을 해야 했는데요. 저는 드롭하지 않고  아삭 5기 동기들과 함께 빅데이터 분석가 교육과정을 끝까지 듣기로 결정했습니다.

 

 

이유는 다음과 같습니다.

  1. 준비된 강의와 세미나가 생각보다 도움이 됩니다. 1, 2주차에 마련된 현직자 및 전 기수 선배들의 특강, 커리어무브 대표의 자기 소개서와 포트폴리오 작성법 강의는 앞으로 6개월간 어떤 전략으로 나를 스토리텔링할 것인지에 대해 생각해 볼 수 있는 좋은 기회가 되었습니다. 파이썬 강의는 파이썬 주요 문법을 리뷰하고 코딩 테스트에 필요한 기본 알고리즘을 배울 수 있어 굉장히 알찼습니다.
  2. 집에서 혼자 공부할 때보다 절대적인 공부 시간이 늘었습니다. 나태해지지 않고 공부에 더 잘 집중할 수 있는 환경이 주어지는 점이 마음에 듭니다. 또, 공덕 프론트원 시설이 아주 잘 돼 있어서 만족스럽습니다.
  3. 같은 방향성을 가진 사람들과 함께할 수 있게 되면서 심리적으로 안정감이 생겼습니다. 동기들의 눈빛이 살아 있고 열정이 넘쳐 건강한 자극을 받습니다. 아직 모두와 친해지지는 못했지만 앞으로 더 많이 알아가고 싶습니다.
  4. 혼자서 가장 막막했었던 프로젝트와 포트폴리오 제작에 큰 도움을 받을 수 있을 것으로 보입니다. 강사님들의 조언 덕분에 벌써부터 관심 분야를 계속해서 서칭하고 주제에 대해서 생각해보는 등 마음가짐이 달라졌습니다. 특히 기업연계 프로젝트에 참여할 수 있다는 점을 가장 기대하고 있습니다.

 

저는 이번 아삭 과정의 목표를 나를 보여줄 수 있는 나만의 포트폴리오를 완성하는 것, 그리고 나를 어떻게 간지나게 스토리텔링할지 전략을 세우는 것으로 잡았습니다. 

 

앞으로 다가올 개인 프로젝트 주제에 대해서 벌써부터 심사숙고 하고 있습니다. 프로젝트는  내가 정말 다루고 싶은 주제를 찾는 것이 가장 중요합니다. 취업 과정에서 고민과 정성을 담은 포트폴리오는 면접관에게 조금 더 어필될 수밖에 없습니다. 이 때 물론 여러 사람들이 공통적으로 다루는 흔한 주제보다는, 나만의 경험과 특색을 드러낼 수 있는 독창적인 주제가 도움이 될 것입니다. 이러한 주제와 인사이트는 꾸준히 고민해야 얻어집니다. 주제 선정 이후에도 어떻게 데이터를 수집할 것인지, 원하는 데이터가 없을 때 어떻게 대처할 것인지 등에 대한 현실적인 이슈가 있으니 그에 대한 고민 역시 함께해야 합니다. 저는 7년간 몸담았던 교육계와 관련덴 도메인을 주제삼아 에듀테크 필드에 어필을 해볼지, 아니면 시원하게 새로운 업계를 파고들 지부터 먼저 정해야 할 것 같습니다.

 

대부분의 강사님들께서 공통적으로 강조한 것이 있습니다. 바로 꾸준함의 힘, 그리고 기록의 중요성입니다. 저는 계속해서 고민의 흔적을 꼼꼼하게 기록하고 집요하게 파고들겠습니다. 

 

 

안녕하세요, 데이터 사이언티스트를 꿈꾸는 수리링입니다.

저는 2024년 내일 배움 카드 KDT 훈련과정인 ASAC 빅데이터 분석 과정 5기에 지원하고 합격하였습니다. 2024년 3월 20부터 과정이 시작되었는데요. 평소에 노션으로 공부 내용을 정리하고 관리해 왔지만, 모두에게 더 쉽게 공유할 수 있는 자료가 되었으면 하는 마음에 이번 과정의 회고는 블로그에 남기려고 합니다.

SK플래닛에서 주관하는 ASAC 빅데이터 분석 과정은 데이터 사이언스와 인공지능 분야까지 배울 수 있는 오프라인 교육과정입니다. 매니저님께 질문해 보니 이번 5기 모집은 어느 때보다 경쟁률이 치열했다고 합니다. 총 28명을 모집하는 데 약 150명 이상의 지원자가 몰렸다고 하는데요. 오프라인 면접과 코딩 테스트를 통해 선별이 된 인원이 모인 만큼 동기들과 함께 좋은 커뮤니티를 형성할 수 있을 거라는 큰 기대를 가지고 있습니다.


 

[1] 지원 계기

나중에 따로 제 소개 글을 써볼 텐데, 먼저 간단히 말씀드리면 저는 공대를 나오지 않은 비전공자입니다. 2023년 11월 말부터 최근까지 약 3개월간 독학으로 코드를 공부했습니다. 첫 시작으로 파이썬 책을 통해 기초 문법을 배웠는데요. 이후로는 주로 유데미에서 인터넷 강의를 들었습니다. 나중에 유데미 강의 후기도 적어보겠습니다. 어쨌든 인터넷에 양질의 자료가 많아 혼자서 공부하는 데 큰 무리는 없었다는 말씀을 드리고 싶습니다.

공부가 참 재밌더라고요. 솔직한 심정으로는 계속해서 자기주도적으로, 혼자! 학습하고 싶었습니다. 저는 평소에도 개인주의적 성향이 강한 편이거든요. 하지만 현실적으로 접근하기로 했습니다. 언젠가 취업을 해야 한다면 나와 비슷한 동료들과 커뮤니티를 형성하여 자극받고 성장할 수 있는 환경을 조성하는 것이 좋을 테니까요. 또 팀 프로젝트를 진행하고 포트폴리오를 작성하기 위해서는 개인플레이로는 분명한 한계가 있겠다고 판단이 되었습니다. 그래서 KDT 국비 교육과정을 탐색하기 시작했습니다.

여러 가지 프로그램 중에서 ASAC 빅데이터 과정을 고른 이유는 다음과 같습니다.

  1. 눈살이 찌푸려지는 과대광고를 하지 않는다.
    • 어느 정도 사회생활을 해 보셨다면 다들 아시겠지만, 이런 정부 사업은 돈이 됩니다. 그래서 다양한 기관들이 비슷한 교육과정을 만들고 진행하고 있어요. 대부분의 프로그램들은 지원자를 모집하기 위해 광고를 합니다.
    • 저는 너무 과하게 공격적으로 광고하는 기관들을 선택지에서 배제했습니다. 다들 먹고 살자고 하는 것은 맞긴 하지만, 그래도 본질을 잊고 수익 창출에만 혈안이 된 단체를 신뢰할 수는 없었어요. 내 소중한 6개월!
  2. 온라인이 아닌 9-6 오프라인 교육과정이다.
    • 사람에 따라 온라인 교육을 선호할 수는 있겠습니다만, 저는 온라인 교육과정은 전부 배제했습니다. 저는 경기 외곽에 살기때문에 서울에 있는 어느 과정에 등록해도 출퇴근이 편도 1시간 이상 걸립니다. 그럼에도 불구하고 무조건 오프라인을 원했습니다.
    • 온라인 교육이랍시고 미리 찍어둔 동영상 대충 보게 할 거면, 유데미 강의 듣는 게 낫다고 생각했습니다.
    • 오프라인 과정만이 줄 수 있는 긴장감, 리듬감, 커뮤니티를 원했습니다.
  3. 선발 과정에 면접, 코딩 테스트가 있다.
    • 선별된 인원과 함께할 수 있으니까요! 정말 중요하게 생각했던 부분입니다.
    • 형식적으로나마 면접, 코딩테스트가 있지만 사실상 필터링 없이 누구나 들어갈 수 있는 교육과정은 배제했습니다.

​이러한 이유로 ASAC을 고르게 되었어요. 그렇다고 해서 이 과정에 엄청난 확신이 있었던 건 아니긴 했습니다만, 그래도 느낌이 제일 괜찮았습니다. (...)

 

[2] 면접, 코딩 테스트 후기

테스트는 간단한 파이썬 기본 문법 테스트로 이루어졌습니다. 구글 시트에 설문 형태로 문제가 주어졌고요. 프로그래머스나 리트 코드 같은 코테 문제 풀이는 안 했습니다. 정말 기초 기초 쌩 기초 문법이 전부였어서 평소 코드를 쓰고 공부하는 사람이었으면 틀릴 수가 없는... 그런 쉬운 문제들로만 구성이 되었습니다. 그래서 쉽게 풀었고요. 아마 합격한 분들은 테스트 문제들 거의 다 맞히지 않았을까? 싶어요.

면접은 2인 1조로 봤습니다. 면접관은 두 분이셨고요. 미리 제출했던 지원서를 보면서 이것저것 질문하셨고, 저는 '공부를 혼자서 체계적으로 잘 하고 계시는데 우리 과정에 지원한 이유는 무엇인지', '집이 먼데 출퇴근이 어렵지는 않겠는지', 등등 어려운 질문은 딱히 안 하셔서 솔직하게 답변드리고 잘 마무리했습니다. 

 

[3] 지원서에는 무엇을 썼는가

문항이 세세하게 기억나지는 않습니다만. 다음과 같은 내용을 적었던 것이 기억납니다.

  1. 이직을 결심하게 된 이유, 그리고 데이터 분석에 관심을 가지게 된 이유
  2. 지금까지 혼자서 공부해온 내용 나열
  3. 앞으로 프로그램에 어떤 자세로 얼마나 열심히 참여할 것인지 나의 열정과 포부 보여주기

공부한 내용은 다음과 같이 작성했습니다.

  1. 파이썬
  2. HTML, CSS, JavaScript, Booststrap 5
  3. 파이썬 Numpy, Pandas, Matplotlib, Seaborn 라이브러리
  4. R
  5. MySQL
  6. 깃/깃허브, Linux Commandline
  7. 파이썬 Tensorflow 
  8. 해커 랭크, 프로그래머스 코딩 테스트 병행 중

블로그나 깃허브, 노션 등 개인용 포트폴리오가 있으면 주소를 적으라고 해서 저의 개인 노션 페이지 주소를 첨부했습니다. 저는 노션에 지금까지 제가 공부한 모든 내용을 전부 깔끔하게 정리하고 있습니다. 정리를 하면서 공부하는 것과 아닌 것에는 하늘과 땅만큼의 차이가 있더라고요. 특이한 점은 모든 정리를 영어로 하고 있다는 건데, 아무래도 유데미 강의를 들으며 평소 영어로 공부하다 보니 자연스럽게 그렇게 되었습니다. 이게 어필이 되었을지는 모르겠습니다.

[4] 마무리

비록 경쟁을 통해 선발되어 학생의 입장으로 과정에 임하게 되었긴 하지만, 저 또한 이들이 준비한 교육과정을 평가하는 동등한 관계에 있다고 생각합니다. 그래서 교육에 참여하는 동안 누구보다 열심히 하되, 독학이 낫겠다는 확실한 판단이 서는 순간에는 가차없이 중도 하차할 생각도 하고 있어요. 변태 같지만.... 그럴 일이 없도록 탄탄하고 만족스러운 과정으로 저를 웃고 울게 해 주었으면 좋겠네요 아삭! ^_^

 

앞으로 매주 솔직한 회고를 올리겠습니다 :)

+ Recent posts