IT/기타

[IT/일상] 엑셀 VBA 페어 매칭 프로그램 후일담 (고도화)

땅일단 2024. 11. 20. 23:58

저번에 블로그 포스팅도 했었던 페어 프로그램 말인데 그동안 후기를 안 적었었지만 어떻게 제가 1등을 해서 치킨을 받았습니다. (아래 글 참고)

 

 

[IT/일상] 엑셀 VBA 매크로로 페어 매칭 프로그램 만들기

계기최근 회사에서 페어 프로그래밍을 주기적으로 하게 됐는데...페어를 짓는 프로그램을 개발부서원들이 직접 개발하는 소소한 대회(?) 가 열렸다.상품은 무려 치킨이기 때문에 도전하지 않을

doringri.tistory.com

 

페어 프로그램 대회에 대해 이야기를 좀 더 하자면 결과는 부서원들의 투표를 통해 정해졌었습니다.

그런데 생각도 못한 참신한 결과물들도 있어서 신기했던 경험이었습니다.

 

이때까지 나온 결과 빈도에 따라 딥러닝마냥 가중치를 두신 분도 있고... (기술점수 ㅇㅈ)

한 분은 유니티를 가지고 만드셨던데 이름이 적힌 공들을 쏘아올려서 공의 길이가 더 가까운 사람끼리 페어를 만드는 방식을 쓰셨더군요. (예술점수 ㅇㅈ)

 

제가 감사하게도 치킨을 받긴 했는데 이 프로그램 유지보수까지 해야 한다는 임무도 함께 받았습니다.

이후 개발팀에 새로운 분이 한 분 더 들어오셔서 인원이 홀수가 됐고 기존 프로그램은 짝수 인원을 상정한 프로그램이었기에 고도화를 진행했었습니다.

 

기존 프로그램은 인원이 홀수라면 빈 셀이 하나 생겼으나, 새로운 프로그램에서는 무작위로 정해진 한 명2개의 페어에 속하도록 변경했습니다.

 

새로운 프로그램에서 인원이 만약 7명이라면 +1한 값인 8을 n에 대입해주어야 합니다.

' ------(인원이 홀수일 때) 빈 셀이 있다면 남은 사람 중에 한 명을 랜덤으로 배정------
        
' 인원 이름 목록 재설정
For i = 0 To n - 1
    names(i) = Cells(startRowIdx + i, 2)
Next

For i = 0 To n \ 2 - 1
    For j = 7 To 8
        If Cells(startRowIdx + i, j) = "" Then
            Dim exitRndLoop As Boolean
            exitRndLoop = False

            Do While exitRndLoop = False
                ' 0 ~ n-2까지의 난수 생성(인원이 홀수라면 마지막 이름은 빈값이므로)
                Dim randomIndex2 As Integer
                randomIndex2 = Int(Rnd * (n - 1))

                ' 같은 이름끼리 페어가 결성되지 않았다면 루프 중단
                If Cells(startRowIdx + i, 7) <> names(randomIndex2) And Cells(startRowIdx + i, 8) <> names(randomIndex2) Then
                    exitRndLoop = True
                End If
            Loop

            Cells(startRowIdx + i, j) = names(randomIndex2)
        End If
    Next
Next

 

주석의 '인원 이름 목록 재설정'이란, 위쪽 코드에서 names 배열에 수정이 가해졌으므로 이름 목록을 다시 가져오는 과정입니다.

 

결과 셀에 만약 빈 셀이 존재한다면 (인원이 홀수라면 무조건 빈 셀이 하나 있습니다), 난수를 생성하고 names 배열에서 해당 난수의 인덱스에 해당하는 사람을 뽑습니다.

 

그 사람이 빈 셀을 채울 사람, 즉 두 개의 페어에 들어갈 사람입니다.

그러나 자기 자신과 같은 페어가 될 수가 있기 때문에 만약 그런 경우가 생긴다면 난수를 다시 생성합니다. 조건에 맞는 게 나올 때까지 계속해서 루프를 도는 로직은 Do While문을 이용했습니다.

 

코드 전문

