[프로그래머스]모의고사

링크 : 문제 바로가기

1.파이썬 코드

def solution(answers):
    answer = []
    cnt=len(answers)
    p1=[1, 2, 3, 4, 5]
    p1_answers=p1*(cnt//5) + p1[0:cnt%5]
    p2=[2, 1, 2, 3, 2, 4, 2, 5]
    p2_answers=p2*(cnt//8) + p2[0:cnt%8]
    p3=[3, 3, 1, 1, 2, 2, 4, 4, 5, 5]
    p3_answers=p3*(cnt//10) + p3[0:cnt%10]
    p1_cnt=0; p2_cnt=0; p3_cnt=0
    for i in range(cnt):
        if p1_answers[i]==answers[i]:
            p1_cnt+=1
        if p2_answers[i]==answers[i]:
            p2_cnt+=1
        if p3_answers[i]==answers[i]:
            p3_cnt+=1
    result= max(p1_cnt,p2_cnt,p3_cnt)
    if p1_cnt==result:
        answer.append(1)
    if p2_cnt==result:
        answer.append(2)
    if p3_cnt==result:
        answer.append(3)
        
    return answer

완전탐색을 이용해 정답과 같은 길이를 가진 3가지 패턴의 리스트를 만들고, 각 리스트를 정답과 일일이 비교하여 가장 정답을 많이 맞춘 리스트들을 구했다.

2.자바 코드와 문제점

import java.util.ArrayList;


public class Solution {

	public ArrayList<Integer> solution(int[] answers) {
		ArrayList<Integer> answer = new ArrayList<Integer>();
        
		int l = answers.length;
		int forCount = 0;
		int forPlus = 0;
		Integer result1 = 0;
		Integer result2 = 0;
		Integer result3 = 0;
		Integer[] pattern1 = { 1, 2, 3, 4, 5 };
		Integer[] pattern2 = { 2, 1, 2, 3, 2, 4, 2, 5 };
		Integer[] pattern3 = { 3, 3, 1, 1, 2, 2, 4, 4, 5, 5 };
		ArrayList<Integer> answers1 = new ArrayList<Integer>();
		ArrayList<Integer> answers2 = new ArrayList<Integer>();
		ArrayList<Integer> answers3 = new ArrayList<Integer>();

		int pattern1Length = pattern1.length;
		forCount = l / pattern1Length;
		forPlus = l % pattern1Length;
		for (int i = 0; i < forCount; ++i) {
			for (Integer oneAnswer : pattern1) {
				answers1.add(oneAnswer);
			}
		}
		for (int i = 0; i < forPlus; ++i) {
			answers1.add(pattern1[i]);
		}

		int pattern2Length = pattern2.length;
		forCount = l / pattern2Length;
		forPlus = l % pattern2Length;
		for (int i = 0; i < forCount; ++i) {
			for (Integer oneAnswer : pattern2) {
				answers2.add(oneAnswer);
			}
		}
		for (int i = 0; i < forPlus; ++i) {
			answers2.add(pattern2[i]);
		}

		int pattern3Length = pattern3.length;
		forCount = l / pattern3Length;
		forPlus = l % pattern3Length;
		for (int i = 0; i < forCount; ++i) {
			for (Integer oneAnswer : pattern3) {
				answers3.add(oneAnswer);
			}
		}
		for (int i = 0; i < forPlus; ++i) {
			answers3.add(pattern3[i]);
		}

		for (int i = 0; i < l; ++i) {
			if (answers[i] == answers1.get(i)) {
				result1 += 1;
			}
		}
		for (int i = 0; i < l; ++i) {
			if (answers[i] == answers2.get(i)) {
				result2 += 1;
			}
		}
		for (int i = 0; i < l; ++i) {
			if (answers[i] == answers3.get(i)) {
				result3 += 1;
			}
		}
		
		Integer maxResult = 0;
		if (result1 > maxResult){
			maxResult=result1;
		}
		if (result2 > maxResult){
			maxResult=result2;
		}
		if (result3 > maxResult){
			maxResult=result3;
		}
		
		
        if (result1 == maxResult){
            answer.add(1);
        }
        if (result2 == maxResult){
            answer.add(2);
        }
        if (result3 == maxResult){
            answer.add(3);
        }
		
		
		return answer;
	}
    
}

자바를 익숙하게 다루기 위해 같은 문제를 자바를 이용해 똑같이 풀어보았다. 하지만 이상하게 같은 로직으로 작성했지만 테스트케이스 10, 11, 12번이 실패했다. 위 코드에서 어떤 부분이 문제일까?

2.1 해결법

문제는 int와 Integer에 있었다.

    if (result1 == maxResult){
            answer.add(1);
    }
    if (result2 == maxResult){
        answer.add(2);
    }
    if (result3 == maxResult){
        answer.add(3);
    }

전체 코드에서 정답을 가장 많이 맞춘 패턴들을 구하는 코드를 잘라내 보았다. 위 코드를 보면 result1(1번패턴정답갯수)이 maxResult(최고정답갯수)와 같은지 비교하기 위해 동등연산자 ‘==’ 을 사용했다. 하지만 Integer는 원시타입이 아닌 참조타입으로 클래스를 기반으로 만들어진 객체다. 즉, ‘Integer객체 == Integer객체’ 이 문장은 Integer객체가 가지고 있는(참조하고 있는) 값이 같냐?가 아니라 Integer객체의 주소값이 같냐?라는 뜻이 된다. 그래서 원래 의도대로 Integer객체가 가지고 있는 값에 대한 비교를 하기 위해서는 equals메서드를 사용해야 한다.

수정된 코드

    if (result1.equals(maxResult)){
            answer.add(1);
    }
    if (result2.equals(maxResult)){
        answer.add(2);
    }
    if (result3.equals(maxResult)){
        answer.add(3);
    }

또한 궁금한 점이 그럼 왜 equals메서드를 사용하지 않고 == 동등연산자를 사용했음에도 불구하고 통과되는 테스트케이스가 꽤 많았을까? 였다. 그 이유는 Integer클래스에 있는 IntegerCache라는 클래스때문이였다. 다음 글은 프로그래머스 질문란에 내가 남겨놓은 내용이다.


글쓴이입니다. 10, 11, 12번 케이스가 실패하신 분들은 이 부분 참고해보시면 좋을 것 같습니다.

알아보니 Integer객체 간 비교에 >나 <같은 비교 연산자는 사용이 가능하지만, 동등 연산자인 == 은 Integer객체의 주소값을
비교하게 되어 문제가 생겼습니다. Integer객체 간 비교를 할 때 Integer객체가 가지고 있는 값에 대한 비교를 하기 위해서는
equals메서드를 사용해야 합니다.           

+이때 궁금할 수 있는 부분        
그럼 equals메서드를 사용하지 않고, == 연산자를 사용했음에도 불구하고 대부분의 테스트케이스는 어떻게 통과했는가?         
Integer에는 -128~127까지는 캐시를 이용해 자동으로 같은 주소를 참조하게 하는 IntegerCache라는 클래스가 선언되어 있습니다.          
ex) 127 이하의 자연수(100)
Integer a = 100;
Integer b= 100;

다음과 같이 a, b변수를 선언 및 초기화하면 IntegerCache로 인해 b변수를 선언하고 초기화할 때 -128~127 사이의 수 중 이미
존재하는 객체가 있는지 없는지 확인하고, 있을 경우 그 객체에 b를 연결만 시켜줌을 통해 같은 값을 지닌 중복 객체 생성을
생략합니다. 즉, 100에 대한 객체는 이미 a를 초기화할 때 생겼으므로 b는 a와 같은 객체를 참조하기만 하게 된 것입니다.
결과적으로 a와 b는 같은 객체를 참조해 객체의 값 100과 객체의 주소값을 모두 공유하게 됩니다. 그래서 이 경우에는 객체의 값을
비교하는 equals메서드와 객체의 주소값을 비교하는 ==동등연산자가 같은 결과를 반환하게 됩니다.

ex) 128 이상의 자연수(128)
Integer a = 128;
Integer b= 128;

다음의 경우에는 a를 초기화할 때 분명히 128값의 객체를 생성했음에도 불구하고 128이 IntegerCache의 범위 밖(-128~127)이여서
b를 초기화할 때는 새로운 128값의 객체를 또 만들게 됩니다. 이 경우에는 a와 b가 참조하는 객체들의 값은 128로 같지만, 참조하고
있는 객체 자체는 달라서(주소값이 다름. a와 b는 그저 같은 값을 가진 서로 다른 객체들을 참조하는 중) equals메서드를 사용하면
a와 b는 같다고 반환하지만, 동등연산자 == 을 이용해 비교하면 a와 b는 같지 않다라는 결과를 반환합니다.

정리
각 패턴별 정답횟수가 127 이하인 경우는 IntegerCache를 통해 같은 주소값을 공유해 == 동등연산자로 비교해도 equals메서드와
같은 결과를 얻었지만, 패턴별 정답횟수가 128 이상인 경우는 IntegerCache의 영향을 받지 않아 같은 정답횟수를 가져도
== 동등연산자로 비교하면 equals메서드와 다른 결과를 얻어 문제가 발생한 것입니다.

추가
ex) 강제로 IntegerCache를 사용하지 않는 경우
Integer a = 100;
Integer b= new Integer(100);
다음과 같이 IntegerCache 범위 안의 숫자라도, 강제로 b에 대한 새로운 객체를 만든다면, a와 b가 참조하는 객체는
서로 다른 객체가 될 것입니다.( 객체의 값은 100으로 동일, 객체 주소값만 다름)

알고리즘 : 완전탐색

results matching ""

    No results matching ""