프로그램언어+/Python

파이썬 문제 풀어보기(매칭 자료구조 알고리즘) - (작업중 엉킴)

logthink 2019. 1. 4. 02:09




<요구 사항> 

 

취미가 가장 비슷한 커플을 매칭 해 주자! 알파벳으로 이루어진 2차원 배열이 입력으로 주어진다. 이 배열에서 각 line은 하나의 사람을 나타내고, 하나의 알파벳은 하나의 취미를 나타낸다. 취미의 종류는 (a-z) 26개이고, 한 사람 당 10개의 취미를 가진다. 한 사람이 중복된 취미를 가질 수는 없다. 취미가 가장 비슷한 커플들을 찾아 내보자. 

 

입력 : 커플 매칭의 대상자의 수가 첫 번째 line에 주어지며(행과 열은 공백으로 구분), 그 다음 line 부터는 취미 값이 주어진다. 취미가 나타나는 순서는 아무런 관계 없다. 

 

입력 샘플  


E H R A D W Q C T P //1번 대상자 

E G U D A M C P V B //2번 대상자 

E H R D A Q W C T M //3번 대상자 

 

출력 : 가장 취미가 일치하는 대상자 2명을 뽑아낸다. (만약, 여러 커플이 발생 할 경우에는 모두 출력한다. )

 

출력  샘플 

1-3 

 

해설 1번 사람은 E,H,R,A,D,W,Q,C,T,P 총 10개의 취미를 가지고 있고, 2번 사람은 E,G,U,D,A,M,C,P,V,B의 10개의 취미를 가지고 있다.  3번 사람은 E,H,R,D,A,Q,W,C,T,M의 10개의 취미를 가지고 있다. 이 커플 매칭 대상자 3명 중 1번과 3번 사람이 E,H,R,D,A,Q,W,C,T 9개의 취미가 서로 일치 하기 때문에 가장 일치 한다고 볼 수 있다. 

 

< 주의 상황> 1. 취미가 10개 일치하는 커플이 여러 커플 발생하는 경우      결과 : 1-3, 2-4 2. 취미[A,B,C,D,E,F,G,H,I,J]를 가진 대상자가 3명 발생한 경우(like 삼각관계) 경우      결과 : 1-2, 2-3, 1-3 

 


요구사항에 앞서 로직의 움직임을 종이 쭉 반복해서 써보고, 알고리즘을 그려가면서 로직과 친해지려 노력했다. (2번정도 씩)

그러다가 컴퓨터 down;;.........cmd를 켜서 코드를 작성했다.(처음부터..메모장 데이터 100백만개...인데 실수로 open함수의 객체.read()해버려써....; IC...)

메모장에 있는 2차원 배열의 알파벳을 가져와서 "D"라는 단일 알파벳을 hard coding으로 사용하여 true or false를 출력해봤다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
with open("C:/Users/namki/Downloads/버드뷰_500x10.txt""r") as f:
...    res = f.readlines()
...    for x in range(100):
...       if "D" in res[x]:
...          print("true")
...       else:
...          print("false")
...
true
false
false
false
false
true
false
false
false
cs

이제 출력문을 formating하자.

#1. 출력되는 1차원 배열의 인덱스

#2. 일치되는 1차원 배열의 data

#3. 일치하는지 안하는지 true와 false


1
2
3
4
5
6
7
8
9
10
11
12
if __name__=="__main__":
    data=['D''F''Y''M''J''T''U''Q''K''Z']
    pass_person=list()
    with open("C:/Users/namki/Downloads/버드뷰_500x10.txt""r") as f:
        res = f.readlines()
        for x in range(100):
            time.sleep(0.1)
            for y in data:
                if y in res[x]:
                   print("결과는 true | 사람 인덱스 {} ({})| 일치 문자 : {}".format(x, res[x], y))
                else:
                    pass
cs


[수정 Flow]

1. hardcoding 데이터를 실제 배열로 만들어서 반복해서 대입했다.

   1.1. 메모장에 있는 1차원 배열안에 data의 글자마다 반복해서 비교한다.(즉, 기준배열과 메모장 데이터를 비교)

2. false 배열들은 관리하지 않았다.(test상) + (sleep함수로 부하를 낮춤)

