if-else 또는 다른 비교 연산자를 사용하지 않고 최대 2 개의 정수를 찾는이 스 니펫을 설명 하시겠습니까?


78

최대 두 개의 숫자를 찾으십시오. if-else 또는 다른 비교 연산자를 사용하면 안됩니다. 온라인 게시판에서이 질문을 찾았으므로 StackOverflow에서 질문해야한다고 생각했습니다.

예제 입력 : 5, 10 출력 : 10

이 솔루션을 찾았습니다. 누군가이 코드 줄을 이해하도록 도울 수 있습니까?


1
기본적으로 프로세서가 .NET에서 수행하는 것과 동일하기 때문에 부호 비트 부정 행위를 고려할 것입니다 <.
starblue 2011 년

7
C ++의 경우 적어도 5.8.3에서 E1 >> E2에 대해 설명합니다. "E1에 부호있는 유형과 음수 값이있는 경우 결과 값은 구현에 의해 정의됩니다.", 따라서 "c >> 31"은 이동하거나 이동하지 않을 수 있습니다. 최하위 비트에 most-에서 부호 비트 ...
토니 델로이

1
어쨌든 비트 31이 부호 비트라는 것을 알 수 없습니다.
Antti Haapala

3
아무도이 질문 텍스트,도 C.입니다 태그 읽기 때문에 어쨌든,이 질문은 폐쇄되어야한다
안티 Haapala

아무도 주목하지 않았기 때문입니다. 이것은 위장 된 형태로 여기 에서 발견 된 유명한 비트 트위들 링 해킹 입니다.
kaartic jul.

답변:


121

이것을 해부합시다. 이 첫 번째 줄은 간단 해 보입니다 . a및 의 차이를 저장합니다 b. 이 값은 음수이고 a < b그렇지 않으면 음수가 아닙니다. 여기에 버그가 실제로있다 - 숫자의 차이가있는 경우 a와는 b죄송합니다 -이 정수에 맞지 않을 수 있도록 큰,이 정의되지 않은 동작으로 이어질 것입니다! 여기서는 그런 일이 발생하지 않는다고 가정 해 봅시다.

다음 줄에서

아이디어는의 값 c이 음수 인지 확인하는 것입니다 . 거의 모든 현대 컴퓨터에서 숫자는 2의 보수 라는 형식으로 저장됩니다. 숫자 의 가장 높은 비트는 숫자가 양수이면 0이고 숫자가 음수이면 1입니다. 또한 대부분의 int는 32 비트입니다. (c >> 31)숫자를 31 비트 아래로 이동하여 숫자의 가장 높은 비트를 가장 낮은 비트에 남겨 둡니다. 이 숫자를 가져 와서 1 (이진 표현이 마지막 비트를 제외한 모든 곳에서 0이 됨)과 AND 처리하는 다음 단계는 모든 상위 비트를 지우고 가장 낮은 비트를 제공합니다. 의 가장 낮은 비트 c >> 31가의 가장 높은 비트이므로 c가장 높은 비트를 c0 또는 1로 읽습니다. 가장 높은 비트는 1이므로 iff c는 1이므로 다음 여부를 확인하는 방법입니다.c음수 (1) 또는 양수 (0)입니다. 이 추론을 위와 결합하면 k1이면 1이고 a < b그렇지 않으면 0입니다.

마지막 단계는 다음을 수행하는 것입니다.

만약 a < b, k == 1그리고 k * c = c = a - b, 그래서

어떤 때문에, 올바른 최대입니다 a < b. 그렇지 않은 경우 a >= b, 다음 k == 0

또한 올바른 최대 값입니다.


7
이 모든 것은 오버플로가 없다고 가정합니다.
피터 테일러

4
@templatetypedef, a = 0x80000000 (int의 최소값) 및 b = 1이라고 가정합니다. c는 무엇입니까?
피터 테일러

4
@Peter Taylor- 좋은 지적입니다. 이 답변을 내놓지 않았습니다. 나는 단지 OP의 코드를 설명하고 있었다. :-) 그러나 숫자가 너무 멀리 떨어져 있으면 이것이 깨질 것이라는 것이 맞습니다.
templatetypedef

1
@templatetypedef, 알아요 : 당신이 당신의 글을 올렸을 때 나는 매우 유사한 답변을 작성하는 중이었습니다. 나는 OP의 이익을 위해 그것을 지적하고 있었다.
피터 테일러

3
@Ani, 상단 비트는 통과하는 모든 위치로 이동합니다. 대안은 다음과 같습니다max = a + (c >> 31) * c
Peter Taylor

28

여기 있습니다 : (a + b) / 2 + |a - b| / 2


1
이것의 수학적 논리를 알고 있습니까?
Senthil Kumaran 2011 년

6
@ mike.did : 할 수 있습니까 | a-b | 조건부없이?
templatetypedef

