파이썬_실전 프로젝트

2018년 월드컵 한국 16강 진출 가능성(경우의 수)

2018년 러시아 월드컵에서 한국은 독일,멕시코,스웨덴과 함께 F조로 편성됐습니다. 죽음의 조라고 하네요.

며칠전에 이영표 해설위원이 오히려 독일이 3승을 해주면 한국이 올라갈수 있는 확률이 높다고 했었죠. 세팀 공평하게 1패씩 하므로, 나머지 세팀간의 싸움에 따라 한팀이 정해지니깐요. 만에 하나, 독일이 멕시코나 스웨덴을 상대로 비기거나 패한경우, 그 상대팀은 승점이 추가되게 되고, 한국은 떨어질 확률이 높아집니다. (한국은 독일한테 무조건 패한다는 전제하에)

그런데 좀 다르게 생각해보면, 독일제외 3팀이 경쟁해서 1팀 올라가는 것보다, 독일포함 4팀이 경쟁해서 2팀이 올라가는게 확률(?)상 더 높아보이기도 합니다. 그래서 어느쪽이 옳은지 확인해볼려고 코드로 한번 만들어 봤습니다.

 

모든 경우의 수 생성

경기수는 6경기이고, 독일은 'G', 멕시코는 'M', 스웨덴은 'S', 한국은 'K'로 표시한 대진표는 아래와같습니다. 순서는 경기일정순입니다. 6경기 2팀씩 해서 총 12개의 알파벳이 있습니다.

matches='GMSKKMGSKGMS'

 

그다음은 6경기의 가상의 경기결과를 L,N,R로 표시하여 출력했습니다.

'LNLLRN'

대진표상의 좌측팀이 이기면 L,비기면 N, 우측팀이 이기면 R로 표시하였고, 경우의 수는 LNR 세가지 수의 6제곱= 729 입니다.


# L : left team win
# N : no decision
# R : right team win

match_result='LLLLLL'
count =0

def main(result=''):
    global count
    if len(result) == 6 :  # 문자열이 6개가 되면, 화면에 출력.
        count+=1
        print(result,count)
    else:
        for i in "LNR":
            main(result+i) # 재귀호출
    return

main()
LLLLLL 1
LLLLLN 2
LLLLLR 3
LLLLNL 4
LLLLNN 5
LLLLNR 6
LLLLRL 7
LLLLRN 8
LLLLRR 9
LLLNLL 10
LLLNLN 11
LLLNLR 12
...
...
RRRNRL 718
RRRNRN 719
RRRNRR 720
RRRRLL 721
RRRRLN 722
RRRRLR 723
RRRRNL 724
RRRRNN 725
RRRRNR 726
RRRRRL 727
RRRRRN 728
RRRRRR 729

main()함수는 "L","N","R" 세 알파벳으로 이루어진 여섯자리의 문자열을 만들어 내는 함수입니다. 재귀호출형식의 반복문으로 되어있고, 6자리가 될때까지 알파벳을 번갈아가며 한자리씩 늘려서, 6자리가 되면, 출력을 하고, 그렇지 않으면 스스로를 다시 불러냅니다.

 

승점 계산

다음은 경기결과에 따라 이긴팀은 3점, 진팀은 0점, 비기면 양팀에 1점씩을 배분하였습니다. 함수들은 main()함수부터 아래에서 위쪽의 순서로 보시면 됩니다.

# 승점 계산 
# G : Germany
# M : Mexico
# S : Sweden
# K : Korea

matches='GMSKKMGSKGMS'
count =0

# 배분된 승점을 팀별로 집계 
def get_team_point(w_point):
    t_point=''
    for i in "GMSK":
        point = 0
        for t,p in zip(matches,w_point):
            if t==i:
                point+=int(p)
        t_point+=str(point)
    return t_point

# 경기 결과에 따라 승점 배분 
def get_winning_point(result):
    w_point=''
    for i in result:
        if i == 'L':
            w_point+='30' # 좌측팀에 3점, 우측팀에 0점
        elif i == 'N':  
            w_point+='11' # 비기면 양팀에 1점씩
        elif i == 'R':
            w_point+='03' # 좌측팀에 0점, 우측팀에 3점.
    return w_point