3. pass_person이라는 배열을 선언했다.(출력되는 1차원 배열들의 인덱스를 담을 변수) then "data배열에서 몇개가 일치하는지 파악이 됨"


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
결과는 true | 사람 인덱스 96 (V W Z N D I B C K A)| 일치 문자 : K
결과는 true | 사람 인덱스 96 (V W Z N D I B C K A)| 일치 문자 : Z
결과는 true | 사람 인덱스 97 (Z I Q E H R K Y O B)| 일치 문자 : Y
결과는 true | 사람 인덱스 97 (Z I Q E H R K Y O B)| 일치 문자 : Q
결과는 true | 사람 인덱스 97 (Z I Q E H R K Y O B)| 일치 문자 : K
결과는 true | 사람 인덱스 97 (Z I Q E H R K Y O B)| 일치 문자 : Z
결과는 true | 사람 인덱스 98 (B G X L M A Q D S U)| 일치 문자 : D
결과는 true | 사람 인덱스 98 (B G X L M A Q D S U)| 일치 문자 : M
결과는 true | 사람 인덱스 98 (B G X L M A Q D S U)| 일치 문자 : U
결과는 true | 사람 인덱스 98 (B G X L M A Q D S U)| 일치 문자 : Q
결과는 true | 사람 인덱스 99 (E V A L W O S C F X)| 일치 문자 : F
[0000000000111122233344444455555666,
 777788101010111111111111121212121213131414151515,
 1515161616171717171717181818181819191919191919202020,
 2020202121212122222223232323242425252525252626262626,
 2627272727282828282829292930303030313131323333333334,
 3435353536363636363636363737373838383839393939393940,
 4040414141424242434343434344444444444545464646464747,
 4747484848494949505050515151515151525252535353545454,
 5455555555565656565656575757585858595959596060606161,
 6161626262636363636363646464646465656566666666676767,
 6767686868686969697070707171717171727272727373737374,
 7474747575757575767677777777787878787879797979808081,
 8181818282828384848484858585858585868686868686878788,
 8888888989899090909091919191919192929292929393939393,
 9494949495959595969696979797979898989899]
cs



즉, 출력되는 readlines를 반복하면서 알게된 것은

1) 일치하는 인덱스 번호

2) 일치하는 문자

3) 일치했던 횟수


이제 위 데이터를 알아보기 쉽게 하기위해 함수하나를 선언했다.

이 함수 내 소스코드는 dict을 리턴할 것이다.

dict{ {중복된문자열 key} : {중복횟수 value} }


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import time
w_count = {}
lists = [000000085858585868686,
 86868687878888888889898990,
 90909091919191919192929292,
 92939393939394949494959595,
 95969696979797979898989899]
for lst in lists:
    time.sleep(1)
    print('lst : {}'.format(lst))
    try:
        w_count[lst] += 1
        print(w_count[lst])
    except: w_count[lst]=1
print (w_count)
cs


위 코드 실행결과는 다음과 같다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
lst : 96
3
lst : 97
lst : 97
2
lst : 97
3
lst : 97
4
lst : 98
lst : 98
2
lst : 98
3
lst : 98
4
lst : 99
{07854866872884893904916925935944954963974984991}
cs


이제 value데이터가 일치하는 여부를 확인한다.

10개일 때 -> 9개일 때 -> 2개일 때 -> 1개일 때 비교해서 데이터를 반환하는 함수를 만든다.

이 함수를 manage_overlap이란 함수로 정했다.

이렇게 반환된 데이터를 true_ing_list 변수에 담았고, res_num_compare(true_ing_list) 이런식으로 인자로 던졌다.


def res_num_compare( )

1
2
3
4
5
6
7
8
9
10
11
12
13
def res_num_compare(data):
    res={}
    for x in data: #딕셔너리 데이터를 반복 출력
        #value값을 비교해서 10부터~1까지 데이터가 있는지 비교
        for y in range(10,0,-1): 
            if data[x] == y: #value와 (10~7)중 일치확인
                #print(y) #일치하는 갯수 확인  #print(x)#일치하는 갯수에 해당하는 key를 출력
                #print("key : {}, value: {}".format(x, y))
                res[x]=y
                #time.sleep(0.1)
            else#일치하지 않음
                pass
    return res
cs


true에 해당하는 인덱스(사람)을 인자로 넣어서 딕셔너리를 반환 받는 코드이다. { 중복데이터, 중복숫자 }

즉, 중복된 데이터를 키, 중복 숫자를 값으로 받는다.


