알고리즘 연습문제 21선_(2)

jaam._.mini·2023년 11월 24일
0

📒Python 기초 수학

목록 보기
45/46

파이썬 마지막 과정인 만큼! 열심히 따라가 보자..!




최소값


1

  • 모듈 : minMod.py
class MinAlgorithm:

    def __init__(self, ns):
        self.nums = ns
        self.minNum = 0
        self.minNumCnt = 0
    #최소값 설정 함수
    def setMinNum(self):
        self.minNum = 51

        for n in self.nums:
            if self.minNum > n:
                self.minNum = n
    #외부에서 최소값을 가져갈 수 있게 하는 함수
    def getMinNum(self):
        self.setMinNum()

        return self.minNum
    #최소값의 갯수 파악 함수
    def setMinNumCnt(self):
        self.setMinNum()

        for n in self.nums:
            if self.minNum == n:
                self.minNumCnt += 1
    #최소값의 갯수를 외부로 가져갈 수 있게 하는 함수
    def getMinNumCnt(self):
        self.setMinNumCnt()

        return self.minNumCnt
  • 실행 : ex.py
import random
import minMod

if __name__ == '__main__':

    nums = []
    for n in range(30):
        nums.append(random.randint(1, 50))

    print(f'nums:\n{nums}')
    ma = minMod.MinAlgorithm(nums)
    print(f'min num: {ma.getMinNum()}')
    print(f'min num cnt: {ma.getMinNumCnt()}')

nums:
[31, 36, 29, 9, 24, 46, 43, 47, 10, 27, 41, 11, 39, 26, 39, 48, 4, 37, 35, 14, 38, 35, 28, 43, 18, 7, 11, 27, 5, 5]
min num: 4
min num cnt: 1



2. module.py + class.py


  • 모듈 : minMod.py
def getAvg(ns): # 평균
    total = 0
    for n in ns:
        total += n

    return total / len(ns)


def getMin(ns): # 최소값
    minN = ns[0]
    for n in ns:
        if minN > n:  # 최소값이 아니니까
            minN = n # 재설정

    return minN

def getDeviation(n1, n2): # 편차
    return round(abs(n1 - n2), 2)


def getMaxOrMin(ns, maxFlag=True): # 최소& 최대값 한번에 받기
    resultN = ns[0] # 최대값(maxFlag=True) 이나 최소값(maxFlag=False)을 반환해주는 역할
    for n in ns: # 최대값
        if maxFlag:
            if resultN < n:
                resultN = n

        else: # 최소값
            if resultN > n:
                resultN = n

    return resultN
  • class: mod2.py
# 최대값, 최소값, 평균, 편차를 class로 만듬

class ScoreManagement:

    def __init__(self, ss): #초기화
        self.scores = ss #점수
        self.score_tot = 0 # total
        self.score_avg = 0
        self.score_min = 0
        self.score_max = 0

    def getMinScore(self): # 최소값

        # = scores가 초기화가 안됐다면,
        if self.scores == None or len(self.scores) == 0:
            # None을 반환 (에러 방지)
            return None

        self.score_min = self.scores[0]
        for score in self.scores:
            if self.score_min > score:
                self.score_min = score

        return self.score_min

    def getMaxScore(self):
        if self.scores == None or len(self.scores) == 0:
            return None

        self.score_max = self.scores[0]
        for score in self.scores:
            if self.score_max < score:
                self.score_max = score

        return self.score_max

    def getTotScore(self):
        if self.scores == None or len(self.scores) == 0:
            return None

        self.score_tot = 0
        # 초기화 해주지 않으면, 호출 할때 마다 이전 값이 누적되어 더해지는 에러 발생

        for score in self.scores:
            self.score_tot += score

        return self.score_tot

    def getAvgScore(self):
        if self.scores == None or len(self.scores) == 0:
            return None

        self.score_avg = round(self.getTotScore() / len(self.scores), 2)
        return self.score_avg

    def getMaxDeviation(self):
        result = abs(self.getAvgScore() - self.getMaxScore())
        return round(result, 2)

    def getMinDeviation(self):
        result = abs(self.getAvgScore() - self.getMinScore())
        return round(result, 2)
  • 실행 : ex.py
import minMod

