본문 바로가기

파이썬 공부/코딩 테스트 코드 분석

파이썬 주사위 눈금의 합의 중복, 자리수들의 합

 

이 블로그의 앞으로 파이썬 문제들은 인프런 김태원 강사의 파이썬 알고리즘 문제 강의의 문제임을 밝힙니다.

첫번째 문제인 주사위 눈금의 합 중복 문제는

두 개의 정 N면체와 정 M면체의 두 개의 주사위를 던져서 나올 수 있는 눈의 합 중 가장 확률이 높은 숫자를 출력하는 프로그램 정답이 여러 개일 경우 오름차순 출력

 

내가 접근한 방식은 이러했다.

 

m,n=map(int, input().split())
#m과n만큼의 주사위가 주워짐
sumlist=[]
countdic={}
finalkeysortedlist=[]

for i in range(1,n+1):
	for j in range(1,m+1):
		sumlist.append(i+j)
# 주사위의 모든 경우의 수를 계산하여 sumlist에 집어넣음

for k in sumlist:
	#sumlist에 있는 주사위의 합들에 중복을 세기 k에 합을 넘김
	try: #이거부터
		countdic[k]+=1 #딕셔너리의 k(주사위의 합을 의미)번째 방에 값을 넣음
	except:#위 코드가 안되면 이거
		countdic[k]=1 # 방없으면 만듦

#오름차순으로 출력해야함 제일 많은 애 기준
#키를 람다 값으로 주고
countdicsortedlist=sorted(countdic.items(), key= lambda item:item[1],reverse=True)
#딕셔너리의 밸류 키 값을 정렬할것, key값을 람다로 즉 함수로 밸류값 기준으로 정렬
#중복이 많은 값 기준으로 내림차순으로 정렬
sorteddic=dict(countdicsortedlist)

#다시 키 값을 출력 해야함
valuesorted=sorteddic.values()
#밸류 값만을 가져옴
valuesorted=list(valuesorted)
#리스트로 형변환


for key,value in sorteddic.items():
	#key,value값을 반복
	nowvalue=int(value)
	#value 값 인트 형변환
	if (nowvalue==valuesorted[0]):
		finalkeysortedlist.append(key)


print(finalkeysortedlist)

 

여기에는 나의 첫번째 장벽인

어떻게 리스트를 딕셔너리로 바꾸냐?를 먼저 언급하겠다.

쓰인 문법은 try, except 문법으로 try의 코드가 시행이 안되면 except 코드를 실행하는 방식의 문법으로 이해했다.

그렇게 모든 주사위 눈의 합을 countdic 딕셔너리에 넣었고 그 값을 정렬하기 위해

나의 두번째 장벽 sorted 함수인데, key를 람다로 주고, 딕셔너리의 밸류를 기준으로 정렬하면 된다는 것을 알았다.

즉 item[0]은 키, [1]은 밸류값을 상징한다는 것도 알았다. 

정렬한 것을 sorteddic에 넣었고 밸류값은 이미 정렬되어있으므로 밸류값만을 나중에 비교하기 위해 valuesorted라는 리스트에 넣어놓고 sorteddic에서 for문을 돌려 0인덱스부터 value값을 비교하여 마지막 최종 답안인 finalkeysortedlist에 넣어서 문제를 마무리했다.

 

여기서 나의 실수는 2가지가 있는데

굳이 딕셔너리로 바꿨다는 사실이고 valuesotred라는 리스트의 사용처가 거의 없다는 것때문에 메모리 효율이 굉장히 안좋은 코드가 됐다는 것이다.

강사의 코드는 여기서 딕셔너리를 사용하지 않고

cnt라는 리스트에다가 중복된 값을 저장할 목적으로 선언할 때

cnt=[0]*(m+n+3)이라는 형태로 구현했는데 이는 그림으로 표현하자면

 

 

잘못그리니까 양해바랍니다.

m,n이라는 주사위의 합을 바로 cnt라는 리스트에 인덱스 번호와 일치하다면 해당 인덱스에 1을 더하는 식으로 구현했다라는 것이다.  그래서 내 코드와는 달리 굉장히 간결하게 구현할 수 있었다...

