C++

[프로그래머스] 3진법 변환 후 뒤집어 10진법 변환

jeongchanhyo 2025. 2. 5. 20:17

이걸 들고 온 이유는 내 생각대로 문제를 풀고, 결과값은 잘 나오는데 문제에서 오답처리가 계속 돼 어려웠던 문제라 이 문제들을 풀면서 겪었던 과정과 이 과정들을 통해 배운 부분을 기록하기위해서다.

문제

자연수 n이 매개변수로 주어집니다. n을 3진법 상에서 앞뒤로 뒤집은 후, 이를 다시 10진법으로 표현한 수를 return 하도록 solution 함수를 완성해주세요.


제한사항
  • n은 1 이상 100,000,000 이하인 자연수입니다.

입출력 예nresult
45 7
125 229

입출력 예 설명

입출력 예 #1

  • 답을 도출하는 과정은 다음과 같습니다.
n (10진법)n (3진법)앞뒤 반전(3진법)10진법으로 표현
45 1200 0021 7
  • 따라서 7을 return 해야 합니다.

사고 풀이

  1. 숫자를 3진법으로 변환
  2. 변환한 숫자 뒤집기
  3. 다시 10진법으로 변환
  4. 값 반환

이렇게 하면 될것같다.

직접 풀기

1.숫자 3진법으로 변환

int x = 0; int y = 0;
while(n>0)
    {
        x = n%3;
        answer += x * pow(10,y);
        n/=3;
        y++;
    }

우리가 3진법을 하는 방법은 쉽다.(링크 : 수학 카테고리에 업로드 예정)

이거에 따라 계산을 하자면 3의 나머지를 더해주는데 10을 y제곱 하는 과정으로 더해주면 n = 11이라고 쳤을 때

처음에 3으로 나누면 나머지가 2 몫이 3이므로

answer = 2 x 10^0 = 2

n = 3

이 되고 두번째는 나머지 0 몫이 1이므로

answer = 2 + (0 x 10^1)

n = 1

세번째는 나머지 1 몫 0 이므로

x = 2 + (1 x 10^2) = 102

n = 0

이 된다. 그리고 조건을 벗어낫기 때문에 반복문은 종료가 되는 것이다.

2. 숫자 뒤집기

y = 0;
    while(answer > 0 )
    {
        y *= 10;
        y += answer%10;
        answer /= 10;
    }

이건 그냥 숫자를 뒤에서부터 빼서 계속 10을 곱해 앞으로 밀면서 넣어주는거라고 보면 된다.

3. 다시 10진법으로 변환

   while(y > 0)
    {
        answer += y%10*pow(3,x);
        y/=10;
        x++;
    }

이제 3진법을 다시 10진법으로 바꿔야한다. (링크 : )

이건 그냥 자릿수마다 제곱해서 더해주면 된다.

그리고 반환

완성

#include <vector>
#include <cmath>
using namespace std;

int solution(int n) {
    int answer = 0;
    int y = 0;
    int x = 0;
    while(n>0)
    {
        int x = n%3;
        answer += x * pow(10,y);
        n/=3;
        y++;
    }
    y = 0;
    while(answer > 0 )
    {
        y *= 10;
        y += answer%10;
        answer /= 10;
    }
    while(y > 0)
    {
        answer += y%10*pow(3,x);
        y/=10;
        x++;
    }
    return answer;
}

이렇게 하면 n(주어진 값) -> answer(3진법모양의 수) -> y(뒤집힌 3진법) -> answer(10진법 모양의 수) 로 왔다갔다 하면서 완성된다.

값도 제대로 나왔으나 오답이였다.

변경

우선 값은 제대로 나온다 그렇다면 계산 속도가 문제인건가? 싶어서 줄일 방법을 생각해보았더니  처음에 3진법으로 바꿀 때 뒤집어서 적용시키면 되는거였다.