scores = [100, 64, 94, 66, 75, 58, 99, 76, 96, 74,
          54, 73, 88, 70, 68, 50, 95, 89, 69, 98]

scores_avg = minMod.getAvg(scores)
scores_min = minMod.getMaxOrMin(scores, maxFlag=False)
# maxFlag=False : 최소값 (minMod #22번 참고)
deviation = minMod.getDeviation(scores_avg, scores_min)

print(f'scores_avg: {scores_avg}')
print(f'scores_min: {scores_min}')
print(f'deviation: {deviation}')

import mod2

sm = mod2.ScoreManagement(scores)
print(f'score_avg: {sm.getAvgScore()}')
print(f'score_min: {sm.getMinScore()}')
print(f'score_max: {sm.getMaxScore()}')
print(f'score_min_deviation: {sm.getMinDeviation()}')
print(f'score_max_deviation: {sm.getMaxDeviation()}')

scores_avg: 77.8
scores_min: 50
deviation: 27.8
score_avg: 77.8
score_min: 50
score_max: 100
score_min_deviation: 27.8
score_max_deviation: 22.2




최빈값


얼마나 많은 빈도로 있는지 확인하는 방법
1. 최대값을 먼저 구한다
2. 최대값을 기준으로 0으로 나열된 리스트를 만들고
3. 각 수의 빈도를 적용한다

1.


  • 최댓값 class : maxMod.py
class MaxAlgorithm:

    def __init__(self, ns): # __init__ method는 숫자를 받기위해 ns로 구성
        self.nums = ns # 초기화
        self.maxNum = 0 # 초기화
        self.maxNumIdx = 0 # 초기화

    def setMaxIdxAndNum(self): # 최대값 설정
        self.maxNum = 0 # 데이터
        self.maxNumIdx = 0 # 인덱스

        # ★ 반복문을 돌면서 최대값 & 최대값의 idx 구하기 ★
        for i, n in enumerate(self.nums): #i인덱스 하고, n데이터 하고
            if self.maxNum < n: #작으면 최대값이 아니니까, 재설정이 필요
                self.maxNum = n # 재설정
                self.maxNumIdx = i # 인덱스도 재설정

    def getMaxNum(self):
        return self.maxNum

    def getMaxNumIdx(self):
        return self.maxNumIdx
  • 모듈 class: modMod.py
import maxMod

class ModeAlgorithm:

    def __init__(self, ns, mn): # ns 나이, mn 가장큰 숫자
        self.nums = ns
        self.maxNum = mn
        self.indexes = []

    #인덱스 리스트 생성 및 빈도 저장
    def setIndexList(self):
        self.indexes = [0 for i in range(self.maxNum + 1)]
        # ▲ data를 '0'으로 하는 리스트 만들기

        for n in self.nums:
            self.indexes[n] = self.indexes[n] +1

    def getIndexList(self):
        if sum(self.indexes) == 0: #에러발생 방지
            return None
        else:
            return self.indexes

    def printAges(self):
    # ▲ 그래프를 출력하는 함수

        n = 1
        while True:

            maxAlo = maxMod.MaxAlgorithm(self.indexes)
            maxAlo.setMaxIdxAndNum()
            maxNum = maxAlo.getMaxNum()
            maxNumIdx = maxAlo.getMaxNumIdx()

            if maxNum == 0: #에러 방지
                break

            print(f'[{n:0>3}] {maxNumIdx}세 빈도수: {maxNum}\t', end='')
            print('+' * maxNum)
            self.indexes[maxNumIdx] = 0

            n += 1 # 무한반복에 빠지지 않도록
  • 실행 : ex.py
import maxMod # 최대값 알고리즘(class로 구현)
import modMod

ages = [25, 27, 27, 24, 31, 34, 33, 31, 29, 25,
        45, 37, 38, 46, 47, 22, 24, 29, 33, 35,
        27, 34, 37, 40, 42, 29, 27, 25, 26, 27,
        31, 31, 32, 38, 25, 27, 28, 40, 41, 34]

print(f'employee cnt: {len(ages)}명')

maxAlg = maxMod.MaxAlgorithm(ages)
maxAlg.setMaxIdxAndNum()
maxAge = maxAlg.getMaxNum() # 나이가 가장 큰 숫자
print(f'maxAge: {maxAge}세')

