바위타는 두루미

5.4 다음숫자 본문

Study/Interview준비

5.4 다음숫자

DoRoMii 2019. 8. 3. 18:14
728x90

문제 

양의 정수가 하나 주어졌다. 이 숫자를 2진수로 표기했을때 1비트의 개수가 같은 숫자중에서 주어진 수보다 크지만 가장 작은 수와, 작지만 가장 큰 수를 구하여라 .

 

해결법

이 문제는 두가지 방식으로 풀 수 있다. 원리는 같지만 비트조작으로 하는지, 수학적으로 해결하는지의 차이이다.

우선 비트조작으로 해결하는 것을 살펴보자 

 

- 비트조작으로 다음수 구하기 

주어진 수보다 크지만 가장 작은수는 1의 갯수가 같은 다음 수를 의미할 것이다. 

그러기 위해서는 0수열로 끝나는 수열이 아닌것 ( 다음에 1수열이 나오는 수열) 중에서 맨 오른쪽 0비트를 1로 뒤집고 , 뒤집은 수 보다 오른쪽에 있는 0과 1을 재정렬해서 0은 전부 왼쪽에 1은 전부 오른쪽에 오도록 하면 가장 작은 수가 될 것이다.

 

11011001111100이라는 수가 있다고 가정했을때 

0수열로 끝나는 수열이 아닌 0수열 중에서 가장 오른쪽 0비트는 7번째 수이다.

1 0  1  1 0 0 1 1 1 1 1 0 0
13 12 11 10 9 8 7 6 5 4 3 2 1 0

이 위치를 p라고 하자 

그리고 p를 기준으로 오른쪽에 있는 0과 1의 갯수를 각각 c0와 c1으로 정의 해보자 

그러면, p = 7 , c0 = 2  , c1 = 5 가 될 것이다.

위의 정의에 따라 세가지 단계를 거치면 다음 수를 구할 수 있을 것이다.

1단계  : p위치에 있는 0을 1로 뒤집는다. 

1 0  1  1 0 1 1 1 1 1 1 0 0
13 12 11 10 9 8 7 6 5 4 3 2 1 0

2단계 : p의 오른쪽에 있는 비트들을 0으로 만든다.

1 0  1  1 0 0 0 0 0 0 0 0 0
13 12 11 10 9 8 7 6 5 4 3 2 1 0

3단계 : c1 -1 개의 1을 오른쪽부터 체워넣는다. 

1 0  1  1 0 1 0 0 0 1 1 1 1
13 12 11 10 9 8 7 6 5 4 3 2 1 0

 

1단계는 간단하다 

n |= 1 << p

를 통해  해결할 수있다.

 

2단계는 

하위비트 p개를 제외한 모든 위치가 1로 세팅된 마스크가 필요하고, 그 마스크와 &연산을 통해 하위 비트를 모두 0으로 만들 수 있다.

n &= ~((1 << p ) -1)

 

3단계는 

오른쪽에 c1-1 개의 1을 체워넣기 위해서는 c1 -1 개의 1을 가진 숫자와 | 연산을 통해 해결 할 수 있다.

n |= (1 << (c1 -1) ) -1

 

비트조작으로 이전수 구하기 

다음수 구하기를 반대로 하면 해결할 수 있다. 

0 0  1  1 1 1 0 0 0 0 0 1 1
13 12 11 10 9 8 7 6 5 4 3 2 1 0

1단계 : 1수열로 끝나지 않으면서 (뒤에 0수열이 있는) 맨 오른쪽에 위치한 1비트를 0으로 바꾼다. 이 위치는 p

0 0  1  1 1 0 0 0 0 0 0 1 1
13 12 11 10 9 8 7 6 5 4 3 2 1 0

2단계 : p오른쪽에 있는 모든 비트를 0으로 만든다. 

0 0  1  1 1 0 0 0 0 0 0 0 0
13 12 11 10 9 8 7 6 5 4 3 2 1 0

3단계 : p바로 오른쪽에 (c1 +1)개의 1을 체워 넣는다. 

0 0  1  1 1 0 1 1 1 0 0 0 0
13 12 11 10 9 8 7 6 5 4 3 2 1 0

 

1단계와 2단계는 동시에 할 수 있다.

모든 비트가 1로 체워진 수에서 왼쪽으로 p+1만큼 쉬프트 하면 p 위치까지 비트는 모두 0이다. 이상태에서 &연산을 함으로써 p부터 0까지의 비트를 모두 0으로 바꿀 수 있다.

