기본 콘텐츠로 건너뛰기

(Python) Merge sorting algorithm의 구현


이번에는 Python으로 Merge sorting을 구현해봤습니다. 수업 과제라서... ㅋㅋㅋ
알고리즘은 인터넷에 이미 많이 올라와있으니까 알고리즘은 아래의 사이트에서 읽어보시면 됩니다!!

(알고리즘이 최적화가 되어있지 않고 variable convention이 안 맞는게 꽤 많습니다 ㅠ, .. 걸러 읽어주세요!!!)





----
사상은 우선 최소 단위인 2개까지 쪼개가지고

아래와 같이 왼쪽/오른쪽에 Handle을 배치해서 크거나 작은 순으로 데이터를 땡기고 핸들을 좌/우에서 밀어가지고 핸들이 끝까지 가면 반대쪽 데이터를 끝까지 append하는 거에요.

알고리즘이 너무 간단해서 설명이 엄청 짧네요!


크게 파트는 두 개로 나눌건데,
1. 입력을 받은 후 처리되는 배열을 정하고, 핸들 배치한 뒤에 비교하는 알고리즘을 호출하는 부분과,
2. 위에서 입력받은 데이터를 비교해서 데이터를 처리하고 정리된 데이터를 리턴하는 부분을 만들어보려고 합니다.
-----
1. 여기서부터는 입력을 받은 후 처리되는 배열을 정하고 핸들 배치, Merge sort를 호출하는 부분입니다.

1) 먼저 List의 길이를 확인합니다. (MainCount_input)
위의 알고리즘을 보면 Merge sorting 하는 Array의 size가 2배씩 커지는데, (MainMultiIndex)에서 1부터 2씩 곱해가다보면 소팅하려고 투입하는 배열 사이즈가 원본 List보다 커지겠죠! 그 때 종료 시켜버리면 되겠죠!

2),3) MainIndex는 Sorting 시작할 부분을 지정해주는 거에요,
그러니까 아래와 같은 순서로 처리가 될 거에요.

(종료호출이 왜 있는지는 아래에다가 달아놓으려고 합니다!)
(2개 정렬)
정렬범위 : 0~1 / 종료호출 : 2
정렬범위 : 2~3 / 종료호출 : 4
정렬범위 : 4~5 / 종료호출 : 6
.. 2개 정렬이 끝나면

(4개 정렬)
정렬범위 : 0~3 / 종료호출 : 4
정렬범위 : 4~7 / 종료호출 : 8
정렬범위 : 8~11 / 종료호출 : 12
.. 4개 정렬이 끝나면

(8개 정렬)
정렬범위 : 0~7 / 종료호출 : 8
정렬범위 : 8~15 / 종료호출 : 16
정렬범위 : 16~23 / 종료호출 : 24

...

규칙을 적어보면
2개 정렬일 때는 [n번째정렬] = 시작점 : 2*(n-1) 종료호출 : 2*n
4개 정렬일 때는 [n번째정렬] = 시작점 : 2*2*(n-1) 종료호출 : 2*2*n
8개 정렬일 때는 [n번째정렬] = 시작점 : 2*2*2*(n-1) 종료호출 : 2*2*2*n

그러니까 x개 정렬시 n번째에는 시작점 : x*(n-1), 종료호출점 : x*n 이 되겠네요!

왜 종료호출점이냐면, 배열을 반으로 쪼갠다고 말씀드렸었는데요~
핸들이 오른쪽으로 검색한다고 쪼개진 왼쪽 핸들은 중간지점이 종료지점이 되지만, 오른쪽 핸들은 종료지점이 마지막 부분이기 때문이에요!

저는 배열을 0부터 시작했으니까 x*n이 되겠지요!

위에서 x와 n을 저는 x : MainMultiIndex, n : MainIndex로 표현하였읍니다.
그래서 n은 2씩 더하게 되고 전체 Array가 다 돌면 x는 1을 더해주면 되겠죠~~

뭔가 설명이 너무 복잡하고 어렵네요~ ㅋㅋ 알고리즘은 참 쉬운데 말이에요~

2.여기서부터는 비교하는 알고리즘 입니다.
위에서 배열을 입력받고 시작점을 정하고 Sorting size도 다 정했으니까 이 부분은 굉장히 간단해집니다. 위와 같이 핸들에 있는 데이터를 비교해서 순서대로 정리한 뒤에 리턴해주면 되니까요!