modAlg = modMod.ModeAlgorithm(ages, maxAge)
modAlg.setIndexList()
print(f'IndexList: {modAlg.getIndexList()}')

modAlg.printAges()

employee cnt: 40명
maxAge: 47세
IndexList: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 4, 1, 6, 1, 3, 0, 4, 1, 2, 3, 1, 0, 2, 2, 0, 2, 1, 1, 0, 0, 1, 1, 1]
[001] 27세 빈도수: 6	++++++
[002] 25세 빈도수: 4	++++
[003] 31세 빈도수: 4	++++
[004] 29세 빈도수: 3	+++
[005] 34세 빈도수: 3	+++
[006] 24세 빈도수: 2	++
[007] 33세 빈도수: 2	++
[008] 37세 빈도수: 2	++
[009] 38세 빈도수: 2	++
[010] 40세 빈도수: 2	++
[011] 22세 빈도수: 1	+
[012] 26세 빈도수: 1	+
[013] 28세 빈도수: 1	+
[014] 32세 빈도수: 1	+
[015] 35세 빈도수: 1	+
[016] 41세 빈도수: 1	+
[017] 42세 빈도수: 1	+
[018] 45세 빈도수: 1	+
[019] 46세 빈도수: 1	+
[020] 47세 빈도수: 1	+



2. 로또 번호

  • 모듈 : mod.py
class LottoMode:

    def __init__(self, ln):
        self.lottoNums = ln
        self.modeList = [0 for n in range(1, 47)] # 빈도수 리스트 : 로또 번호를 모두 스캔

    def getLottoNumMode(self):  # 최빈도 함수()

        for roundNums in self.lottoNums:# roundNums : 회차 번호
            for num in roundNums:
                self.modeList[num] = self.modeList[num] + 1
                # ▲ "최빈도 알고리즘"의 ★핵심★ 문장
                # self.modeList에 있는 [num]이 얼마나 출현했는지 확인하는 문장

        return self.modeList

    def printModeList(self):
        # ▲함수는 'getLottoNumMode'가 한번 실행되서 세팅이 완료되어 있어야 사용 가능
        # (빈도수가 저장이 되어 있어야 함)
        if sum(self.modeList) == 0:
        # 아이템의 모든 값을 더했더니 0이다
        # = 즉, getLottoNumMode 가 호출이 되어 있지 않은 상태
        # = 즉, 여기서 출력하는 의미가 없음
            return None
            # 이런 경우라면 None으로 작업이 되어 있지 않음을 표시

        # self.modeList(빈도수 리스트)에서 숫자와 빈도수를 파악하겠다
        for i, m in enumerate(self.modeList):
            if i != 0:
            # 0이 아닌 경우,
            # 로또는 인덱스 '0'은 의미가 없음
            # 따라서, i가 0이 아니라면
                print(f'번호: {i:>2}, 빈도: {m}, {"*" * m}')
                # 출력해라
  • 실행 : ex.py
import mod

lottoNums = [[13, 23, 15, 5, 6, 39], [36, 13, 5, 3, 30, 16], [43, 1, 15, 9, 3, 38],
             [32, 42, 24, 45, 8, 31], [18, 39, 41, 11, 4, 9], [12, 39, 11, 38, 32, 5],
             [29, 25, 13, 6, 14, 8], [21, 33, 19, 20, 42, 7], [6, 28, 3, 45, 41, 24],
             [42, 15, 8, 5, 35, 4], [14, 4, 35, 24, 29, 3], [15, 20, 6, 37, 34, 39],
             [27, 5, 32, 15, 25, 19], [45, 25, 2, 8, 30, 43], [4, 19, 33, 10, 6, 24],
             [25, 26, 45, 23, 24, 16], [33, 28, 45, 21, 38, 24], [4, 30, 29, 28, 32, 38],
             [11, 28, 12, 2, 42, 3], [40, 29, 16, 8, 9, 28], [6, 9, 37, 30, 3, 35],
             [29, 18, 41, 28, 38, 15], [9, 31, 13, 44, 1, 36], [36, 1, 37, 32, 15, 12],
             [41, 32, 16, 6, 26, 33], [12, 43, 10, 29, 39, 9], [41, 9, 23, 35, 18, 17],
             [35, 38, 3, 28, 36, 31], [21, 44, 4, 29, 18, 7], [20, 23, 6, 2, 34, 44]]

