<요구 사항>
취미가 가장 비슷한 커플을 매칭 해 주자! 알파벳으로 이루어진 2차원 배열이 입력으로 주어진다. 이 배열에서 각 line은 하나의 사람을 나타내고, 하나의 알파벳은 하나의 취미를 나타낸다. 취미의 종류는 (a-z) 26개이고, 한 사람 당 10개의 취미를 가진다. 한 사람이 중복된 취미를 가질 수는 없다. 취미가 가장 비슷한 커플들을 찾아 내보자.
입력 : 커플 매칭의 대상자의 수가 첫 번째 line에 주어지며(행과 열은 공백으로 구분), 그 다음 line 부터는 취미 값이 주어진다. 취미가 나타나는 순서는 아무런 관계 없다.
입력 샘플 3 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 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, 10, 10, 10, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 13, 13, 14, 14, 15, 15, 15, 15, 15, 16, 16, 16, 17, 17, 17, 17, 17, 17, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 21, 21, 21, 21, 22, 22, 22, 23, 23, 23, 23, 24, 24, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27, 28, 28, 28, 28, 28, 29, 29, 29, 30, 30, 30, 30, 31, 31, 31, 32, 33, 33, 33, 33, 34, 34, 35, 35, 35, 36, 36, 36, 36, 36, 36, 36, 36, 37, 37, 37, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 40, 40, 40, 41, 41, 41, 42, 42, 42, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 45, 45, 46, 46, 46, 46, 47, 47, 47, 47, 48, 48, 48, 49, 49, 49, 50, 50, 50, 51, 51, 51, 51, 51, 51, 52, 52, 52, 53, 53, 53, 54, 54, 54, 54, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 57, 57, 57, 58, 58, 58, 59, 59, 59, 59, 60, 60, 60, 61, 61, 61, 61, 62, 62, 62, 63, 63, 63, 63, 63, 63, 64, 64, 64, 64, 64, 65, 65, 65, 66, 66, 66, 66, 67, 67, 67, 67, 67, 68, 68, 68, 68, 69, 69, 69, 70, 70, 70, 71, 71, 71, 71, 71, 72, 72, 72, 72, 73, 73, 73, 73, 74, 74, 74, 74, 75, 75, 75, 75, 75, 76, 76, 77, 77, 77, 77, 78, 78, 78, 78, 78, 79, 79, 79, 79, 80, 80, 81, 81, 81, 81, 82, 82, 82, 83, 84, 84, 84, 84, 85, 85, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, 87, 87, 88, 88, 88, 88, 89, 89, 89, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96, 97, 97, 97, 97, 98, 98, 98, 98, 99] | 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 = [0, 0, 0, 0, 0, 0, 0, 85, 85, 85, 85, 86, 86, 86, 86, 86, 86, 87, 87, 88, 88, 88, 88, 89, 89, 89, 90, 90, 90, 90, 91, 91, 91, 91, 91, 91, 92, 92, 92, 92, 92, 93, 93, 93, 93, 93, 94, 94, 94, 94, 95, 95, 95, 95, 96, 96, 96, 97, 97, 97, 97, 98, 98, 98, 98, 99] 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 {0: 7, 85: 4, 86: 6, 87: 2, 88: 4, 89: 3, 90: 4, 91: 6, 92: 5, 93: 5, 94: 4, 95: 4, 96: 3, 97: 4, 98: 4, 99: 1} | 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 |
1 2 3 | print('-'.join(str(v) for v in matched)) >> 71-22 | cs |
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 |
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 |
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) [1, 2, 3, 4, 5, 6] | cs |
에러
1 2 3 | "list.py", line 6, in <module> reversedlist = ' '.join(toberlist1) TypeError: can only join an iterable | cs |
반복자가 들어가야하는 구분에 다른 Type이 들어와서 생겨서 PC께서 알려주신 축복.
(예상치 못했거나, 원치않게 return 되는 데이터가 있을 때 발생하는 경우 다수)
1 2 3 | File "C:\Users\namki\Desktop\버드뷰테스트.py", line 62, in 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 |
'프로그램언어+ > Python' 카테고리의 다른 글
파이썬으로 데이터 분석(2) - 정규표현 (0) | 2019.01.08 |
---|---|
파이썬으로 데이터 분석(1) (0) | 2019.01.08 |
★ 파이썬 코드를 효율적으로 짜는 6가지 습관 ★ (0) | 2019.01.03 |
파이썬 정규표현식 사용하기 (re modual) (0) | 2019.01.02 |