그리고 이렇게 반환된 데이터를 result_formating( )의 인자로 넣는다.


result_formating( )

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def result_formating(datas):
    #num_index에는 과연 매칭이 가장높은애가 몇 명인지 알려줌 
    result = list()
    datas=sorted(datas, key=lambda k : datas[k], reverse=True) #value를 기준으로 역정렬화
    num_index = high_match_index(datas) #선별된 매칭인원
    #join으로 출력데이터 사이에 '-'를 붙히기 위해 미리 배열에 저장해두자.
    #num_index개수 만큼 출력하자
    compare = 0
    for only_key in datas: #미리 정렬된 dict을 하나씩 빼자
        result.append(only_key)
        if compare == num_index: #이 조건문은 딱 최고로 매칭된 사람(key)만큼 출력하기 위해서 선언
            return result#매칭되지 않는 사람이 나오게 된다
        else
            compare += 1 #같은값이 처음부터 나오면 리턴값이 1개뿐이니 2개를 뽑기 위해 0부터 비교
    pass 
cs


이렇게 해서 가장 매칭이 잘된 사람들을 배열에 담아서 리턴해준다.
그리고 리턴된 배열의 사람들을 출력하는 format을 하자.

1
2
3
print('-'.join(str(v) for v in matched))
 
>> 71-22
cs

마지막으로 처음에 기준으로 두었던 data변수안에 리스트의 각 값들을 랜덤하게 뽑기 위해 함수를 선언했다. 
(이 함수의 반환값은 10자리의 리스트)