8
절대 값이이 문제에 사용될 수있는 연산자라고 가정하는 것은 일종의 스트레칭입니다. 수학적으로는 좋은 대답이지만 받아 들여질 것 같지는 않습니다.
Keith Irwin

5
-1 int에서 잘못되었습니다 (3 + 2) / 2 + |3 - 2| / 2 = 2 + 0 = 2 != 3.
starblue 2011 년

4
@starblue : ((3 + 2) + | 3-2 |) / 2 = 3 여기에서 자리를 봅니다.
Michael Foukarakis 2011 년

21

비트 해킹 사용

알고 있다면 INT_MIN <= x - y <= INT_MAX,다음을 사용할 수 있습니다 (x - y). 한 번만 평가하면 되므로 더 빠릅니다 .

출처 : Sean Eron Anderson의 Bit Twiddling Hacks


14
첫 번째 제안은 "비교 연산자 없음"제약 조건을 위반합니다.
Matthieu M.

12

이것은 mike.dld의 솔루션 과 동일한 기술을 기반으로 하지만 여기서 내가하는 일이 덜 "명백하지"않습니다. "abs"연산은 무언가의 부호를 비교하는 것처럼 보이지만 여기에서는 sqrt ()가 항상 양의 제곱근을 반환하므로 제곱 (ab)을 제곱 한 다음 제곱으로 작성합니다. 다시 응원하고 a + b를 더하고 2로 나눕니다.

예를 들어 10과 5의 사용자 예제에서는 sqrt (100 + 25-100) = 5를 얻은 다음 10을 더하고 5를 더하면 20을, 2로 나누면 10이됩니다.

9와 11을 숫자로 사용하면 (sqrt (121 + 81-198) + 11 + 9) / 2 = (sqrt (4) + 20) / 2 = 22/2 = 11


A * A는 것입니다 쉽게 오버 플로우
Dvole

8

가장 간단한 대답은 다음과 같습니다.


6

이 솔루션은 곱셈을 방지합니다. m은 0x00000000 또는 0xffffffff입니다.


4

이동 아이디어를 사용하여 다른 사람이 게시 한 기호를 추출하는 다른 방법은 다음과 같습니다.

이것은 인덱스가 두 숫자 사이의 차이의 부호 비트 인 배열 요소가 제공하는 최대 숫자를 가진 배열로 두 숫자를 밀어 넣습니다.

다음 사항에 유의하십시오.

  1. 차이 (a - b)가 넘칠 수 있습니다.
  2. 숫자에 부호가없고 >>연산자가 논리적 오른쪽 시프트를 참조하는 경우 & 1는 필요하지 않습니다.

4

내가 그 일을 할 것이라고 생각하는 방법은 다음과 같습니다. 당신이 좋아하는 것만 큼 가독성이 좋지는 않지만 "X를하는 명백한 방법을 사용하지 않고 X를 어떻게 수행합니까?"로 시작하면 그것을 기대해야합니다. 이론적으로 이것은 약간의 이식성을 포기합니다. d 문제를보기 위해 매우 특이한 시스템을 찾아야합니다.

이것은 질문에 표시된 것보다 몇 가지 장점이 있습니다. 우선 32 비트 정수에 대해 하드 코딩되는 대신 올바른 시프트 크기를 계산합니다. 둘째, 대부분의 컴파일러에서 우리는 컴파일 타임에 모든 곱셈이 일어날 것으로 예상 할 수 있으므로 런타임에 남은 것은로드와 리턴이 뒤 따르는 사소한 비트 조작 (빼기와 시프트)뿐입니다. 요컨대, 이것은 원본이 런타임에 발생해야하는 곱셈을 사용했던 가장 작은 마이크로 컨트롤러에서도 꽤 빠르다는 것이 거의 확실하므로 데스크톱 컴퓨터에서는 꽤 빠르지 만 종종 꽤 빠를 것입니다. 작은 마이크로 컨트롤러에서는 조금 느립니다.


2

그 라인이하는 일은 다음과 같습니다.

c는 ab입니다. c가 음수이면 a <b입니다.

k는 c의 부호 비트 인 c의 32 번째 비트입니다 (32 비트 정수라고 가정합니다. 64 비트 정수가있는 플랫폼에서 수행하면이 코드는 작동하지 않습니다). 가장 오른쪽에있는 31 비트를 제거하기 위해 오른쪽으로 31 비트 이동하고 가장 오른쪽에있는 부호 비트를 남겨두고 1로 이동하여 왼쪽에있는 모든 비트를 제거합니다 (c가 음수이면 1로 채워짐). 따라서 c가 음수이면 k는 1이되고 c가 양수이면 0이됩니다.