Sub 생성_click()
    Randomize
    
    Dim startRowIdx As Integer
    startRowIdx = 5
    
    Dim names() As String
    Dim res() As String
    Dim n As Integer
    
    ' 인원이 홀수라면 n에 +1한 값 입력
    n = 10
    
    ReDim names(0 To n - 1)
    ReDim names2(0 To n - 1)
    ReDim res(0 To n - 1)
    
    Dim isDifferentFromPreviousResult As Boolean
    isDifferentFromPreviousResult = False
    
    Do While isDifferentFromPreviousResult = False
    
        ' ------랜덤한 페어 만들기------
        
        For i = 0 To n - 1
            names(i) = Cells(startRowIdx + i, 2)
        Next
            
        For i = 0 To n - 1
            ' 0 ~ n-i-1까지의 난수 생성
            Dim randomIndex As Integer
            randomIndex = Int(Rnd * (n - i))
            
            ' 매칭
            res(i) = names(randomIndex)
            
            ' 마지막 인덱스 요소를 덮어쓰기
            names(randomIndex) = names(n - 1 - i)
        Next
        
        
        ' ------셀에 결과 표시------
        
        For i = 0 To n - 1
            If i Mod 2 = 0 Then
                Cells(startRowIdx + (i \ 2), 7) = res(i)
            Else
                Cells(startRowIdx + ((i - 1) \ 2), 8) = res(i)
            End If
        Next
        
        
        ' ------(인원이 홀수일 때) 빈 셀이 있다면 남은 사람 중에 한 명을 랜덤으로 배정------
        
        ' 인원 이름 목록 재설정
        For i = 0 To n - 1
            names(i) = Cells(startRowIdx + i, 2)
        Next
        
        For i = 0 To n \ 2 - 1
            For j = 7 To 8
                If Cells(startRowIdx + i, j) = "" Then
                    Dim exitRndLoop As Boolean
                    exitRndLoop = False
                    
                    Do While exitRndLoop = False
                        ' 0 ~ n-2까지의 난수 생성(인원이 홀수라면 마지막 이름은 빈값이므로)
                        Dim randomIndex2 As Integer
                        randomIndex2 = Int(Rnd * (n - 1))
                        
                        ' 같은 이름끼리 페어가 결성되지 않았다면 루프 중단
                        If Cells(startRowIdx + i, 7) <> names(randomIndex2) And Cells(startRowIdx + i, 8) <> names(randomIndex2) Then
                            exitRndLoop = True
                        End If
                    Loop
                    
                    Cells(startRowIdx + i, j) = names(randomIndex2)
                End If
            Next
        Next
        
        ' ------직전 결과와 겹치는 페어가 있는지 확인------
        
        isDifferentFromPreviousResult = True
        
        Dim previousPairs() As String
        Dim currentPairs() As String
        Dim currentPairsReverse() As String
        
        ReDim previousPairs(0 To n \ 2 - 1)
        ReDim currentPairs(0 To n \ 2 - 1)
        ReDim currentPairsReverse(0 To n \ 2 - 1)
        
        Dim exitLoop As Boolean
        exitLoop = False
        
        For i = 0 To n \ 2 - 1
            previousPairs(i) = Cells(startRowIdx + i, 4) & Cells(startRowIdx + i, 5)
        Next
        
        For i = 0 To n \ 2 - 1
            currentPairs(i) = Cells(startRowIdx + i, 7) & Cells(startRowIdx + i, 8)
            currentPairsReverse(i) = Cells(startRowIdx + i, 8) & Cells(startRowIdx + i, 7)
        Next
        
        For i = 0 To n \ 2 - 1
            For j = 0 To n \ 2 - 1
                If currentPairs(i) = previousPairs(j) Or currentPairsReverse(i) = previousPairs(j) Then
                    isDifferentFromPreviousResult = False
                    exitLoop = True
                    
                    Exit For
                End If
            Next
            
            If exitLoop = True Then
                Exit For
            End If
        Next
    Loop
    
End Sub

 

 

결과

 

결과입니다.

이름은 백설공주에게 죽음을(드라마) 배역들 이름입니다. (tmi)

첫번째는 '하설' 이 2번, 두번째는 '심보영' 이 2번 나왔네요.

 

 


백설공주에게 죽음을 드라마 재밌게 봤는데 끝에 너무 급하게 끝난 거 같아서 좀 아쉬웠습니다.

그런데 다들 연기도 잘하고, 내용 각색도 생각보다 잘 됐더군요.

형사가 두 명에서 한 명이 돼서 엥?했는데 보다보니까 되게 괜찮았습니다. 캐릭터성도 오히려 더 좋아진 것 같고...

...

근데 이 얘기를 왜 하고 있었더라?