앞선 순서도를 보면 배열의 좌/우측 중 하나가 끝나면 나머지 부분을 모두 append시키고 리턴해줘야 합니다.
그래서 EndFlag라는걸 하나 위치시켜서 왼쪽이나 오른쪽이 끝나면 반대쪽 데이터를 모두 배열에 실어서 리턴해주면 됩니다.

1) 오름차순인지 내림차순인지에 따라서 판단조건을 설정하고
2)
비교하여 조건이 맞는 쪽의 데이터를 임시 배열에 밀어 넣습니다. (오름차순이냐 내림차순이냐에 따라서)
이를 반복하는데 , 한 쪽의 핸들이 마지막에 도착하면 배열 반대쪽 데이터를 임시배열에 끝까지 밀어넣습니다.

반대쪽의 데이터는 그대로 밀어넣어도 되는 이유가, 쪼개진 단위의 데이터는 순서대로 배열되어 있기 때문에 그렇습니다.

완성될 알고리즘으로 머지쏘트를 돌려보면 아래와 같이 처리되는데 진행되는 순서를 보시면 이해가 될 것 같습니다. (내림차순의 경우에요!)


3) 핸들이 마지막에 도착하면 원본 리스트에 옮겨 담아줍니다

-----
위의 과정을 반복한다면 한 가지 문제가 발생합니다.
바로 배열이 2^n개가 아니라면요? 라는 문제에요.

가장 큰 숫자를 한다면 나중에 해당 숫자를 제거해야 할테고
가장 작은 숫자를 해도 똑같겠죠?

그러니까 제가 사용할 방법은 입력된 배열을 반으로 쪼갠 뒤 오른쪽 절반의 핸들의 종료지점이 입력된 배열의 사이즈를 초과한다면 오른쪽 핸들의 종료호출 지점으로 입력된 배열의 사이즈를 지정하는거에요. 만약에 그 종료호출 지점이 오른쪽 시작지점보다 크다면 그대로 종료 시켜버리면 됩니다.

말은 길지만 코딩은 엄청 짧게 되었네요! ㅎ

※ Gap이라는 변수는 원본 배열에 복사하기 위해서 설정하였습니다.
(위와 같이 원래 Sort했어야 할 갯수와 최종 복사되는 갯수의 차이가 발생시에 그만큼 빼서 원래 배열에 복사하는데 오류가 발생하는 것을 방지합니다.)


----

처리가 잘 되었네요! ㅎㅎ

설명이 너무 어려운 것 같아서 죄송합니다만 ㅠ 실제로 해보시면 굉장히 쉽다는 것을 아시게 될 것 같습니다. 그럼 다음에도 뭔가 만들면 또 써놓을게요. 도움이 되신다면 좋겠습니다~~

아래에서 List_Input의 데이터와 Direction 변수만 "Up", "Down"으로 지정해주시면 확인이 가능하세용~~