그러면 max = a-k * c. c가 0이면 a> = b를 의미하므로 max는 a-0 * c = a입니다. c가 1이면 a <b 다음 a-1 * c = a-(a-b) = a-a + b = b를 의미합니다.

전반적으로보다 크거나 작은 작업을 사용하지 않도록 차이의 부호 비트를 사용하고 있습니다. 이 코드가 비교를 사용하지 않는다고 말하는 것은 솔직히 약간 어리석은 일입니다. c는 a와 b를 비교 한 결과입니다. 코드는 비교 연산자를 사용하지 않습니다. 숫자를 빼고 상태 레지스터에 설정된 값을 기준으로 점프하여 많은 어셈블리 코드에서 비슷한 작업을 수행 할 수 있습니다.

또한이 모든 솔루션이 두 숫자가 정수라고 가정하고 있음을 추가해야합니다. 만약 그것들이 float, double, 또는 더 복잡한 것 (BigInts, Rational numbers 등)이라면 정말로 비교 연산자를 사용해야합니다. Bit-tricks는 일반적으로이를 위해하지 않습니다.


1

논리 연산이없는 getMax () 함수


설명:

'최대'를 조각으로 부수고

따라서 함수는 다음과 같아야합니다.

지금,

정수 양수에서 첫 번째 비트 (부호 비트)는 -0입니다 . 음수는 -1 입니다. 비트를 오른쪽 (>>)으로 이동하여 첫 번째 비트를 캡처 할 수 있습니다.

오른쪽 시프트 동안 빈 공간은 부호 비트로 채워집니다. 따라서 01110001 >> 2 = 00011100 , 반면 10110001 >> 2 = 11101100 .

결과적으로 8 비트 숫자 시프 팅의 경우 7 비트는 음의 경우 1 1 1 1 1 1 [0 또는 1] , 양의 경우 0 0 0 0 0 0 0 [0 또는 1] 을 생성합니다.

이제 00000001 (= 1)으로 OR 연산을 수행 하면 음수는 11111111 (= -1) , 양수 -00000001 (= 1)이 됩니다.

그래서,

드디어,


또 다른 방법 -


0

static int mymax (int a, int b)

b> a이면 (ab)가 음수이면 부호는 -1을 반환하고, 1을 더하면 b 인 인덱스 0을 얻고, b = a이면 ab는 0이고, +1은 1 인덱스를 제공하므로 중요하지 않습니다. a 또는 b를 반환하는 경우 a> b이면 ab는 양수이고 부호는 1을 반환하고 1을 더하면 a가 저장된 인덱스 2가됩니다.


0

0

내가 제공하는 코드는 두 숫자 사이의 최대 값을 찾기위한 것이며 숫자는 모든 데이터 유형 (정수, 부동) 일 수 있습니다. 입력 숫자가 같으면 함수는 숫자를 반환합니다.

기술

  • 함수가 가장 먼저 인수를 double로 취하고 반환 유형을 double로 사용합니다. 그 이유는 모든 유형에 대해 최대 값을 찾을 수있는 단일 함수를 생성하기 때문입니다. 정수 유형 숫자가 제공되거나 하나가 정수이고 다른 하나가 부동 소수점 인 경우 암시 적 변환으로 인해 함수를 사용하여 정수에 대한 최대 값도 찾을 수 있습니다.
  • 기본 논리는 간단합니다. ab> 0 (즉, 차이가 양수)이면 a와 b가 최대이고 ab == 0이면 둘 다 같고 ab <0 (즉 diff가- ve) b는 최대입니다.
  • 부호 비트는 메모리에 MSB (Most Significant Bit)로 저장됩니다. MSB가 1이고 그 반대의 경우도 마찬가지입니다. MSB가 1인지 0인지 확인하기 위해 MSB를 LSB 위치로 이동하고 Bitwise & 1로 결과가 1이면 숫자는 -ve가 아니면 no입니다. + ve입니다. 이 결과는 다음 명령문으로 얻습니다.

    int_diff >> (sizeof (int) * 8-1) & 1