1
2
3
4
5
6
ef rendom_str(): #처음 비교를 하기 위해 기준점을 세우는 사람생성(10개의 취미를 가진)
    res= '' #출력할 문자열
    for x in range(10): #열 번 반복
        ran=random.randrange(65,90+1#65~90 index출력
        res+=chr(ran) #해당 인덱스에 해당하는 ascii 문자 반환
    return list(res) #10개 취미를 배열로 리턴
cs


참고


딕셔너리 문법 : https://wikidocs.net/3089

구분자 문자열과 문자열 리스트 연결 : https://dojang.io/mod/page/view.php?id=2299

딕셔너리에서 키 찾기 : https://hashcode.co.kr/questions/1194/dictionary%EC%97%90%EC%84%9C-%EA%B0%92%EC%9C%BC%EB%A1%9C-%ED%82%A4%EB%A5%BC-%EC%B0%BE%EC%95%84%EB%82%B4%EB%A0%A4%EB%A9%B4

딕셔너리 정렬(sort) : http://blog.naver.com/PostView.nhn?blogId=msyang59&logNo=220627524714


+ Array 중복제거


1
2
3
4
5
6
7
8
9
def no_continuous(s):#중복제거
    # 함수를 완성하세요
    r=[]    #정답을 저장할 빈공간의 list 생성
    for i in range(len(s)): #i를 번지수로 돌릴것이며 범위는 s의 갯수만큼
        if i == 0:          #초기값은 무조건 0이기 때문에 중복될수가 없음
            r.append(s[i])  #0은 바로 추가시켜줍니다
        elif s[i] != s[i-1]:#현재 주소와 그전의 주소가 같지 않을경우
            r.append(s[i])  #지금 값을 추가해줍니다
    return r
cs


+ Index ( ) 사용법


1
2
3
4
5
6
7
8
9
#!/usr/bin/python
 
aList = [123'xyz''zara''abc'];
print "Index for xyz : ", aList.index( 'xyz' ) 
print "Index for zara : ", aList.index( 'zara' ) 
위의 프로그램을 실행하면 다음과 같은 결과가 생성됩니다.
 
Index for xyz :  1
Index for zara :  2
cs

+ find ( ) 사용법

1
2
3
4
5
6
7
8
9
10
11
12
#!/usr/bin/python
 
str1 = "this is string example....wow!!!";
str2 = "exam";
 
print str1.find(str2)
print str1.find(str2, 10)
print str1.find(str2, 40)
결과
15
15
-1
cs

+ random ( ) 사용법

1
2
3
4
5
6
7
8
9
10
11
>>> import random
>>> random.random()
0.90389642027948769
 
>>> random.randrange(1,7)
6
>>> random.randrange(1,7)
2
 
>>> range(1,7)
[123456]
cs

에러

1
2
3
 "list.py", line 6in <module>
    reversedlist = ' '.join(toberlist1)
TypeError: can only join an iterable
cs

반복자가 들어가야하는 구분에 다른 Type이 들어와서 생겨서 PC께서 알려주신 축복.

(예상치 못했거나, 원치않게 return 되는 데이터가 있을 때 발생하는 경우 다수)



1
2
3
  File "C:\Users\namki\Desktop\버드뷰테스트.py", line 62in result_formating
    print('가장 높게 매칭된 인원은 : {}'.format(num_index))
UnboundLocalError: local variable 'num_index' referenced before assignment
cs



로컬변수가 있는데 참조타이밍 전에 참조가 되어 PC께서 알려주신 축복.

(갑자기 소스코드 구문을 변경하다가 발생하는경우 다수)



1
2
print("-".join(matched))
TypeError: sequence item 0: expected str instance, int found
cs

Join을 하려는데, 시퀀스 항목이 없고, 예상되는 데이터는 str이며 int형을 찾았다고 나왔다. 입력 데이터는 분명 list type을 확인했지만, join 절에서 추가로 리스트를 문자열로 뽑아내야했다.

해결로는 map을 사용하거나 lambda 혹은 한 줄 라인절을 통한 해결법 등이 있다.














OTL....

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# -*- coding: cp949 -*-
import time, random
#첫 번째 배열을 기준 -> D F Y M J T U Q K Z
#(단, 전부 대문자)
#1차원 배열의 첫 번째 인덱스가 [0]=D일 때  1차원 배열의 순번을 기억해둔다.  (1)(2)
#해당 순번의 배열에서 두 번째 인덱스 [1]=F일 때  일치하는 순번을 기억해두자.
#해당 순번의 배열에서 세 번째 인덱스 [2]=Y일때 일치하는 순번을 기억해두자.
def rendom_str(): #처음 비교를 하기 위해 기준점을 세우는 사람생성(10개의 취미를 가진)
    res= '' #출력할 문자열
    for x in range(10): #열 번 반복
        ran=random.randrange(65,90+1#65~90 index출력
        res+=chr(ran) #해당 인덱스에 해당하는 ascii 문자 반환
    return list(res) #10개 취미를 배열로 리턴
'''
선언 리스트 목록
1) 기준점 리스트(사람)
2) 기준점 리스트와 비교해서 일치하는 리스트(사람)들의 인덱스를 담는 리스트(매칭된 리스트)
3) 기준점 리스트로 쓰고 버려질 리스트들(이미 취미 매칭에 사용한 리스트)
4) 비교하고 몇 개가 일치하는지 적어둘 리스트
*2번 4번을 { dict } 로 담아두자.
'''
def manage_overlap(list_data):
    w_count = {}
    for lst in list_data: #리스트 데이터 반복하기
        #반환할 dict 타입의 인덱스에 삽입.(첫 번째 : 중복갯수, 두 번째, )
        try: w_count[lst] += 1  
        except: w_count[lst]=1 
    print(w_count)
    return w_count #딕셔너리 리턴
#해당 순번의 배열에서 열 번째 인덱스 [9]=Z일 때  일치하는 순번을 기억해두자.
 
 
def get_file_lines():
    data=''
    with open("C:/Users/namki/Downloads/버드뷰_500x10.txt""r") as f:
        data=f.read()
    return data
 
def res_num_compare(data):
    res={}
    for x in data: #딕셔너리 데이터를 반복 출력
        #value값을 비교해서 10부터~1까지 데이터가 있는지 비교
        for y in range(10,0,-1): 
            if data[x] == y: #value와 (10~7)중 일치확인
                #print(y) #일치하는 갯수 확인  #print(x)#일치하는 갯수에 해당하는 key를 출력
                #print("key : {}, value: {}".format(x, y))
                res[x]=y
                #time.sleep(0.1)
            else#일치하지 않음
                pass
    return res
def high_match_index(datas):
    num_index=1
    while(True):
        if datas[0== datas[num_index]:
            num_index += 1  #언젠가는 끝나게 되있기 때문에 계속 +1
        else:  #같지 않을때 num_index반환
            print("high_match_index함수에서 반환된 최고 매칭수: {} 명".format(num_index))
            return num_index
    
def result_formating(datas):
    #num_index에는 과연 매칭이 가장높은애가 몇 명인지 알려줌 
    result = list()
    datas=sorted(datas, key=lambda k : datas[k], reverse=True) #value를 기준으로 역정렬화
    num_index = high_match_index(datas) #선별된 매칭인원
    #join으로 출력데이터 사이에 '-'를 붙히기 위해 미리 배열에 저장해두자.
    #num_index개수 만큼 출력하자
    compare = 0
    for only_key in datas: #미리 정렬된 dict을 하나씩 빼자
        result.append(only_key)
        if compare == num_index: #이 조건문은 딱 최고로 매칭된 사람(key)만큼 출력하기 위해서 선언
            return result#매칭되지 않는 사람이 나오게 된다
        else
            compare += 1 #같은값이 처음부터 나오면 리턴값이 1개뿐이니 2개를 뽑기 위해 0부터 비교
    pass 
    '''
    가장 일치하는 대상자 2명 출력
    만일 3명이상이면 모두 출력
    [결과] value에 해당하는 key를 반환 (formatting해)
    3개이상 일치하나?--일치하는데 해당하는 data(dict 타입)을 담은 배열
    '''
if __name__=="__main__":
    play_cnt = 0#플레이 카운트
    try#잘못된 input값의 예외처리
        coin = input("input : ")
    except:
        coin = 1
        print("잘못된 입력으로 임의의 숫자 '1' 이 입력되었습니다.")        
    coin = int(coin)
    while(play_cnt < coin):  #입력된 데이터 만큼 반복
        play_cnt+=1
        print("coin : {} play_cnt: {}".format(coin, play_cnt))
        data=rendom_str() #['Z', 'C', 'Y', 'A', 'F', 'X', 'U', 'Q', 'K', 'G']
        print("기준 : {}".format(data))
        pass_person=list()
        with open("C:/Users/namki/Downloads/버드뷰_500000x10.txt""r") as f:
            res = f.readlines()
            for x in range(len(res)): #파일에서 가져온 row 수 만큼 반복해서 비교
                for y in data:
                    if y in res[x]:
                       print("결과는 true   사람 인덱스 {} ({})   일치 문자 : {}".format(x, res[x], y))
                       pass_person.append(x)#인덱스를 담자
                    else:
                        pass
            true_ing_list =manage_overlap(pass_person)#true에 반환된 리스트 가공
            res_dict = res_num_compare(true_ing_list) #true중에 나온 데이터를 인자로 넣어서 딕셔너리 리턴값(중복데이터와 : 중복숫자)
            matched = result_formating(res_dict) #가장 매칭이 잘 된 사람이 배열로 출력된다.
            #print("matched 출력해보자")
            #print(matched)
            #print(type(matched))
            print('-'.join(str(v) for v in matched))
        
            
                   
'''
    list_standard=list()
    list_already_usage=list()
    dict_result = dict()
    
        res_data = f.readline() #첫 번째 기준사람
        res_datas=f.readlines() #두 번째 이후 사람들 리스트
        list_standard = res_data.split()
        print(res_datas)
        for l_stand in f.readlines(): #비교될 대상 사람들
            print(l_stand)
            time.sleep(0.2)
            for t_num in range(len(res_data.split())): #한 사람의 취미 개수만큼 반복
                print(res_data.split()[t_num]) #한 사람이 가진 취미를 배열로 만들고 -> 기준배열에 넣기
                #비교 시작(다음번째 라인의 인덱스와 기준점 리스트의 인덱스)
                
                #if res_data.split()[t_num] == l_stand[t_num]
        
        #for x in  f.readlines():
         #   time.sleep(0.2)
         #   print(x)
'''         
        
'''
    res[0]==res[1]
    res[0]==res[2]
    res[0]==res[3]
    res[0]==res[4]
    res[0]==res[5]
    0번째에서 같지 않은 값이 나온다?
    -> res[1]==res[1]
    -> res[1]==res[2]
    -> res[1]==res[3]
    -> res[1]==res[4]
    -> res[1]==res[5]
    1번째에서 같지 않은 값이 나온다?
    ---> res[2]==res[1]
    ---> res[2]==res[2]
    ---> res[2]==res[3]
    ---> res[2]==res[4]
    ---> res[2]==res[5]
    2번째에서 같지 않은 값이 나온다?
    -----> res[3]==res[1]
    -----> res[3]==res[2]
    -----> res[3]==res[3]
    -----> res[3]==res[4]
    -----> res[3]==res[5]
    '''        
 
 
cs