def Shims_Merge (StartPoint, SortSize, DataSeries, Direction) :
    '''
    Handle Initialization
    Using Mergesort Algorithm : https://www.geeksforgeeks.org/merge-sort/
    When Each handle meets the endpoint of given side, then exit function
    
    MH : Merge Handle (for left side / right side)
    Temp : Temporary data address (for left side / right side)
    Temp_List : Empty list to sort the data in 'DataSeries'
    
    '''
    n = len(DataSeries) -1
    MH_L = StartPoint
    MH_L_End = StartPoint + SortSize - 1
    MH_R = StartPoint + SortSize
    MH_R_End = StartPoint + 2 * SortSize - 1
    CIndex = 0
    Gap = 0
    # If right handle is positioned at the value than right handle , then exit function.
    # Left Handle will be always smaller then length of 'DataSeries' in input level, So it doesn't need to be cared in this function.
    if MH_R_End > n :
        Gap = MH_R_End - n
        MH_R_End = n
    if MH_R_End < MH_R :
        return
    EndPoint = MH_R_End
    CopyCount = range(0,2 * SortSize - Gap,1)
    Count = 0
    Temp_L = DataSeries[MH_L]
    Temp_R = DataSeries[MH_R]
    Temp_List = []
    EndFlag = False
    #print ("MH_L : %d, MH_R : %d, MH_LE : %d, MH_RE : %d"%(MH_L,MH_R,MH_L_End,MH_R_End))
    #When adds data to Empty list, user should use append inner function.-Important
    while (1) :  
        if Direction == "Up" :
            Criteria = Temp_L < Temp_R
        else :
            Criteria = Temp_L > Temp_R
        if Criteria :
            Temp_List.append(Temp_L)
            MH_L = MH_L + 1
            #Sweep datas into temporary list
            if MH_L > MH_L_End :
                EndFlag = True
                while MH_R <= MH_R_End :
                    Temp_R = DataSeries[MH_R]
                    Temp_List.append(Temp_R)
                    MH_R = MH_R + 1
            Temp_L = DataSeries[MH_L]
        else :
            Temp_List.append(Temp_R)
            MH_R = MH_R + 1
            #Sweep datas into temporary list
            if MH_R > MH_R_End :
                EndFlag = True
                MH_R = MH_R_End
                while MH_L <= MH_L_End :
                    Temp_L = DataSeries[MH_L]
                    Temp_List.append(Temp_L)
                    MH_L = MH_L + 1
            Temp_R = DataSeries[MH_R]
        if EndFlag == True :
            for CIndex in CopyCount :
                DataSeries[StartPoint + CIndex] = Temp_List[CIndex]
            del(Temp_List)
            return
def Shims_Input() :
    num = []
    i = 1
    while i <= 20 : 
        try:
            user_input = int(input(str(i)+"번째 숫자를 입력하세요 : "))
            num.append(user_input)
            i = i + 1
        except ValueError:
            print("숫자만 입력하세요.")
    return num
    
#Main function is written from here.
'''
#Using this part when using manual input

List_input = Shims_Input()
MainCount_input = len(List_input)
MainMultiIndex = 1
'''

List_input = [1,5,6,123,2137,8,1,2,45,6,72,6,4,2,45,7,87,32,6,3,12]
Direction = "Down" #Set direction of sorting, "Up" for Upward or "Down" for Downward

#Processing merge sort algorithm from smaller group to larger group
print (List_input)
MainCount_input = len(List_input)
MainMultiIndex = 1
while MainMultiIndex < MainCount_input : 
    MainIndex = 0
    while MainIndex*MainMultiIndex < MainCount_input :
        Shims_Merge(MainIndex*MainMultiIndex, MainMultiIndex, List_input,Direction)
        MainIndex = MainIndex + 2
        print (List_input)
    MainMultiIndex = MainMultiIndex * 2
print (List_input)

댓글

이 블로그의 인기 게시물

(VBA) 009 - 닫힌 파일에서 데이터 읽어오기 (ExecuteExcel4Macro)

#毎日育ちゃん可愛い大会 예시의 매크로 파일을 테스트 할 때는 저장된 폴더를 사용하실 폴더로 꼭 바꿔주세요! (pptx파일) pptx파일 (xlsx파일) 예제데이터파일   Macro파일 ★ 진행목적 : 왜 이걸 사용합니까 . 1) 행이나 열 , 또는 Sheet 과 같이 다른 특성을 가지는 1,2,3 차 데이터배열에 대한 처리 방법을 지금까지 설명드렸습니다 . 2) 그럼 이제 , 다른 파일에서 데이터를 읽어올 방법을 알아볼 필요가 있습니다 . 어째선가 회사의 데이터를 처리하다보면 , 주기적인 이름의 엑셀 파일 특정 Sheet, Cell 에 있는 경우가 많았습니다 . 3) 엑셀에서 이미 열려있는 파일의 참조는 ‘=‘ 을 사용하면 가능하지만 , 닫힌 파일은 데이터를 읽지 못합니다 . 4) 그래서 이를 처리하기 위해 VBA 의 ‘ExecuteExcel4Macro( 주소 )’ 를 사용합니다 ! ★ 다른 파일의 참조는 어떻게 합니까 ? 1) 열려 있는 다른 파일의 데이터를 읽는 방식은 ‘=‘ 을 입력하고 해당 Cell 을 클릭하면 됩니다 ! 2) 그러면 아래와 같이 (=‘ 파일이 있는 폴더 [ 파일명 ]Sheet 명 ’!Cell 주소 ) 의 형태로 기록 이 됩니다 . ★ 닫힌 파일에 대해서는 INDIRECT 는 사용이 되질 않습니다 ! 1) INDIRECT 로는 처리가 되질 않습니다 . 2) 어째선가 전에 사용하던 INDIRECT 를 사용하고 싶지만 , 사용이 되질 않습니다 . 검색을 해봐도 안된다는 답변만 있네요 ! 3) 파일이 하나 두 개라면 , 이전과 같이 ‘=‘ 를 쓰면 되겠지만 , 그러면 자동화를 통한 효율화가 불가능해지겠죠 ! 4) 그래서 이를 처리하기 위해 VBA 의 ‘ExecuteExcel4Macro( 주소 )’ 를 사용합니다 ! ★ ExecuteExcel4Macro 는 어떻게 사용합니까 ? 1) VBA 의 ExecuteExcel4Macro 란 매크로...