lm = mod.LottoMode(lottoNums)

mList = lm.getLottoNumMode()
# 최빈도 알고리즘을 통해 어떤 숫자가 가장 많이 나왔는지 보겠따

lm.printModeList()

번호:  1, 빈도: 3, ***
번호:  2, 빈도: 3, ***
번호:  3, 빈도: 7, *******
번호:  4, 빈도: 6, ******
.
.
.
번호: 43, 빈도: 3, ***
번호: 44, 빈도: 3, ***
번호: 45, 빈도: 5, *****




근삿값


나와 가장 가까이에 있는 데이터가 어떤 것인지 찾는 것

1.

  • 모듈 : nearMod.pt
class NearAlgorithm:

    def __init__(self, d):
        self.temp = {0:24, 5:22, 10:20, 15:16, 20:13, 25:10, 30:6}
        # 딕셔너리로 관리

        self.depth = d
        self.nearNum = 0
        self.minNum = 24
        #가장 큰 숫자로 초기화 함

    def getNearNumber(self):
    # 외부에서 근사값을 가져오는 함수

        for n in self.temp.keys():
            absNum = abs(n - self.depth)
            # n과 내가 입혁한 수심(self.depth)의 차이를 구한다

            if absNum < self.minNum:
                self.minNum = absNum
                self.nearNum = n
            # 근사값이 구해졌으니까 반환 해야 함

        return self.temp[self.nearNum]
        # 반환 때, 근사값에 해당되는 "수온"을 반환 해줌
  • 실행 : ex.py
import nearMod

depth = int(float(input('깊이 : '))) # 소수점도 입력이 되게끔 float로 캐스팅
print((f'깊이 : {depth}m'))

na = nearMod.NearAlgorithm(depth) # 근사값
temp = na.getNearNumber() # 현재 깊이에 대한 수온
print(f'water temperature: {temp}도')

깊이 : 7
깊이 : 7m
water temperature: 22도



2.

  • 모듈 : nearMod.py
class BmiAlgorithm:

    def __init__(self, w, h):
        self.BMISection = {18.5:['저체중', '정상'],
                           23:['정상','과체중'],
                           25:['과체중', '비만']}
        self.userWeight = w
        self.userHeight = h
        self.userBMI = 0
        self.nearNum = 0
        self.userCondition = ' ' # 문자열('') 로 표시
        self.minNum = 25 # 근사값 계산에 필요한 최대값 25로 기본적으로 설정

    def calculatorBMI(self):
        self.userBMI = round(self.userWeight / (self.userHeight * self.userHeight), 2)
        print(f'userBMI ; {self.userBMI}')


    def printUserCondition(self):
        for n in self.BMISection.keys():
            absNum = abs(n - self.userBMI) # 나의 값을 뺴줌 (나의 값 = self.userBMI)
            if absNum < self.minNum:
                self.minNum = absNum
                self.nearNum = n # 근삿값 을 얻어 냄

        print(f'self.nearNum: {self.nearNum}')

        if self.userBMI <= self.nearNum:
            self.userCondition = self.BMISection[self.nearNum][0]
        else:
            self.userCondition = self.BMISection[self.nearNum][1]
        print(f'self.userCondition: {self.userCondition}')
  • 실행 : ex.py
import nearMod

uWeight = float(input('input weight(Kg): '))
uHeight = float(input('input height(m): '))

na = nearMod.BmiAlgorithm(uWeight, uHeight) #객체 출력
na.calculatorBMI() # BMI 지수 출력
na.printUserCondition()

input weight(Kg): 52.7
input height(m): 1.70
userBMI ; 18.24
self.nearNum: 18.5
self.userCondition: 저체중

나의 스펙을 넣어보니.. 저체중...?
이상하네...🤔




재귀 알고리즘


내가 나를 호출하는 방식

  • 하노이의 탑
  • !(팩토리얼)

1. 재귀함수 활용

sales = [12000, 13000, 12500, 11000, 10500, 98000,
         91000, 91500, 10500, 11500, 12000, 12500]

