[TIL #18] 2개 이하로 다른 비트

2024. 9. 30. 19:25·Camp/T.I.L.

https://school.programmers.co.kr/learn/courses/30/lessons/77885

 

프로그래머스

코드 중심의 개발자 채용. 스택 기반의 포지션 매칭. 프로그래머스의 개발자 맞춤형 프로필을 등록하고, 나와 기술 궁합이 잘 맞는 기업들을 매칭 받으세요.

programmers.co.kr

 

비트를 다루는 문제.

결국 현대의 컴퓨터는 이진수로 동작하기 때문에,

비트 연산에 대해 잘 알아둘 필요가 있다.

그만큼 그 성질을 잘 이해하고 사용할 필요가 있다.

이번에도 엄청 헤맸다...

 

일단 이 문제는 정수 ``x``에 대해 해밍거리가 2 이하인 수 중에서 가장 작은 수를 찾는 문제다.

즉, 몇 개의 비트가 차이가 나는지 비교해 볼 필요가 있다.

C++이라면 ``std::bitset::count`` 같은 내장 함수로 비트의 개수를 가져올 수 있지만 JS엔 없는 것 같다.

그래서 일단 각 거리 별 비트마스크를 생성해 그걸 통해 XOR연산을 하며 값들을 찾기로 했다.

그 코드는 다음과 같다.

 

// 해밍거리가 1이나 2인 수들이 필요
// Brian Kernighan's 알고리즘 대신
// 각 거리를 위한 비트마스크를 만들어 비트 차이 계산

function solution(numbers) {
    const answer = [];

    // 비트 마스크 사전 생성
    const bitMasks1 = [];
    const bitMasks2 = [];
    const maxBits = 50; // 10^15는 50비트로 표현 가능

    // 거리 1을 위한 비트마스크 생성
    for (let i = 0; i < maxBits; i++) {
        bitMasks1.push(1n << BigInt(i));
    }

    // 거리 2를 위한 비트마스크 생성
    for (let i = 0; i < maxBits; i++) {
        for (let j = i + 1; j < maxBits; j++) {
            bitMasks2.push((1n << BigInt(i)) | (1n << BigInt(j)));
        }
    }

    // 거리 1인 마스크 먼저 검사 후, 거리 2인 마스크 검사
    for (let i = 0; i < numbers.length; i++) {
        const num = BigInt(numbers[i]);
        let minCandi = null;

        // 거리 1 검사
        // XOR 연산을 통해 비트 N개가 반전된 새로운 수 생성
        for (let mask of bitMasks1) {
            const candi = num ^ mask;
            if (candi > num) {
                if (minCandi === null || candi < minCandi) {
                    minCandi = candi;
                }
            }
        }

        // 거리 2 검사
        if(minCandi !== null) {
            for (let mask of bitMasks2) {
                const candi = num ^ mask;
                if (candi > num) {
                    if (minCandi === null || candi < minCandi) {
                        minCandi = candi;
                    }
                }
            }
        }

        // 최소 후보가 존재하면 그 수를 변환, 아니면 null
        answer.push(minCandi !== null ? Number(minCandi) : null);
    }

    return answer;
}

결국은 터졌다.

나름 무식하게 브루트 포스로 하지 않았기에 나쁘지 않을 거라고 생각했는데...

 

사실상 시간만 보면 전부 실패라는 느낌.

이렇게 시간이 걸려선 안될 일이라고 생각했다.

``BigInt``를 사용하지 않으면 뒤쪽의 테스트에서 에러가 생긴다.

분명 ``10^15``는 ``Number``의 범위 안에 있을 텐데...

계속 문제가 생기므로 다른 접근법을 찾아야 했다.

뭔가 심플하게 나올 규칙성이 있지 않을까 고민했다.

 

 

문제를 다시 잘 보자.

정수 ``x``에 대해 해밍거리가 2 이하인 ``x``보다 큰 수를 찾아야 한다.

만약 최우측 비트가 0이라면?

그냥 그 부분을 1로 만들면 된다.

기존 수 보다 1만 커지면서 조건을 만족한다.

짝수는 이걸로 필터링 가능하다.

그럼 홀수는?

 

홀수에도 규칙이 있다.

문제에서 제시하는 예시를 보자.

``0111``에 ``1011``이 정답이다.

다른 예시로 ``1111``을 생각해 보자.

이 경우 정답은 ``10111``이 된다.

 

``1011`` - ``0111`` = ``100`` = ``2 ^ 2``

``10111`` - ``1111`` = ``1000`` = ``2 ^ 3``

이러면 규칙성이 보이는 것 같다.

정수 ``x``의 비트에서 1인 연속되는 횟수만큼 에서 1을 빼고 그 수만큼 2를 제곱한 값이 키가 된다.

1을 뺀 수를 ``c``라고 할 때, 식은 다음과 같은 형태가 된다.

$$ f(x) = x + 2^{c-1} $$

 

이걸 코드로 옮기면 다음과 같다.

// 연속된 1의 개수를 세는 함수
function countTrail(x) {
    let count = 0;
    while ((x & 1n) === 1n) {
        count += 1;
        x >>= 1n;
    }
    return count;
}

function solution(numbers) {
    // 각 숫자에 대해 f(x)를 계산
    return numbers.map((num) => {
        let x = BigInt(num);
        if (x % 2n === 0n) {    // 짝수인 경우
            return Number(x + 1n);
        } else {    // 홀수인 경우      
            let c = countTrail(x);
            // c가 1이여도 x + 1이 된다.
            let increment = 1n << BigInt(c - 1);
            return Number(x + increment);
        }
    });
}

복잡할 것도 없고 엄청 간결해졌다.

 

시간도 잘 나왔다.

비트에서의 해밍거리가 2인 문제는 앞으로도 이를 응용해서 풀 수 있을 것 같다.

3부터는 확실히 복잡해질 것 같다는 느낌이 든다...

'Camp > T.I.L.' 카테고리의 다른 글

[TIL #20] 쿼드압축 후 개수 세기  (0) 2024.10.02
[TIL #19] 다리를 지나는 트럭  (0) 2024.10.01
[TIL #17] 롤케이크 자르기  (0) 2024.09.26
[TIL #16] 모음사전  (0) 2024.09.25
[TIL #15] 주차 요금 계산  (0) 2024.09.24
'Camp/T.I.L.' 카테고리의 다른 글
  • [TIL #20] 쿼드압축 후 개수 세기
  • [TIL #19] 다리를 지나는 트럭
  • [TIL #17] 롤케이크 자르기
  • [TIL #16] 모음사전
BVM
BVM
  • BVM
    E:\
    BVM
  • 전체
    오늘
    어제
    • 분류 전체보기 (168)
      • Thoughts (14)
      • Study (69)
        • Japanese (3)
        • C++ & C# (46)
        • Javascript (3)
        • Python (14)
        • Others (3)
      • Play (1)
        • Battlefield (1)
      • Others (11)
      • Camp (73)
        • T.I.L. (57)
        • Temp (1)
        • Standard (10)
        • Challenge (3)
        • Project (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

    • 본 블로그 개설의 목적
  • 인기 글

  • 태그

    cloudtype
    클라우드
    boost
    네트워크 프로그래밍
    Server
    포인터
    discord
    로깅
    OSI
    docker
    FF14
    암호화
    7계층
    db
    IOCP
    베데스다
    Network
    스타필드
    C++
    프로그래머스
    bot
    Python
    서버
    Dalamud
    c#
    discord py
    네트워크
    JS
    Selenium
    Asio
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.2
BVM
[TIL #18] 2개 이하로 다른 비트
상단으로

티스토리툴바