def main(result=''):
    global count
    global case_count
    if len(result) == 6 :
        winning_point = get_winning_point(result)
        team_point = get_team_point(winning_point)
        count+=1
        print(count,result,winning_point,team_point,case_count)
    else:
        for i in "LNR":
            main(result+i)
    return

main()
LLLLLL 303030303030 6336 1
LLLLLN 303030303011 6146 2
LLLLLR 303030303003 6066 3
LLLLNL 303030301130 7334 4
LLLLNN 303030301111 7144 5
LLLLNR 303030301103 7064 6
LLLLRL 303030300330 9333 7
LLLLRN 303030300311 9143 8
LLLLRR 303030300303 9063 9
LLLNLL 303030113030 4346 10
LLLNLN 303030113011 4156 11
LLLNLR 303030113003 4076 12
...
...
RRRNRR 030303110303 4643 720
RRRRLL 030303033030 0936 721
RRRRLN 030303033011 0746 722
RRRRLR 030303033003 0666 723
RRRRNL 030303031130 1934 724
RRRRNN 030303031111 1744 725
RRRRNR 030303031103 1664 726
RRRRRL 030303030330 3933 727
RRRRRN 030303030311 3743 728
RRRRRR 030303030303 3663 729

12자리 승점은 matches='GMSKKMGSKGMS' 의 순서입니다. 그 다음 4자리수는 각팀의 누적총점입니다. 'GMSK' 순서입니다.

 

조별리그 통과팀 판별

새로운 함수 find_winner() 을 만들어서, 점수에 따라 토너먼트 진출팀을 선별하였습니다. 공동순위가 있어서 두팀이상이 선별됐을 경우는 골득실을 따지는 상황이기 때문에 일단 다 표시했습니다.

matches='GMSKKMGSKGMS'
count =0

#  16강 진출팀 선별 
def find_winner(team_point,winner='',teams='GMSK'):
    max_point = max(team_point)
    next_point='';next_teams=''
    for t,p in zip(teams,team_point):
        if p == max_point:
            winner+=t
        else :
            next_teams+=t
            next_point+=p
    if len(winner)<2:
        winner = find_winner(next_point,winner,next_teams)
    return winner

def get_team_point(w_point):
    t_point=''
    for i in "GMSK":
        point = 0
        for t,p in zip(matches,w_point):
            if t==i:
                point+=int(p)
        t_point+=str(point)
    return t_point


def get_winning_point(result):
    w_point=''
    for i in result:
        if i == 'L':
            w_point+='30'
        elif i == 'N':
            w_point+='11'
        elif i == 'R':
            w_point+='03'
    return w_point


def main(result=''):
    global count
    global case_count
    if len(result) == 6 :
        winning_point = get_winning_point(result)
        team_point = get_team_point(winning_point)
        winner = find_winner(team_point)
        count+=1
        print(count,result,winning_point,team_point,winner)
    else:
        for i in "LNR":
            main(result+i)
    return

main()
1 LLLLLL 303030303030 6336 GK
2 LLLLLN 303030303011 6146 GK
3 LLLLLR 303030303003 6066 GSK
4 LLLLNL 303030301130 7334 GK
5 LLLLNN 303030301111 7144 GSK
6 LLLLNR 303030301103 7064 GS
7 LLLLRL 303030300330 9333 GMSK
8 LLLLRN 303030300311 9143 GS
9 LLLLRR 303030300303 9063 GS
10 LLLNLL 303030113030 4346 KGS
11 LLLNLN 303030113011 4156 KS
12 LLLNLR 303030113003 4076 SK
...
...
719 RRRNRN 030303110311 4723 MG
720 RRRNRR 030303110303 4643 MGS
721 RRRRLL 030303033030 0936 MK
722 RRRRLN 030303033011 0746 MK
723 RRRRLR 030303033003 0666 MSK
724 RRRRNL 030303031130 1934 MK
725 RRRRNN 030303031111 1744 MSK
726 RRRRNR 030303031103 1664 MS
727 RRRRRL 030303030330 3933 MGSK
728 RRRRRN 030303030311 3743 MS
729 RRRRRR 030303030303 3663 MS