def salesUpAndDown(ss):

    if len(ss) == 1:
    # 길이가 1개 밖에 없다 = 반복해서 끝까지 간 것.
        return ss
        #끝까지 갔기 때문에 더이상 반복되면 안되니까, return

    print(f'sales: {ss}')
    currentSale = ss.pop(0)
    # 이번달 : 인덱스 0에 있는 값(12000)을 제거한다
    nextSales = ss[0]
    # 다음달  : ss[0] = 13000 을 받겠다 (currentSale에서 12000을 삭제 했기때문)
    increase = nextSales - currentSale
    #증가/감소 값이 궁금? : 다음달 - 이번달

    #감소는 알아서 -가 나오지만, 증가는 +가 생략됨
    #따라서 +가 출력될 수 있도록 조건문 설정
    if increase > 0:
        increase = '+' + str(increase)
    print(f'매출증감액 : {increase}')

    return salesUpAndDown(ss)

if __name__ =='__main__':
    # main이면 실행해라
    salesUpAndDown(sales)
    # salesUpAndDown을



2. 재귀함수 활용 - 더하기

  • 모듈: mod.py
class NumsSum:

    def __init__(self, n1, n2):
        self.bigNum = 0
        self.smallNum = 0
        self.setN1N2(n1, n2) # n1, n2 자동 초기화

    def setN1N2(self, n1, n2):
        self.bigNum = n1
        self.smallNum = n2

        if n1 < n2:
            self.bigNum = n2
            self.smallNum = n1

    def addNum(self, n): # 특정 숫자까지의 합 구하는 문장
        if n <= 1: # 0직전 까지 온 것
            return n # 더이상 반복하면 안되니까 반환

        return n + self.addNum(n - 1)
        # 자기 자신을 다시 호출
        # 9면, 9 + 8

    def sumBetweenNums(self):
        
        return self.addNum(self.bigNum - 1) - self.addNum(self.smallNum)
        # 10이면, (9까지의 합) - (작은 숫자까지의 합) = 두 수 사이의 합
  • 실행 : ex.py
import mod

num1 = int(input(f'input number1: '))
num2 = int(input(f'input number2: '))
ns = mod.NumsSum(num1, num2)
result = ns.sumBetweenNums()
print(f'result: {result}')

input number1: 1
input number2: 3
result: 2




평균 알고리즘


1.

  • 모듈(1) : min.py
class Min:

    def __init__(self, ss):
        self.scores = ss
        self.minScore = 0
        self.minIdx = 0

    def removeMinScore(self):
        self.minScore = self.scores[0]

        for i, s in enumerate(self.scores):
            if self.minScore > s :
                self.minScore = s
                self.minIdx = i
        print(f'self.minScore: {self.minScore}')
        print(f'self.minIdx: {self.minIdx}')

        self.scores.pop(self.minIdx) # self.minIdx 를 지우겠다
        print(f'scores : {self.scores}')
  • 모듈(2) : max.py
class Max:

    def __init__(self, ss):
        self.scores = ss
        self.maxScore = 0
        self.maxIdx = 0

    def removeMaxScore(self):
        self.maxScore = self.scores[0]

        for i, s in enumerate(self.scores):
            if self.maxScore > s :
                self.maxScore = s
                self.maxIdx = i
        print(f'self.maxScore: {self.maxScore}')
        print(f'self.maxIdx: {self.maxIdx}')

        self.scores.pop(self.maxIdx) # self.maxIdx 를 지우겠다
        print(f'scores : {self.scores}')
  • 모듈(3) : near.py
class Top5player:

    def __init__(self, cts, ns): # 현재 점수, 새로운 점수
        self.currentScores = cts
        self.newScore = ns

    def setAlignScore(self): # 재정렬 함수
        nearIdx = 0
        minNum = 10.0 # 점수 중 가장 크느 값으로 설정

        for i, s in enumerate(self.currentScores):
            absNum = abs(self.newScore - s)

            if absNum < minNum:
                minNum = absNum
                nearIdx = i

        if self.newScore >= self.currentScores[nearIdx]:
            for i in range(len(self.currentScores)-1, nearIdx, -1):
            # self.currentScores-1 ~ nearIdx 까지, 1씩 감소하면서
                self.currentScores[i] = self.currentScores[i -1]
                # 데이터가 1자리 씩 밀려나고
            self.currentScores[nearIdx] = self.newScore

        else:
            for i in range(len(self.currentScores)-1, nearIdx +1, -1):
                self.currentScores[nearIdx + 1] = self.newScore

    def getFinalTop5Scroes(self):
        return self.currentScores
  • 실행: ex.py
