콜라츠 추측 / 핸드폰 번호 가리기

나의 기록·2026년 6월 10일

코딩테스트

목록 보기
12/35

콜라츠 추측 (Collatz Conjecture)

문제 설명

1937년 Collatz가 제기한 추측으로, 주어진 수가 1이 될 때까지 아래 작업을 반복하면 모든 수를 1로 만들 수 있다는 추측이다.

  • 짝수라면 → n / 2
  • 홀수라면 → 3n + 1

작업을 몇 번 반복해야 1이 되는지 반환하는 함수를 작성한다.

예시

6 → 3 → 10 → 5 → 16 → 8 → 4 → 2 → 1  (총 8번)

제한 조건

  • 주어진 수가 1인 경우 → 0 반환
  • 500번 반복해도 1이 되지 않으면 → -1 반환
  • 입력 범위: 1 이상 8,000,000 미만

내 풀이

1차 코드

class Solution {
    public int solution(int num) {
        int answer = 0;

        while(true){
            if(num==1){
                answer=0;  // ❌ break 없음
            }
            if(num%2==0){
                num=num/2;
                answer++;
            }else if(num%2==1){
                num=(num*3)+1;
                answer++;
            }
            if(answer>=500){
                answer=-1;
                break;
            }
            if(num==1){
                break;
            }
        }
        return answer;
    }
}

문제점 1 - break 누락
num=1로 시작하면 answer=0 초기화만 하고 루프를 탈출하지 않는다.
이후 홀수 조건(1%2==1)에 걸려 num=4가 되고 루프가 계속 돈다.

문제점 2 - int 오버플로우
626331 케이스에서 num * 3 + 1 연산 중간에 int 범위(약 21억)를 초과하는 값이 발생한다.
오버플로우가 나면 num이 음수가 되고, num==1 조건을 영원히 만족하지 못해 무한루프(10초 이상 멈춤) 에 빠진다.

2차 코드 - break 수정

if(num==1){
    answer=0;
    break;  // ✅ break 추가
}

num=1 예외는 해결했지만 오버플로우 문제는 아직 남아있다.

최종 코드

class Solution {
    public int solution(int num) {
        int answer = 0;
        long targetNum = num;  // ✅ long으로 오버플로우 방지

        while(targetNum > 1){  // ✅ num=1 예외처리 겸용
            if(targetNum % 2 == 0){
                targetNum = targetNum / 2;
            } else {
                targetNum = (targetNum * 3) + 1;
            }
            answer++;

            if(answer >= 500){
                answer = -1;
                break;
            }
        }

        return answer;
    }
}

while(targetNum > 1) 조건 자체가 num=1 예외처리 역할을 겸한다.
num=1로 시작하면 루프에 진입하지 않고 answer=0을 그대로 반환한다.


배운 점

int vs long
| 타입 | 최댓값 | 비고 |
|------|--------|------|
| int | 약 21억 | 오버플로우 → 음수 → 무한루프 |
| long | 약 922경 | 콜라츠 중간값 안전하게 처리 가능 |

반환값이 int여도 내부 연산 변수는 long 으로 선언하는 것이 안전하다.

n * 3, n * n 처럼 값이 반복적으로 커지는 구조가 보이면 long 타입을 먼저 고려하자.
정확한 최댓값 계산이 어려운 구조일수록 long으로 선언하고 시작하는 게 안전하다.



핸드폰 번호 가리기

문제 설명

전화번호 문자열에서 뒷 4자리를 제외한 나머지를 전부 *로 가린 문자열을 반환한다.

예시

"01033334444" → "*******4444"
"027778888"   → "*****8888"

제한 조건

  • phone_number 길이: 4 이상 20 이하

내 풀이

1차 코드

class Solution {
    public String solution(String phone_number) {
        int frontNumberLeng = phone_number.length() - 4;
        String frontNumber = phone_number.substring(0, frontNumberLeng);
        String backNumber = phone_number.substring(frontNumberLeng, phone_number.length());
        frontNumber = frontNumber.replaceAll("[0-9]", "*");  // 불필요한 정규식

        return frontNumber + backNumber;
    }
}

개선 포인트
frontNumber는 어차피 전부 *로 바꿔야 하는 문자열이다.
정규식으로 숫자를 하나씩 치환할 필요 없이, 길이만큼 *를 채우면 된다.

최종 코드

class Solution {
    public String solution(String phone_number) {
        int len = phone_number.length() - 4;
        return "*".repeat(len) + phone_number.substring(len);
    }
}

배운 점

String.repeat(n)
Java 11부터 지원하는 문자열 반복 메서드.
"*".repeat(7)"*******"
반복문이나 정규식 없이 한 줄로 처리 가능하다.

substring(n)
끝까지 자를 때는 두 번째 인자 생략 가능.

phone_number.substring(len, phone_number.length())  // 동일
phone_number.substring(len)  // ✅ 간결

Java는 파이썬과 달리 음수 인덱스를 지원하지 않는다.
뒤에서 n자리는 substring(length - n) 으로 처리해야 한다.

profile
뭐든 남겨본다

0개의 댓글