n &= ~0 << (p+1) 

 

3단계는 

c1+1 개의 1을 가지는 수를 만들고 그 수를 오른쪽으로 c0-1만큼 쉬프트 한 값을 |연산을 해서 해결할 수있다.

n |= ( (1 << (c1 +1) ) -1 ) << (c0 -1)

 

def getNext(n):
    c = n
    c0, c1 = 0, 0
    while c&1 ==0 and c !=0 :
        c0 +=1
        c >>=1
    while c&1 ==1 :
        c1 +=1
        c >>1
    if c0+c1 ==31 or c0+c1 ==0 :
        return -1
    p = c0+c1
    n |= 1 << p
    n &= ~((1 <<p)-1)
    n |= (1<<(c1-1))-1
    return n

def getPrev(n):
    c = n
    c0, c1 = 0, 0
    while c&1 ==1 :
        c1 +=1
        c >>=1
    if c ==0 :
        return -1
    while c&1 ==0 && c != 0 :
        c0 +=1
        c >>=1
    p = c0+ c1
    n &= ~0 << (p+1)
    mask = (1<<(c1+1)-1)
    n |= mask << (c0-1)
    return n

 

-수학적 해법으로 다음수 구하기

원리는 같다. 

1단계 : p번째 비트를 1로 바꾼다.

2단계 : p번째를 기준을오 오른쪽에 있는 모든 비트를 0으로 만든다.

3단계 : 오른쪽에 c1 -1 개의 비트를 1로 체워 넣는다 

 

1단계와 2단계를 빠르게 할 수 있는 방법이 있다. 

맨 오른쪽에 있는 0을 1로 체워 넣고 +1을 하게되면 연속된 1비트들이 전부 0으로 바뀌면서 마지막에 p번째 비트가 1로 바뀔 것이다.

n += 2^c0 -1 

n += 1

 

3단계를 수학적으로 해결하면 

n += 2^(c1-1) -1 

즉, 이 세단계를 수식으로 보면 n += 2^c0 + (2^(c-1)-1) -1  이다.

이 방법은 비트 조작 연산을 첨가하면 코딩하기가 간편하다는 장점이 있다. 

return n+(1<<c0)  + (1 << (c1-1) ) -1

 

-수학적 해법으로 이전수 구하기 

마찬가지로 원리는 같다. 

1단계 : p번째 비트를 0으로 바꾼다.

2단계 : p번째를 기준으로 오른쪽에 있는 모든 비트를 1로 만든다.

3단계 : (c0-1) 번째 비트부터 0번째까지 모든 비트를 0으로 만든다. 

 

이번에도 1단계와 2단계를 빠르게 할 수 있는 방법이 있다. 

가장 오른쪽에 있는 1을 0으로 바꾸고 -1을 하게되면 p는 0이되고 p보다 오른쪽에 있는 모든 수는 1로 만들어진다. 

n -= 2^c1 -1

n -=1

 

3단계를 수학적으로 해결하면 

n -= 2^(c0-1) -1 

즉, 이 세단계를 수식으로 보면 n = n - 2^c1 -2^(c0-1) -1

비트조작 연산을 첨가하면 return n - (1 << c1) - (1 << (c0-1)) -1 으로 표현 할 수 있다. 

def getNext(n):
    c = n
    c0, c1 = 0, 0
    while c&1 ==0 and c !=0 :
        c0 +=1
        c >>=1
    while c&1 ==1 :
        c1 +=1
        c >>1
    if c0+c1 ==31 or c0+c1 ==0 :
        return -1

    p = c0+c1
    return n + (1 << c0) + (1 << (c1-1)) -1


def getPrev(n):
    c = n
    c0, c1 = 0, 0
    while c&1 ==1 :
        c1 +=1
        c >>=1
    if c ==0 :
        return -1
    while c&1 ==0 && c != 0 :
        c0 +=1
        c >>=1
    
    return n - (1 << c1) - (1 << (c0-1)) -1

'Study > Interview준비' 카테고리의 다른 글

5.6 변환  (0) 2019.08.03
5.5 디버거  (0) 2019.08.03
5.3 비트 뒤집기  (0) 2019.08.02
5.2 2진수를 문자열로  (0) 2019.08.02
5.1 삽입  (0) 2019.08.02
Comments