import max, min
import near

top5Scores = [9.12, 8.95, 8.12, 6.90, 6.18]
scores = [6.7, 5.9, 8.1, 7.9, 6.7, 7.3, 7.2, 8.2, 6.2, 5.8]
print(f'scores: {scores}')

maxA = max.Max(scores)
maxA.removeMaxScore()

minA = min.Min(scores)
minA.removeMinScore()

total = 0
average = 0

for n in scores:
    total += n

average = round(total / len(scores), 2)

print(f'total: {round(total, 2)}')
print(f'average: {average}')

tp = near.Top5player(top5Scores, average)
tp.setAlignScore()
top5Scores = tp.getFinalTop5Scroes()
print(f'top5Scores: {top5Scores}')

scores: [6.7, 5.9, 8.1, 7.9, 6.7, 7.3, 7.2, 8.2, 6.2, 5.8]
self.maxScore: 5.8
self.maxIdx: 9
scores : [6.7, 5.9, 8.1, 7.9, 6.7, 7.3, 7.2, 8.2, 6.2]
self.minScore: 5.9
self.minIdx: 1
scores : [6.7, 8.1, 7.9, 6.7, 7.3, 7.2, 8.2, 6.2]
total: 58.3
average: 7.29
top5Scores: [9.12, 8.95, 8.12, 7.29, 6.9]



2.

kor_avg = 88; eng_avg = 82; mat_avg = 90
sci_avg = 78; his_avg = 92

hong_kor_score = 85; hong_eng_score = 90; hong_mat_score = 82
hong_sci_score = 88; hong_his_score = 100

#19명의 과목별 총합
stu19cnt_kor_total = kor_avg * 20 - hong_kor_score
stu19cnt_eng_total = eng_avg * 20 - hong_eng_score
stu19cnt_mat_total = mat_avg * 20 - hong_mat_score
stu19cnt_sci_total = sci_avg * 20 - hong_sci_score
stu19cnt_his_total = his_avg * 20 - hong_his_score

#19명의 과목별 평균
stu19cnt_kor_avg = stu19cnt_kor_total / 19
stu19cnt_eng_avg = stu19cnt_eng_total / 19
stu19cnt_mat_avg = stu19cnt_mat_total / 19
stu19cnt_sci_avg = stu19cnt_sci_total / 19
stu19cnt_his_avg = stu19cnt_his_total / 19

#홍길동을 제외한 19명 학생과의 차이 
kor_gap = hong_kor_score - stu19cnt_kor_avg
eng_gap = hong_eng_score - stu19cnt_eng_avg
mat_gap = hong_mat_score - stu19cnt_mat_avg
sci_gap = hong_sci_score - stu19cnt_sci_avg
his_gap = hong_his_score - stu19cnt_his_avg

print(f'국어 점수 차이: {"+" + str(round(kor_gap, 2)) if kor_gap > 0 else round(kor_gap, 2)}')
print(f'영어 점수 차이: {"+" + str(round(eng_gap, 2)) if eng_gap > 0 else round(eng_gap, 2)}')
print(f'수학 점수 차이: {"+" + str(round(mat_gap, 2)) if mat_gap > 0 else round(mat_gap, 2)}')
print(f'과학 점수 차이: {"+" + str(round(sci_gap, 2)) if sci_gap > 0 else round(sci_gap, 2)}')
print(f'국사 점수 차이: {"+" + str(round(his_gap, 2)) if his_gap > 0 else round(his_gap, 2)}')

stu19Cnt_total = stu19cnt_kor_avg + stu19cnt_eng_avg + stu19cnt_mat_avg + stu19cnt_sci_avg + stu19cnt_his_avg
stu19Cnt_avg = stu19Cnt_total / 5
hong_total = hong_kor_score + hong_eng_score + hong_mat_score + hong_sci_score + hong_his_score
hong_avg = hong_total / 5

avg_gap = round(hong_avg - stu19Cnt_avg, 2)
print(f'평균 차이: {"+" + str(round(avg_gap, 2)) if avg_gap > 0 else round(avg_gap, 2)}')

제로베이스 데이터 스쿨
profile
비전공자의 데이터 공부법

0개의 댓글