여기서 MSB에서 LSB로 부호 비트를 가져 오려면 오른쪽으로 k-1 비트로 이동합니다 (여기서 k는 시스템 유형에 따라 메모리에 정수를 저장하는 데 필요한 비트 수). 여기서 k = sizeof (int) * 8 as sizeof ()는 정수를 저장하는 데 필요한 바이트 수를 제공합니다. 비트 수에 8을 곱합니다. 오른쪽 시프트 후에 비트 & 1을 적용하여 결과를 얻습니다.

  • 이제 결과를 얻은 후 (r로 가정하자) 1 (for -ve diff) 및 0 (for + ve diff) 결과에 두 숫자의 차이를 곱하면 논리는 다음과 같습니다.

    1. a> b이면 ab> 0 즉, + ve이므로 결과는 0 (즉, r = 0)입니다. 따라서 a- (ab) * r => a- (ab) * 0, 'a'를 최대 값으로 제공합니다.
    2. a <b이면 ab <0 즉, -ve이므로 결과는 1 (즉, r = 1)입니다. 따라서 a- (ab) * r => a- (ab) * 1 => a-a + b => b, 이는 'b'를 최대 값으로 제공합니다.
  • 이제 나머지 두 점이 있습니다. 1. while 루프의 사용과 2. 변수 'int_diff'를 정수로 사용한 이유입니다. 이에 대해 제대로 답하려면 몇 가지 사항을 이해해야합니다.

    1. 부동 유형 값은 비트 연산자의 피연산자로 사용할 수 없습니다.
    2. 위의 이유로 인해 비트 연산자를 사용하여 차이의 부호를 얻으려면 정수 값으로 값을 가져와야합니다. 이 두 점은 정수 유형으로 변수 'int_diff'의 필요성을 설명합니다.
    3. 이제 변수 'diff'에서 차이를 찾았다 고 가정 해 보겠습니다. 이제 이러한 값의 부호에 관계없이 'diff'값에 대해 3 가지 가능성이 있습니다. (ㅏ). | diff |> = 1, (b). 0 <| diff | <1, (c). | diff | == 0.
    4. 정수 변수에 double 값을 할당하면 소수 부분이 손실됩니다.
    5. case (a)의 경우 'int_diff'> 0 (즉, 1,2, ...)의 값입니다. 다른 두 가지 경우 int_diff = 0.
    6. 조건 (temp_diff-int_diff) || 0.0은 diff == 0인지 확인하여 두 숫자가 동일하도록합니다.
    7. diff! = 0이면 int_diff | 0이 참인지 확인합니다. 즉, case (b)가 참인지 확인합니다.
    8. while 루프에서 int_diff의 값도 diff의 부호를 갖도록 int_diff의 값을 0이 아닌 값으로 가져 오려고합니다.

0

다음은 두 정수 값의 최대 값을 얻기 위한 몇 가지 비트 트위들 링 방법입니다.

방법 1

설명:

  • (a - b) >> SIGN_BIT_SHIFT은 - 경우 a > b다음 a - b, 양성 따라서 부호 비트이고 0, 상기 마스크이다 0x00.00. 그렇지 않으면, a < b그래서 a - b음, 부호 비트입니다 1및 이동 후, 우리의 마스크를 얻을0xFF..FF
  • (A & ~ 마스크) - 마스크 인 경우 0xFF..FF, 다음 ~mask이며 0x00..00다음이 값이 0. 그렇지 않으면, ~mask이다0xFF..FF 및 값입니다a
  • (b & 마스크)-마스크가 0xFF..FF이면이 값은 b입니다. 그렇지 않으면, mask0x00..00및 값입니다 0.

드디어:

  • 경우 a >= b다음 a - b긍정적, 우리는 도착max = a | 0 = a
  • 경우 a < b다음 a - b부정적이며, 우리가 얻을max = 0 | b = b

방법 2

설명:

  • 마스크 설명은 방법 1 과 동일 합니다. 경우 a > b마스크가 0x00..00, 그렇지 않으면 마스크입니다0xFF..FF
  • 마스크 인 경우 0x00..00, 다음 (a ^ b) & mask입니다0x00..00
  • 마스크 인 경우 0xFF..FF, 다음 (a ^ b) & mask입니다a ^ b

드디어:

  • 이면 a >= b우리는a ^ 0x00..00 = a
  • 이면 a < b우리는a ^ a ^ b = b

0

// C #에서는 수학 라이브러리를 사용하여 최소 또는 최대 기능을 수행 할 수 있습니다.

시스템 사용;

class NumberComparator {

}


주의 봐 : OP의 질문은 [C] 없습니다 [C #을] 태그가
알렉스 유

0

논리 연산자, libs (JS) 없음


-2

문제에 설명 된 논리는 첫 번째 숫자가 0보다 작 으면 0을 뺀 것처럼 설명 할 수 있습니다. 그렇지 않으면 첫 번째 숫자에서 차이를 빼서 두 번째 숫자를 얻습니다. 이 개념을 이해하는 것이 조금 더 간단하다고 생각하는 수학적 솔루션을 하나 더 찾았습니다.

a와 b를 주어진 숫자로 간주

다시 말하지만, 아이디어는 0 또는 1 인 k를 찾아서 두 숫자의 차이를 곱하는 것입니다. 마지막으로이 숫자는 두 숫자 중 더 작은 숫자를 산출하기 위해 첫 번째 숫자에서 빼야합니다. PS이 솔루션은 두 번째 숫자가 0 인 경우 실패합니다.


-3

한 가지 방법이 있습니다

그리고 하나


-3

-4

비트 비교로 숫자를 곱할 수 있다고 생각하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.