3진법 변환과 숫자 뒤집기 한번에 하기

   int x = 0; int y = 0;
   while(n>0)
    {
        int x = n%3;
        y += y * 10 + x;
        n/=3;
    }

전에는 나머지값에 10의 y제곱해서 더해주었는데 그냥 기존 수 *10하고 나머지를 더해주면 알아서 뒤집힌 형태로 잘 나온다.

#include <vector>
#include <cmath>
using namespace std;

int solution(int n) {
    int answer = 0;
    int y = 0;
    int x = 0;
    while(n>0)
    {
        int x = n%3;
        y += y * 10 + x;
        n/=3;
    }
    x = 0;
    while(y > 0)
    {
        answer += y%10*pow(3,x);
        y/=10;
        x++;
    }
    return answer;
}

하지만 정답이 안나온다.

왜 그런지 몰랐다. 나는 나의 수학적 지식에 의문을 품고 엑셀을 통해 내 계산방법이 맞는지 체크해보았다.

엑셀

45를 3진수로 바꾸면 1200

역순으로하면 21

10진수 변환하면 7이다. 정답은 제대로 나온다.

그렇다면 큰 수도 제대로 되는지 체크를 해보면

이런 답이 나온다.

제대로 되는지 다른 사이트들을 돌면서 검증해보았다.(사이트 링크 : https://ko.calc-site.com/bases/convert_n)

우선 99999999를 3진수로 변환하고 뒤집은 값은 엑셀의 값과 동일하다.

뒤집은 수를 바꾸면

이렇게 정답이 나온다.

그렇다면 내 방식은 일단 틀리지 않았다. 그렇다면 뭐가 문제일까... 모르겠어서 주위에 도움을 요청했더니

99999999이라는 수를 넣어보라고 해서 넣어봤더니.... 전혀 다른 수가 나왔다.

변수 타입 바꾸기

비쥬얼 스튜디오에서 왜 다른 수가 나올까 한번 해봤더니.

중간부터 이상해졌다.

그렇다... int의 최대 저장 가능한 수 범위는? -2,147,483,648 ~ 2,147,483,647 다.

그래서 나는unsigned long long으로 바꿔줬다.

아주 잘 나온다.

그래서 나는 그대로 바로 적었더니...

 

이런 식으로 나온다. 찾아보니 

이 오류는 링커 오류로, 함수 solution(int)의 구현체를 찾을 수 없어 발생합니다. 코드에서 함수 선언과 정의가 일치하지 않거나, 구현 파일이 컴파일되지 않았을 때 나타납니다.

라는 문제라고 한다.... 그래서 입력값인 n을 int로 바꿨더니 

n은 1 이상 100,000,000 이하인 자연수입니다.

이 조건인 99,999,999를 넣으면 0이 나온다...

근데 생각해보면 int면 제한사항이랑 다르지 않나...?라는 생각이 들긴 한다 하지만 제출 시 통과된다.

제한조건을 너무 진지하게 안받아들여도 될거같다.

#include <iostream>
#include <string>
#include <vector>
#include <cmath>
using namespace std;

int solution(int n) 
{
    int answer = 0;
    unsigned long long x = 0;
    unsigned long long y = 0;
    
    while(n>0)
    {
        x = n%3;
        y = y * 10 + x;
        n/=3;
    }
    
    x = 1;
    while(y > 0)
    {
        answer += y%10*x;
        y/=10;
        x*=3;
    }
    
    return answer;
}

결론

하지만 결국 3진법의 형태를 띌 뿐이지 실제로는

221021111022202이라는 엄청난 크기의 수를 갖게되는것이기 때문에 프로그래머스에서 처음에 제시해주는 string을 사용해서 해결하거나 vector를 이용해서 하는게 더 나을거같다. 

나는 어느 순간부터 어떤 자료형 형태를 사용할지 고민을 안하고있었는데 이런거 하나하나 생각을 제대로 하는게 좋겠다고 다시 한번 상기할 수 있었고 최대한 잊지 않도록 여기에 기록해두려고 한다.