(VBA) 004 - Object 이름으로 이미지 복사 붙여넣기

(VBA) 004- Object 이름으로 이미지 복사 붙여넣기 진행목적  :  왜 이걸 사용합니까 . → Object  이름을 사용해서 주기적 복사가 가능한 경우가 있습니다 .     ( 예  :  사진의 이름이  ‘ 사진  1’ ‘ 사진  2’…  로 되어있거나 , ‘Picture 1’, ‘Picture 2’…  로 됨 ) (설명자료는 여기) 설명자료 ( 예제파일은 여기 ) 예제파일 (VBA 진행에 대해) 이제 VBA 세션 자료를 필요할 때 보려고 이 블로그에 남기려고 해요. 앞선 001-003도 옮겨야겠습니다. 올해 내에 100가지 주제를 가지고 포스팅 할 수 있도록 할게요. ( 中谷 育 さんのイメージで に対して) 本当に可愛い中谷 育さんのイメージが 含まれています。 ありがとうございます。 何か問題があったら、教えてください。 直ちに処理します。

(Node.js) XLSX 모듈 사용 / 행렬 파싱 및 조건에 맞는 데이터만 추출

요번에는 Node.js로 아래와 같이 KRX에서 코스피/코스닥 상장사 정보를 취득한 후 네이버에서 원하는 조건에 맞는 정보만 크롤링하는 모듈을 만들어보려고 합니다! 그러면 주식하는 친구들은 조건식을 걸어놓기만 해도 손 안대고 틈틈히 자동으로 수집된 정보를 확인할 수 있게 되겠네요!  (이후에는 자동 메일링까지 추가할 건데 우선은 크롤링해서 유의미한 XLSX로 Export하는 것 까지를 먼저 만들려고 합니다!) 네이버 주식에서 페이지를 확인해보니 종목코드를 기준으로 페이지 주소가 매칭이 되고, 그 유니크한 종목 코드에 유니크한 Selector를 확인하면 데이터 크롤링이 되겠네요! ----- 저는 KRX 사이트에서 업체리스트를 xls로 받아온 뒤에 추출된 결과를 중간에 result.xls로 먼저 저장해놨다가 크롤링할 때 다시 리딩해서 쓰는 방식을 구현해보려고 합니다. VBA / C++ 데이터 처리 방식이 익숙하기도 하고 나중에 불필요하면 떼버리면 되니까요? XLSX Import하는 모듈은 앞서 설명을 드렸었고요, 데이터를 Readfile한 뒤에 조건에 맞는거만 옮겨담는 작업이 필요한데요, 이를 위해 stackoverflow에서 parsing하는 알고리즘을 참고해서 아래와 같이 변형합니다! https://stackoverflow.com/questions/30859901/parse-xlsx-with-node-and-create-json 원본은 data[row][headers[col]] = value인데, XLSX에서 가상의 sheet에 데이터를 넣어주려면 array of arrays 방식이 되어야 하기 때문에, 아래와 같이 우선 빈 배열을 선언하고, row / column을 동적할당(new array()) 후 push 하는 방식으로 처리해줘야 합니다. 제가 아는 방법 중엔 이게 제대로 동작을 하기 때문에 이렇게 바꿔서 사용했습니다. 조건식은 나중에 입력받겠지만,...