find_winner() 함수는 각팀의 승점순서대로 2팀 이상 선별하는 함수입니다. 최대점수를 가진 팀을 가려내고 2팀이상이 선별되지 않으면, 선별된 팀은 제외하고 남은팀을 다시 find_winner() 함수로 넣어서 계산을 반복합니다. 동점팀(골득실을 따져야되는 팀)이 있으면 2팀 이상이 될수도 있습니다.

 

조건별로 결과 필터링 하기

특정 조건하에서 한국팀이 16강을 진출할수 있는 경우의 수를 확인하기 위해서 main()함수에 조건문을 추가했습니다.

# 조건 별로 결과 출력

matches='GMSKKMGSKGMS'
count =0
case_count =0

def find_winner(team_point,winner='',teams='GMSK'):
    max_point = max(team_point)
    next_point='';next_teams=''
    for t,p in zip(teams,team_point):
        if p == max_point:
            winner+=t
        else :
            next_teams+=t
            next_point+=p
    if len(winner)<2:
        winner = find_winner(next_point,winner,next_teams)
    return winner

def get_team_point(w_point):
    t_point=''
    for i in "GMSK":
        point = 0
        for t,p in zip(matches,w_point):
            if t==i:
                point+=int(p)
        t_point+=str(point)
    return t_point


def get_winning_point(result):
    w_point=''
    for i in result:
        if i == 'L':
            w_point+='30'
        elif i == 'N':
            w_point+='11'
        elif i == 'R':
            w_point+='03'
    return w_point


def main(result=''):
    global count
    global case_count
    if len(result) == 6 :
        winning_point = get_winning_point(result)
        team_point = get_team_point(winning_point)
        winner = find_winner(team_point)
        count+=1
        # 독일이 2승1무로 승점 7점, 한국이 독일에 패하고도 2위로 올라가는 경우의 수 출력.
        if team_point[0]=='7' and result[4]=='R' and 'K' in winner and len(winner)==2:
            case_count+=1
            print(count,result,winning_point,team_point,winner,case_count)
    else:
        for i in "LNR":
            main(result+i)
    return

main()
97 LNLNRL 301130110330 7324 GK 1
98 LNLNRN 301130110311 7134 GK 2
178 LRLNRL 300330110330 7316 GK 3
179 LRLNRN 300330110311 7126 GK 4
180 LRLNRR 300330110303 7046 GK 5
206 LRNNRN 300311110311 7224 GK 6
332 NNLLRN 111130300311 7224 GK 7
412 NRLLRL 110330300330 7406 GK 8
413 NRLLRN 110330300311 7216 GK 9
414 NRLLRR 110330300303 7136 GK 10
440 NRNLRN 110311300311 7314 GK 11
441 NRNLRR 110311300303 7234 GK 12

위 결과는 독일이 2승1무로 승점 7점일때, 한국이 2위로 올라가는 경우의 수이고, 12 가지입니다. 한국이 올라가지 못하는 경우의 수는 38가지이고, (자력)진출:실패 = 12:38 입니다. 골득실 상황제외하구요.

조건문을 살짝 바꿔서 독일이 3승을 했을시엔 한국의 16강 진출의 경우의 수가 성공 7, 실패 15로 비율은 7:15입니다.

 

정리

독일 3승 때, 한국 16강 경우의수 : 자력진출 7/ 골득실 5/ 탈락 15, 자력진출은 1승1무 이상, 골득실은 최소 1승.(2무도 가능하나, 이땐 독일제외 3팀이 모두 비기는 상황)

독일 2승 1무,  자력진출 12/ 골득실 4 / 탈락 38, 자력진출은 1승1무 이상, 골득실도 1승1무 이상.

독일 2승 1패시,  한국 16강 자력진출 4/ 골득실 8 / 탈락 42, 자력진출 하려면 무조건 2승,, 골득실은 1승1무 이상.

역시 이영표 해설위원 말대로 독일이 3승때가 가능성이 가장 높고, 독일 성적이 내려갈수록 한국도 진출 가능성이 내려갑니다,, 아이러니한 현실이네요 ㅎㅎ

물론 전제조건은 "한국은 독일에 패"한 경우입니다. 독일에 비기기라도 한다면 희망이 급상승하겠죠.

댓글

댓글 본문
  1. nomadlife
    다시 보니, 좀 어렵게 만들긴 했네요. 저도 공부할겸, 다시 만들어 볼게요.
    대화보기
    • 최윤진
      너무어렵..