놀라운점은 파이썬은 그냥 리스트를 cnt=[0]*(m+n+3)  이런식으로도 구현 할 수 있다는 점이다.

 

2번째 문제는

 

N개의 자연수가 입력 각 자연수의 자릿수의 합을 , 그 합이 최대인 자연수를 출력
 각 자연수의 자릿수의 합을 구하는 함수를 def digit_sum(x)를 꼭 작성하기

 

라는 문제였고 내가 접근한 방식은 이러했다.

 

def digit_sum(x):
	# 최대 숫자는 999만
	nsums = []

	for j in range(7,-1,-1): #7~0까지 줄어드는 형태 j 값이
		check10 = 10**j #현재 자리 수를 상징하는 숫자를 정함
		if (x<10):# 받은 값이 1자리 수 일때 442라면 여기는 안감
			nsums.append(x//1)
			break
		elif (x>check10):
			#x값이 j*10보다 크다? 클때까지 즉 j가 계속 작아지면서 체크
			#j*10보다 x가 큰 경우 j는 큰 수에서부터 내려오면서 체크하기때문에 현재 j*10은 현재 x의 최대 자리수임
			if(j==0): # 마지막 자리수는 0의 갯수가 없으므로 따로 처리
				nsums.append(x//1)
			else:
				nowmok=x //check10
				#현재 최대 자리 숫자의 몫을 구함
				nsums.append(nowmok)
				#현재의 몫을 정리할 리스트에 넣어둠
				x-=(check10*nowmok)
				#현재 최대 자리 숫자를 날림
	return sum(nsums)



#입력받은 숫자들의 각 자릿수를 더해서 최대 크기인 입력받은 숫자 자릿수합이 같을 경우에는 입력순으로 출력
Ncount=int,input()
n=list(map(int, input().split()))
sumlist=[]
#함수 리턴값을 넣어둘 장소
inputcountlist=[]
#입력순서

for i in n:
	inputcountlist.append(n.index(i))
		#입력받은 정수들의 갯수만큼 반복
		#함수호출
	sumlist.append(digit_sum(i))
	#받은 자리수 다 합친 값을 섬리스트에 넣음

listdic=dict(zip(inputcountlist,sumlist))
#딕셔너리로 만들어서 스스로의 인덱스값을 키, 밸류를 합친값으로 넣어둠
flag=max(sumlist)
#최대값

print(listdic)

for key,value in listdic.items():
	if (flag==value):
		#합친값과 합친 것들의 최대 값이 같다면
		print(n[key])

일단 매우 비효율적으로 작성했다

digit함수는 작성자체는 잘 한 것 같은데 조건문을 좀 더 다듬을 여지가 있어보인다는 점이 아쉽고

그리고 또 저번문제의 여파로 딕셔너리에 집착하면서 작성한 후반부 출력부분 코드가 아쉽다.

그렇지만 그만큼 딕셔너리의 활용은 얻은 것 같다.

그리고 입력시 n개의 자연수를 입력받을 때 n개 이상을 받은 경우의

예외처리를 하지 않았다라는 걸 이제 발견해서 굉장히 아쉽다..

 

정답코드에서는 당연히 조건부를 강사분은 굉장히 효율적으로 작성하였다.

 

입력받은 x에 대해

sum이라는 변수를 선언하여

while x>0:

    sum=sum+x%10

    x=x//10

라는 3줄로 끝냈는데

sum에 10으로 나눈 나머지를 넣어 현재 x의 일의 자리 숫자를 가져오고

x를 다시 10으로 나눈 몫을 넣어 sum에 넣어둔 일의 자리 숫자를 날려서 자리수를 지우는 식으로 만든 코드이다

 

그림으로 표현하자면

x가 0보단 큰 동안 하는 수행을 그려봤다..

대충 이정도로 표현할 수 있겠다. 이게 대학생이 맞나 싶은 그림이지만 나에게 많은 도움이 되기를..