설계 함수 f (f (n)) == -n


841

마지막 인터뷰에서 얻은 질문 :

다음 f과 같은 기능을 설계하십시오 .

f(f(n)) == -n

n32 비트 부호있는 정수 는 어디에 있습니까 ? 복소수 산술을 사용할 수 없습니다.

전체 범위의 숫자에 대해 이러한 기능을 설계 할 수없는 경우 가능한 가장 큰 범위에 맞게 설계하십시오.

어떤 아이디어?


2
이 인터뷰는 어떤 직업입니까?
tymtam

답변:


377

어때요?

f (n) = 부호 (n)-(-1) n * n

파이썬에서 :

def f(n): 
    if n == 0: return 0
    if n >= 0:
        if n % 2 == 1: 
            return n + 1
        else: 
            return -1 * (n - 1)
    else:
        if n % 2 == 1:
            return n - 1
        else:
            return -1 * (n + 1)

파이썬은 정수를 임의의 길이로 자동 승격시킵니다. 다른 언어에서는 가장 큰 양의 정수가 오버플로되므로 해당 정수를 제외한 모든 정수에서 작동합니다.


그것은 실수 작동하려면 당신은 교체해야 N 에서 (-1) N 과 함께 { ceiling(n) if n>0; floor(n) if n<0 }.

C #에서 (오버플로 상황을 제외하고 두 배로 작동) :

static double F(double n)
{
    if (n == 0) return 0;

    if (n < 0)
        return ((long)Math.Ceiling(n) % 2 == 0) ? (n + 1) : (-1 * (n - 1));
    else
        return ((long)Math.Floor(n) % 2 == 0) ? (n - 1) : (-1 * (n + 1));
}

10
-1 * 0은 여전히 0이기 때문에, -1에 대한 브로큰
조엘 Coehoorn

3
아닙니다. f (-1) = 0. f (0) = 1
1800 정보

5
그러나 1은 깨졌습니다. f (1) = 0. f (0) = 1
1800 정보

18
흠, 짝수와 홀수로 상태를 저장하는 것 같아요.
알 수 없음

38
가장 중요한 것은 실제 함수 (무한한 많은 솔루션이 있음)가 아니라 그러한 함수를 구성 할 수있는 프로세스라고 생각합니다.
pyon

440

당신은 그들이 어떤 종류의 언어를 기대했는지 말하지 않았다 ... 정적 솔루션 (Haskell)이있다. 기본적으로 가장 중요한 2 비트를 망칩니다.

f :: Int -> Int
f x | (testBit x 30 /= testBit x 31) = negate $ complementBit x 30
    | otherwise = complementBit x 30

동적 언어 (Python)에서는 훨씬 쉽습니다. 인수가 숫자 X인지 확인하고 -X를 반환하는 람다를 반환하십시오.

def f(x):
   if isinstance(x,int):
      return (lambda: -x)
   else:
      return x()

23
쿨, 나는 이것을 좋아한다 ... JavaScript에서와 같은 접근법 : var f = function (n) {return (typeof n == 'function')? n () : function () {return -n; }}
Mark Renouf

아마도 내 Haskell이 매우 녹슨 것일 수도 있지만 (f 0)을 확인 했습니까? 적어도 우리가 wraparound 산술을 가진 32 비트 정수를 처리하는 경우 (f 0x80000000)과 동일한 결과를 생성해야합니다 (부정 연산에서). 그리고 그것은 나쁠 것입니다.
다리우스 베이컨

11
평범한 면접관은 람다 구조 무엇인지 아는가 ?
Jeremy Powell

4
물론 이러한 유형 부정 행위는 정적이지만 Haskell에서도 작동합니다 class C a b | a->b where { f :: a->b }. instance C Int (()->Int) where { f=const.negate }; instance C (()->Int) Int where { f=($()) }.
leftaroundabout

4
뭐? typeof f (n) === 'function'이라는 아이디어를 어디서 얻었습니까? 특히 n은 숫자이며 숫자가 반환 될 것으로 예상합니까? 인스턴스 사례가 어떻게 적용되는지 이해할 수 없습니다. 나는 파이썬을 잘 말하지 않지만 JS에서는 함수 유형에 대한 인수를 확인하는 것이 명백합니다. 여기에는 숫자 솔루션 만 적용됩니다. f는 함수이고 f (n)은 숫자입니다.
Harry

284

추가 정보를 사용하지 않는 경우 (32 비트 int 제외) 모든 숫자에 대해 그러한 함수가 존재하지 않는 이유에 대한 증거는 다음과 같습니다.

f (0) = 0이어야합니다. (증거 : f (0) = x라고 가정 한 다음 f (x) = f (f (0)) = -0 = 0이라고 가정하십시오. )) = f (0) = x, 이는 x = 0을 의미합니다.)

또한 임의의 xy에 대해 가정 f(x) = y합니다. 우리는 f(y) = -x그때 원합니다 . 그리고 f(f(y)) = -y => f(-x) = -y. 요약하면 : 만약 f(x) = y, 다음 f(-x) = -y,와 f(y) = -x,와 f(-y) = x.

따라서 0을 제외한 모든 정수를 4 개의 세트로 나눌 필요가 있지만 홀수의 정수가 있습니다. 뿐만 아니라 양의 상대가없는 정수를 제거해도 여전히 2 (mod4) 개의 숫자가 있습니다.

남아있는 최대 값 2 개를 제거하면 (abs 값으로) 함수를 얻을 수 있습니다.

int sign(int n)
{
    if(n>0)
        return 1;
    else 
        return -1;
}

int f(int n)
{
    if(n==0) return 0;
    switch(abs(n)%2)
    {
        case 1:
             return sign(n)*(abs(n)+1);
        case 0:
             return -sign(n)*(abs(n)-1);
    }
}   

물론 다른 옵션은 0을 준수하지 않고 보너스로 제거한 2 개의 숫자를 얻는 것입니다. (그러나 그것은 바보 같은 경우입니다.)


29
전역 변수 또는 코드를 난독 화하는 트릭에 의존하지 않고 음수를 처리하는 좋은 절차 솔루션을 찾으려면이 글을 아래로 읽어야한다고 믿을 수 없습니다. 내가 두 번 이상 투표 할 수 있다면
Kyle Simek

n 개의 부호있는 비트 에는 0이 아닌 정수가 홀수 인 것이 좋습니다.
Andres Jaan Tack

이것도 저의 대답 일 것입니다. 그러나 가장 중요한 경우 n = -2147483648(최소 값)를 주의하십시오 . 이 abs(n)경우에는 불가능 하며 결과는 정의되지 않습니다 (또는 예외).
커크 브로드 허스트

1
@ a1kmm : 죄송합니다. 위의 -2³²는 -2³¹ 여야합니다. 어쨌든 f (0) ≠ 0 (그래서 f (0) =-2³¹) 인 경우는 실제로 더 쉬운 경우입니다. 우리가 고려해야 할 다른 경우는 f (0) = 0이지만 일부 x ≠ 0, x ≠ -2³¹의 경우 f (x) =-2³¹입니다. 이 경우 f (-2³¹) = f (f (x)) =-x (주 : x는 존재하지 않으므로 -x는 -2³¹ 일 수 없음). 또한 f (-x) = y라고하자. 그런 다음 f (y) = f (f (-x)) = x입니다. 다시 y는 -2³¹ 일 수 없습니다 (f (y) = x, f (-2³¹) =-x, x는 0이 아님). 따라서 -2³¹ = f (x) = f (f (y)) =-y는 불가능합니다. 따라서 실제로 0과 -2³¹는 나머지 이미지와 분리해야합니다 (다른 이미지는 제외).
ShreevatsaR

1
@will (제대로 가정 할 경우) 2의 보수 32 비트 정수에 대해 이야기하는 경우 부호가없는 0은 없습니다.
goffrie 2016 년

146

C ++의 과부하로 인해 :

double f(int var)
{
 return double(var);
} 

int f(double var)
{
 return -int(var);
}

int main(){
int n(42);
std::cout<<f(f(n));
}

4
불행히도 이름 맹 글링 (mangling) 때문에 "f"라고 부르는 함수는 실제로 이상한 이름을 가지고 있습니다.
pyon

1
나는 그런 것을 생각했지만 C로 생각할 때 이것은 버려졌습니다 ... 잘하셨습니다!
Liran Orevi

@Rui Craverio : 작성자가 var 키워드를 변수 이름으로 사용하기로 선택했기 때문에 .NET 3.5 이상에서는 작동하지 않습니다.
Kredns

72
기술적으로 ... 이것은 질문이 요구하는 것이 아닙니다. 당신이 F를 정의 () 함수, F (INT)와 F (플로트)과 질문은 "... () 함수 f를 디자인"요청
elcuco

2
@elcuco 기술적으로는 물론 논리적으로 여러 개의 과부하가있는 기능입니다 (f (f (42)) 할 수 있습니다). 정의는 매개 변수와 반환 값에 대해 아무 말도하지 않으므로 기술 정의로 받아 들일 수 없습니다.
Marek Toman 2016 년

135

또는 전처리기를 남용 할 수 있습니다.

#define f(n) (f##n)
#define ff(n) -n

int main()
{
  int n = -42;
  cout << "f(f(" << n << ")) = " << f(f(n)) << endl;
}

그럼 Konrad "Le Chiffre"Rudolph가 되셨나요? 코트를 가져 오겠습니다. 네, "void main"전체에 대해서는 알고 있지만 "return 0;" ;-)
Skizz

25
@Skizz, int 반환 값을 사용하더라도 C ++에서는 main에서 0을 반환 할 필요가 없습니다. 그래서 올바르게 수행하면 실제로 하나의 적은 문자를 입력합니다!
Dan Olson

10
Skizz는 항상 전처리기를 남용합니다. D
Arnis Lapsa

23
이것은 함수가 아닙니다. 따라서 유효한 해결책이 아닙니다
smerlin

3
@ smerlin : 기술적으로 인라인 함수를 반환하는 인라인 함수입니다. 둘 다 본문은 컴파일 타임에 또는 오히려 직전에 확장됩니다. 이보다 훨씬 더 효율적으로 얻을 수는 없습니다.
Jon Purdy

103

이것은 모든 음수에 해당됩니다.

    f (n) = abs (n)

2의 보수 정수에 대한 양수보다 하나 이상의 음수가 있기 때문에 와 동일한 해보다 f(n) = abs(n)하나 이상의 경우에 유효합니다 . 하나 하나 당신이 ... : Df(n) = n > 0 ? -n : nf(n) = -abs(n)

최신 정보

아니요, 방금 litb의 의견으로 인식 한대로 더 이상 유효하지 않습니다 abs(Int.Min)...

나는 mod 2 정보 사용에 대해서도 생각했지만 초기에는 작동하지 않는다고 결론지었습니다. 올바르게 수행 Int.Min하면 오버플로되기 때문에 모든 숫자에 대해 작동합니다 .

최신 정보

나는 잠시 동안 그것을 가지고 놀면서 멋진 비트 조작 트릭을 찾았지만 멋진 2 라이너를 찾을 수 없지만 mod 2 솔루션은 1에 맞습니다.

    f (n) = 2n (abs (n) % 2)-n + sgn (n)

C #에서는 다음과 같습니다.

public static Int32 f(Int32 n)
{
    return 2 * n * (Math.Abs(n) % 2) - n + Math.Sign(n);
}

이 모든 값에 대한 작업을하려면, 당신은 교체해야 Math.Abs()(n > 0) ? +n : -n와의 계산에 포함 unchecked블록. 그런 다음 Int.Min확인되지 않은 부정과 마찬가지로 자체 매핑됩니다.

최신 정보

다른 답변에서 영감을 얻어 함수가 작동하는 방식과 그러한 함수를 구성하는 방법에 대해 설명하겠습니다.

처음부터 시작할 수 있습니다. 함수 f는 주어진 값에 반복적으로 적용되어 n일련의 값을 산출합니다.

    n => f (n) => f (f (n)) => f (f (f (n))) => f (f (f (f (n)))) => ...

의문 f(f(n)) = -nf무효화하는 두 가지 연속적인 적용이다 . f총 4 건의 2 건의 추가 적용은 논증을 다시 무효화 n합니다.

    n => f (n) => -n => f (f (f (n))) => n => f (n) => ...

이제 길이 4의 명백한주기가 있습니다. x = f(n)획득 된 방정식이 f(f(f(n))) = f(f(x)) = -x보유 하고 있음을 대체 하고 주목 하면 다음과 같은 결과가 나온다.

    n => x => -n => -x => n => ...

따라서 우리는 두 개의 숫자를 가진 길이가 4이고 두 개의 숫자는 부정됩니다. 주기를 직사각형으로 가정하면 무시 된 값이 반대편 모서리에 있습니다.

이러한주기를 구성하는 많은 솔루션 중 하나는 n부터 시작하는 다음과 같습니다.

 n => 부정하고 빼기
-n-1 =-(n + 1) => 하나 추가
-n => 부정하고 하나 추가
 n + 1 => 하나 빼기
 엔

구체적인 예는 다음과 같습니다 +1 => -2 => -1 => +2 => +1. 우리는 거의 끝났습니다. 생성 된 사이클에 홀수 양수가 포함되어 있고 짝수의 후속 숫자도 무시하고 정수를 여러 사이클 ( 2^324의 배수) 로 쉽게 분할 할 수 있으며 조건을 만족시키는 함수를 찾았습니다.

그러나 우리는 0에 문제가 있습니다. 0 => x => 00은 자체적으로 부정되므로 주기가 포함되어야합니다 . 그리고주기 상태는 이미 0 => x다음과 같습니다 0 => x => 0 => x. 이것은 단지 길이가 2의주기 일 뿐이며, x두 번의 애플리케이션이 아닌 자체로 바뀝니다 -x. 운좋게도 문제를 해결하는 한 가지 사례가 있습니다. 경우 X등호 제로 우리는 제로를 포함하는 길이 하나의 사이클을 얻고 제로의 고정 점이라고 우리는 그 문제의 결론을 해결 f.

끝난? 거의. 우리는 2^32숫자가 있고, 0은 2^32 - 1숫자를 남기는 고정 소수점 이며, 그 숫자를 4 개의 숫자로 나누어야합니다. 2^32 - 14의 배수가 아닌 나쁜 것 -길이 4의 어떤주기에도 3 개의 숫자가 남아 있습니다.

나는 이르기까지 3 비트 부호 itegers의 작은 세트를 사용하여 솔루션의 나머지 부분 설명 할 것 -4을을 +3. 우리는 0으로 끝납니다. 하나의 완전한주기가 +1 => -2 => -1 => +2 => +1있습니다. 이제부터 시작하는 사이클을 구성하겠습니다 +3.

    +3 => -4 => -3 => +4 => +3

발생하는 문제는 +43 비트 정수로 표현할 수 없다는 것입니다. 우리는 얻을 것이다 +4부정에 의해 -3+3여전히 유효한 3 비트 정수 무엇인지 - -하지만 하나를 추가 +3(이진은 011) 산출 100진. 부호없는 정수로 해석되지만 부호있는 정수 +4로 해석해야합니다 -4. 그래서, 실제로 -4이 실시 예 또는 Int.MinValue일반적인 경우는 정수 산술 부정의 제 2 고정 점 인 - 0Int.MinValuethemselve 매핑된다. 따라서주기는 실제로 다음과 같습니다.

    +3 => -4 => -3 => -4 => -3

길이가 2의주기이고 +3를 통해 추가 로주기에 들어갑니다 -4. 결과적 -4으로 두 기능 응용 프로그램 후에 +3올바르게 맵핑되고 -3두 기능 응용 프로그램 후에 올바르게 맵핑 되지만 -3두 기능 응용 프로그램 후에는 자체적으로 잘못 맵핑됩니다.

그래서 우리는 하나를 제외한 모든 정수에서 작동하는 함수를 만들었습니다. 더 잘할 수 있을까요? 아니야 우리는 할 수 없어. 왜? 우리는 길이가 4 인 사이클을 구성해야하며 전체 정수 범위를 최대 4 개의 값까지 포함 할 수 있습니다. 나머지 값은 두 개의 고정 된 점 0이며 Int.MinValue두 개의 임의의 정수 x와 두 개의 임의의 정수 -x로 맵핑되어야하며 두 개의 함수 애플리케이션으로 서로 맵핑되어야합니다.

지도하기 x-x하고 그 그들이 네 사이클을 형성해야하며, 그들이 그주기의 반대 모서리에 위치해야합니다 마찬가지입니다. 결과에서 0Int.MinValue도 반대 모서리에 있어야합니다. 이것은 올바르게 매핑 x되고 -x두 개의 고정 점 0Int.MinValue두 개의 함수 응용 프로그램을 바꾼 다음 두 개의 실패한 입력으로 남겨 둡니다. 따라서 모든 값에 대해 작동하는 함수를 구성 할 수는 없지만 하나를 제외한 모든 값에 대해 작동하는 함수가 있으며 이것이 달성 할 수있는 최선입니다.


조건을 충족하지 않습니다 : abs (abs (n))! = -n
Dan Olson

그가 말한 것처럼 모든 음수에 대해서도 마찬가지입니다. 그것은 질문의 일부였습니다. 일반적인 것을 만들 수 없다면 가능한 한 가장 넓은 범위에서 작동하는 것을 생각해보십시오.
jalf

이 답변은 적어도 Marj Synowiec와 Rowland Shaw의 답변만큼 우수합니다. 다른 숫자 범위에서도 작동합니다.
1800 INFORMATION

19
야, 당신은 "업데이트"를 제거하고 하나의 응집력있는 정답을 쓸 수 있습니다. 하단 3/4 ( "다른 답변에서 영감을 받음")은 훌륭합니다.
Andres Jaan Tack

1
나는 음수에 대한 절대 해를 좋아합니다. 간단하고 쉽게 이해할 수 있습니다.
Thorbjørn Ravn Andersen

97

복소수를 사용하면 숫자 무효화 작업을 두 단계로 효과적으로 나눌 수 있습니다.

  • n에 i를 곱하면 n * i를 얻게되는데, n은 시계 반대 방향으로 90도 회전합니다.
  • 다시 i를 곱하면 -n이됩니다

가장 좋은 점은 특별한 처리 코드가 필요 없다는 것입니다. i를 곱하면 작업이 수행됩니다.

그러나 복잡한 숫자는 사용할 수 없습니다. 따라서 데이터 범위의 일부를 사용하여 자신 만의 가상 축을 만들어야합니다. 초기 값만큼의 가상 (중간) 값이 정확히 필요하므로 데이터 범위의 절반 만 남습니다.

서명 된 8 비트 데이터를 가정하여 다음 그림에서 이것을 시각화하려고했습니다. 이것을 32 비트 정수로 확장해야합니다. 초기 n에 허용되는 범위는 -64 ~ +63입니다. 양의 n에 대해 함수가 수행하는 작업은 다음과 같습니다.

  • n이 0..63 (초기 범위) 인 경우 함수 호출은 64를 추가하여 n을 64..127 (중간 범위) 범위에 매핑합니다.
  • n이 64..127 (중간 범위)에 있으면 함수는 64에서 n을 빼서 n을 0 ..- 63 범위에 매핑합니다.

음수 n의 경우이 함수는 중간 범위 -65 ..- 128을 사용합니다.

대체 텍스트


4
@geschema, 멋진 그래픽을 만들기 위해 어떤 도구를 사용하셨습니까?
jwfearn 2009

10
죄송하지만 질문에 명시 적으로 복잡한 숫자는 없습니다.
Rui Craveiro

6
@Liran : OmniGraffle (Mac 전용)
geschema를 사용

39
+1 이것이 가장 좋은 답변이라고 생각합니다. 나는 사람들이 충분히 읽을 수 없다고 생각합니다. 왜냐하면 그들은 모두 복잡한 숫자를 사용할 수 없다는 질문에 주목했기 때문입니다. 전체 내용을 읽었으며 복잡한 수의 솔루션을 설명하여 비 복합 솔루션의 단계를 질문에 설정했습니다. 아주 잘 했어요
jrista

1
@ jrista의 모든 솔루션은 2 차원을 사용합니다.이 숫자는 '복소수'가 실제로 가장 높습니다 (대부분은 홀수 대 짝수, 위는 floatvs 사용 int). 많은 답변에서 설명하는 '4 요소 링'은 4 개의 상태를 필요로하며, 2 개의 상태를 갖는 2 차원으로 표현 될 수 있습니다. 이 답변 의 문제점 은 추가 처리 공간 (-64..63의 경우 '작동'이지만 -128..127의 공간이 필요함)이 필요하고 서면 수식을 명시 적으로 명시하지 않는다는 것입니다!
커크 브로드 허스트

65

int.MaxValue 및 int.MinValue를 제외한 작동

    public static int f(int x)
    {

        if (x == 0) return 0;

        if ((x % 2) != 0)
            return x * -1 + (-1 *x) / (Math.Abs(x));
        else
            return x - x / (Math.Abs(x));
    }

화보


이것이 왜 다운 보트인지 확실하지 않습니다. 어떤 입력에 실패합니까?
Rodrick Chapman 2016

왜 signum 기능을 사용하지 않습니까?!?
comonad

1
이미지가 정말 좋습니다. 보내기 00-2147483648-2147483648, 부정 연산자에 대한이 두 숫자가 고정되어 있기 때문에 포인트 x => -x. 나머지 숫자는 위 이미지의 화살표를 따르십시오. SurDin의 대답과 설명에서 알 수있는 바와 같이,이 경우, 두 수있을 것 2147483647-2147483647와 스왑에 남아 다른 쌍.
Jeppe Stig Nielsen

그것은 주름이 많은 스마일처럼 보인다
Anshul

48

문제는 입력 유형 및 함수의 반환 값이 무엇인지에 대해 아무것도 말하지 않는다 f있어야합니다 (적어도하지 당신이 제시 한 방법을) ...

... n이 32 비트 정수 인 경우 f(f(n)) = -n

그래서, 어떻습니까?

Int64 f(Int64 n)
{
    return(n > Int32.MaxValue ? 
        -(n - 4L * Int32.MaxValue):
        n + 4L * Int32.MaxValue);
}

n이 32 비트 정수이면 명령문 f(f(n)) == -n이 참입니다.

분명히이 접근법은 더 넓은 범위의 숫자에서 작동하도록 확장 될 수 있습니다 ...


2
교활한. 글자 수 한도
Joe Phillips

2
예, 비슷한 접근법을 연구하고있었습니다. 그래도 날 이겼어 +1 :)
jalf

1
매우 영리한! 이것은 복소수를 사용하는 것과 매우 유사하며 (그리고 효과적으로 동일), 이는 명백하고 이상적인 솔루션이지만 명시 적으로 허용되지 않습니다. 허용되는 수의 범위를 벗어난 작업.
Kirk Broadhurst

48

자바 스크립트 (또는 다른 동적 유형 언어)의 경우 함수가 int 또는 객체를 수락하고 다른 객체를 반환하도록 할 수 있습니다. 즉

function f(n) {
    if (n.passed) {
        return -n.val;
    } else {
        return {val:n, passed:1};
    }
}

기부

js> f(f(10))  
-10
js> f(f(-10))
10

또는 규칙을 위반할 수 있지만 강력한 형식의 언어로 오버로드를 사용할 수 있습니다.

int f(long n) {
    return n;
}

long f(int n) {
    return -n;
}

후자는 "a"(단수) 함수의 요구 사항을 의미하지 않습니다. :)
Drew

답의 후반부를 제거하면 정답입니다.
jmucchiello

@Drew 이것이 내가 규칙을 어길 수 있다고 언급 한 이유입니다
cobbal

2
JavaScript에서 함수는 객체이므로 상태를 유지할 수 있습니다.
Nosredna 2016 년

1
IMO : 함수 f (n) {return n.passed? -n.val : {val : n, passed : 1}}이 더 읽기 쉽고 짧습니다.
SamGoody

46

플랫폼에 따라 일부 언어를 통해 기능 상태를 유지할 수 있습니다. VB.Net의 예 :

Function f(ByVal n As Integer) As Integer
    Static flag As Integer = -1
    flag *= -1

    Return n * flag
End Function

IIRC, C ++도 이것을 허용했습니다. 나는 그들이 다른 해결책을 찾고 있다고 생각합니다.

또 다른 아이디어는 함수에 대한 첫 번째 호출의 결과를 정의하지 않았기 때문에 홀수 / 짝수를 사용하여 부호 반전 여부를 제어 할 수 있다는 것입니다.

int f(int n)
{
   int sign = n>=0?1:-1;
   if (abs(n)%2 == 0)
      return ((abs(n)+1)*sign * -1;
   else
      return (abs(n)-1)*sign;
}

모든 짝수의 크기에 1을 더하고 모든 홀수의 크기에서 1을 뺍니다. 두 번의 호출 결과는 같은 크기이지만 한 번의 호출로도 부호를 바꿉니다. 이것이 작동하지 않는 경우가 있지만 (-1, max 또는 min int), 지금까지 제안 된 것보다 훨씬 잘 작동합니다.


1
나는 그것이 항상 이상하기 때문에 MAX_INT에서 작동한다고 생각합니다. MIN_INT 및 -1에서는 작동하지 않습니다.
Airsource Ltd

9
부작용이 있으면 기능이 아닙니다.
nos

12
수학에는 맞을지 모르지만 프로그래밍에는 관련이 없습니다. 문제는 그들이 수학 솔루션을 찾고 있는지 아니면 프로그래밍 솔루션을 찾고 있는지입니다. 하지만 ... 프로그래밍 작업의 주어진
라이언 런디에게

+1 출력을 부정하고 FIFO를 구현하는 "정적 int x"를 사용하여 C에 하나를 게시하려고했습니다. 그러나 이것은 충분히 가깝습니다.
phkahler

2
@ nos : 예, 그것은 단지 참조 투명하지 않습니다.
Clark Gaebel

26

JavaScript 예외를 악용합니다.

function f(n) {
    try {
        return n();
    }
    catch(e) { 
        return function() { return -n; };
    }
}

f(f(0)) => 0

f(f(1)) => -1


나는 이전에 이런 식으로 예외가 사용 된 것을 의심한다 ... :)
NoBugs

+1 즉시 생각합니다. 멋있는! 그러나 프로덕션 코드에서는 안전을 위해 typeof를 사용합니다.

21

모든 32 비트 값 (-0은 -2147483648이라는 경고)

int rotate(int x)
{
    static const int split = INT_MAX / 2 + 1;
    static const int negativeSplit = INT_MIN / 2 + 1;

    if (x == INT_MAX)
        return INT_MIN;
    if (x == INT_MIN)
        return x + 1;

    if (x >= split)
        return x + 1 - INT_MIN;
    if (x >= 0)
        return INT_MAX - x;
    if (x >= negativeSplit)
        return INT_MIN - x + 1;
    return split -(negativeSplit - x);
}

기본적으로 각 -x => x => -x 루프를 ay => -y => y 루프와 쌍으로 연결해야합니다. 그래서 나는 반대편을 페어링했습니다 split.

예를 들어 4 비트 정수의 경우 :

0 => 7 => -8 => -7 => 0
1 => 6 => -1 => -6 => 1
2 => 5 => -2 => -5 => 2
3 => 4 => -3 => -4 => 3

21

C ++ 버전은 규칙을 다소 구부릴 수 있지만 모든 숫자 유형 (floats, ints, doubles) 및 단항 빼기에 과부하가 걸리는 클래스 유형에서도 작동합니다.

template <class T>
struct f_result
{
  T value;
};

template <class T>
f_result <T> f (T n)
{
  f_result <T> result = {n};
  return result;
}

template <class T>
T f (f_result <T> n)
{
  return -n.value;
}

void main (void)
{
  int n = 45;
  cout << "f(f(" << n << ")) = " << f(f(n)) << endl;
  float p = 3.14f;
  cout << "f(f(" << p << ")) = " << f(f(p)) << endl;
}

좋은 생각. 대안으로, 당신은 아마도 구조체를 잃어 버릴 수 있고 대신 한 함수가 포인터를 리턴하고 다른 함수는 역 참조하고 무효화 할 수 있습니다.
Imbue

20

x86 asm (AT & T 스타일) :

; input %edi
; output %eax
; clobbered regs: %ecx, %edx
f:
    testl   %edi, %edi
    je  .zero

    movl    %edi, %eax
    movl    $1, %ecx
    movl    %edi, %edx
    andl    $1, %eax
    addl    %eax, %eax
    subl    %eax, %ecx
    xorl    %eax, %eax
    testl   %edi, %edi
    setg    %al
    shrl    $31, %edx
    subl    %edx, %eax
    imull   %ecx, %eax
    subl    %eax, %edi
    movl    %edi, %eax
    imull   %ecx, %eax
.zero:
    xorl    %eax, %eax
    ret

코드 검사, 가능한 모든 32 비트 정수 전달, -2147483647 (언더 플로우) 오류.


19

글로벌을 사용하지만 ... 그렇습니까?

bool done = false
f(int n)
{
  int out = n;
  if(!done)
  {  
      out = n * -1;
      done = true;
   }
   return out;
}

3
이것이 질문을하는 사람의 의도인지는 확신 할 수 없지만 "상자 밖으로 생각"에 대해서는 +1입니다.
Liran Orevi

5
조건부로 "done = true"라고 말하지 말고 항상 "done =! done"이라고 말해야합니다. 이렇게하면 함수를 두 번 이상 사용할 수 있습니다.
Chris Lutz

@Chris, true로 설정하면 if (! done) 블록 안에 있으므로 done =! done과 동일하지만! done은 계산할 필요가 없습니다 (또는 충분히 똑똑하다면 컴파일러가 최적화 할 필요가 없습니다) .
nsayer

1
내 첫 번째 생각은 글로벌 변수를 사용 하여이 문제를 해결하는 것이 었습니다.이 특정 질문에 대해 속이는 것처럼 느껴졌습니다. 그러나 전역 변수 솔루션이 문제의 사양을 감안할 때 최상의 솔루션이라고 주장합니다. 전역을 사용하면 무슨 일이 일어나고 있는지 쉽게 이해할 수 있습니다. 나는 완료! = 완료가 더 나을 것이라는 데 동의합니다. if 절 외부로 옮기십시오.
Alderath

3
기술적으로 상태를 유지하는 것은 기능이 아니라 상태 시스템입니다. 으로 정의 하는 함수는 항상 동일한 입력에 대한 동일한 출력을 제공한다.
Ted Hopp

19

이 Perl 솔루션 은 정수, 부동 소수점 및 문자열에 작동합니다 .

sub f {
    my $n = shift;
    return ref($n) ? -$$n : \$n;
}

테스트 데이터를 사용해보십시오.

print $_, ' ', f(f($_)), "\n" for -2, 0, 1, 1.1, -3.3, 'foo' '-bar';

산출:

-2 2
0 0
1 -1
1.1 -1.1
-3.3 3.3
foo -foo
-bar +bar

그러나 그것은 int를 유지하지 않습니다. 기본적으로 전역 변수 데이터를 int "n"자체에 저장하고 있습니다. 그렇지 않으면 int "n"자체를 제외하고는 그렇지 않습니다. 예를 들어 n문자열 인 경우 548을 "First_Time_548"로 만들고 다음에 함수를 실행할 때 ... (prefix == First_Time_ ")"First_Time_ "을"- "로 바꾸십시오.
Albert Renshaw

@AlbertRenshaw 어디서 그런 아이디어를 얻었는지 잘 모르겠습니다. (1) 여기에는 관련된 전역 변수가 없습니다. (2) 함수에 int를 제공하면 함수를 홀수 번 호출하면 int가 반환되거나 int에 대한 참조가 표시됩니다. (3) 아마도 가장 근본적으로 이것은 Perl 입니다. 모든 실제적인 목적을 위해 int와 string은 완전히 호환됩니다. 숫자처럼 보이는 문자열은 대부분의 상황에서 숫자처럼 완벽하게 작동하며, 요청 될 때마다 숫자는 행복하게 문자열 화됩니다.
FMc

죄송합니다. 글로벌 배열을 사용하고있는 것 같습니다. haha
Albert Renshaw

18

아무도 f (x)가 같은 유형이어야한다고 말한 적이 없습니다.

def f(x):
    if type(x) == list:
        return -x[0]
    return [x]


f(2) => [2]
f(f(2)) => -2

16

나는 실제로 문제 자체에 대한 해결책을 제시하려고하지는 않지만이 문제가 제기 된 질문은 (직업?) 면접의 일부라고 언급하면서 몇 가지 의견이 있습니다.

  • 먼저 "왜 그런 기능이 필요한가? 이것이 더 큰 문제는 무엇입니까?" 현장에서 실제 제기 된 문제를 해결하려고하는 대신. 이것은 내가 어떻게 생각하고 어떻게 이런 문제를 해결하는지 보여줍니다. 누가 알아? 처음에 인터뷰에서 질문을받는 실제 이유 일 수도 있습니다. 대답이 "걱정하지 않아도 필요하다고 가정하고이 기능을 어떻게 설계 할 것인지 보여주십시오." 그런 다음 계속합니다.
  • 그런 다음 내가 사용할 C # 테스트 사례 코드를 작성하고 (명확한 : loop int.MinValueto to int.MaxValue, n해당 범위의 각 호출 f(f(n))및 결과 확인은 -n) 테스트 구동 개발을 사용하여 해당 기능을 사용하도록 지시합니다.
  • 면접관이 제기 된 문제를 해결하도록 계속 요청하는 경우에만 실제로는 인터뷰 자체에서 의사 코드를 시도하고 낙서하여 어떤 종류의 답변을 얻습니다. 그러나 면접관이 회사가 어떤지에 대한 표시가 될 경우 직업을 갖기 위해 뛰어들 것이라고 생각하지 않습니다.

오,이 답변은 인터뷰가 C # 프로그래밍 관련 위치에 대한 것이라고 가정합니다. 인터뷰가 수학 관련 직책에 대한 것이라면 어리석은 대답 일 것입니다. ;-)


7
64 비트 인 경우 테스트를 실행 한 후에는 인터뷰가 계속되지 않습니다 ;-)
alex2k8

실제로, 실제로 시험을 작성하고 면담 중에 시험을 실시해야 할 시점에 도달하게 될 수도 있습니다. ;-) 내 요점 : 나는 인터뷰에서 그 시점에 전혀 도달하지 않으려 고 노력했다. 프로그래밍은 "어떻게 코드를 작성 하는가"보다 "생각하는 방법"입니다.
peSHIr 2009

7
실제 인터뷰에서이 조언을 따르지 마십시오. 면접관은 당신이 실제로 그 질문에 대답하기를 기대합니다. 질문의 관련성에 의문을 제기하면 아무것도 사지 않지만 면접관을 귀찮게 할 수 있습니다. 사소한 테스트를 설계한다고해서 답에 한 걸음 더 다가 갈 수는 없으며 인터뷰에서 실행할 수 없습니다. 추가 정보 (32 비트)를 얻는다면 그것이 어떻게 유용한 지 알아보십시오.
Stefan Haustein

내가 더 많은 정보를 요구할 때 성가신 면접관은 과정에서 그의 질문과의 관련성을 질문하는 동안 필자가 반드시 함께 일하고 싶은 면접관이 아니다. 그래서 나는 인터뷰에서 그런 질문을 계속할 것입니다. 그들이 마음에 들지 않는다면, 나는 우리 두 시간을 더 이상 낭비하지 않기 위해 인터뷰를 끝낼 것입니다. "나는 단지 명령을 따랐다"라는 생각이 마음에 들지 않았다. 당신은 ..?
peSHIr

16

가장 중요한 비트 2 개를 변경하겠습니다.

00.... => 01.... => 10.....

01.... => 10.... => 11.....

10.... => 11.... => 00.....

11.... => 00.... => 01.....

보시다시피, 그것은 단지 추가이며 캐리 비트를 제외합니다.

어떻게 대답 했습니까? 나의 첫 생각은 단지 대칭의 필요성이었다. 4는 내가 시작한 곳으로 돌아갑니다. 처음에는 2 비트 그레이 코드라고 생각했습니다. 그런 다음 표준 바이너리로 충분하다고 생각했습니다.


이 접근법의 문제점은 2의 칭찬 음수 (모든 현대 CPU가 사용하는 것)와 작동하지 않는다는 것입니다. 그렇기 때문에 동일한 답변을 삭제했습니다.
Tamas Czinege

질문은 32 비트 부호있는 정수를 지정했습니다. 이 솔루션은 32 비트 부호있는 정수의 2의 보수 또는 1의 보수 표현에는 작동하지 않습니다. 부동 소수점 숫자 이외의 최신 컴퓨터에서는 흔하지 않은 부호 및 크기 표현에 대해서만 작동합니다.
Jeffrey L Whitledge

1
@ DrJokepu-와우, 6 개월 후-징크스!
Jeffrey L Whitledge

함수 내에서 숫자를 부호 및 크기 표현으로 변환하고 변환을 수행 한 다음 반환하기 전에 기본 정수 표현으로 다시 변환하지 않아도됩니까?
Bill Michell

나는 당신이 기본적으로 가상의 비트를 도입함으로써 복소수를 구현 한 것을 좋아합니다 :)
jabirali

16

이 문제를 해결하기 위해 복소수를 사용할 수 없다는 요구 사항이나 주장에서 영감을 얻은 솔루션이 있습니다.

-1에 제곱근을 곱하면 -1에 정수에 대한 제곱근이 없기 때문에 실패하는 것 같습니다. 하지만 수학과 같은 프로그램을 가지고 놀면

(1849436465 2 +1) mod (2 32 -3) = 0입니다.

그리고 이것은 -1의 제곱근을 갖는 것만큼이나 좋습니다. 함수의 결과는 부호있는 정수 여야합니다. 따라서 수정 된 모듈로 연산 mods (x, n)을 사용하여 0에 가장 가까운 x 모듈로 n과 일치하는 정수 y를 반환합니다. 프로그래밍 언어가 suc 인 모듈로 연산은 거의 없지만 쉽게 정의 할 수 있습니다. . 예를 들어 파이썬에서는 다음과 같습니다.

def mods(x, n):
    y = x % n
    if y > n/2: y-= n
    return y

위의 방정식을 사용하여 문제를 다음과 같이 해결할 수 있습니다.

def f(x):
    return mods(x*1849436465, 2**32-3)

이것은 f(f(x)) = -x범위의 모든 정수에 해당합니다 . 결과 도이 범위에 있지만 물론 계산에는 64 비트 정수가 필요합니다.[-231-2, 231-2]f(x)


13

2 ^ 32-1 범위의 숫자에 대한 C # (Int32.MinValue)를 제외한 모든 int32 숫자

    Func<int, int> f = n =>
        n < 0
           ? (n & (1 << 30)) == (1 << 30) ? (n ^ (1 << 30)) : - (n | (1 << 30))
           : (n & (1 << 30)) == (1 << 30) ? -(n ^ (1 << 30)) : (n | (1 << 30));

    Console.WriteLine(f(f(Int32.MinValue + 1))); // -2147483648 + 1
    for (int i = -3; i <= 3  ; i++)
        Console.WriteLine(f(f(i)));
    Console.WriteLine(f(f(Int32.MaxValue))); // 2147483647

인쇄물:

2147483647
3
2
1
0
-1
-2
-3
-2147483647

이것은 또한 f (0) 1073741824에서 작동하지 않습니다. f (1073741824) = 0. f (f (1073741824)) = 1073741824
Dinah

일반적으로 비트 크기의 2의 보수 정수 유형에 대해 함수 2 개 이상의 입력 값에 대해 작동하지 않아야 함을 증명할 수 있습니다 .
느슨하게

12

기본적으로이 함수는 사용 가능한 범위를 크기 4의 주기로 나누어야하며, n주기의 반대쪽 끝에 -n이 있어야합니다. 그러나 0은 크기가 1 인 사이클의 일부 여야합니다 0->x->0->x != -x. 그렇지 않으면 . 0은 단독이므로 4 개의 요소가있는 적절한주기가 아닌 범위 내에 3 개의 다른 값 (크기가 4의 배수)이 있어야합니다.

나는 것으로 이러한 추가 이상한 값을 선택 MIN_INT, MAX_INT하고 MIN_INT+1. 또한 올바르게 MIN_INT+1매핑 MAX_INT되지만 거기에 붙어 다시 매핑되지 않습니다. 나는 이것이 최선의 타협이라고 생각합니다. 왜냐하면 극단적 인 값의 좋은 속성이 올바르게 작동하지 않기 때문입니다. 또한 모든 BigInts에서 작동한다는 의미 입니다.

int f(int n):
    if n == 0 or n == MIN_INT or n == MAX_INT: return n
    return ((Math.abs(n) mod 2) * 2 - 1) * n + Math.sign(n)

12

아무도 무국적자 여야한다고 말한 사람은 없다.

int32 f(int32 x) {
    static bool idempotent = false;
    if (!idempotent) {
        idempotent = true;
        return -x;
    } else {
        return x;
    }
}

부정 행위이지만 많은 예 만큼은 아닙니다. 호출자의 주소가 & f인지 확인하기 위해 스택을 들여다 보는 것이 더 악한 일이지만 더 안전합니다 (스레드 안전하지는 않지만 스레드 안전 버전은 TLS를 사용합니다). 더 악한 :

int32 f (int32 x) {
    static int32 answer = -x;
    return answer;
}

물론, 이들 중 어느 것도 MIN_INT32의 경우에는 잘 작동하지 않지만 더 넓은 유형을 반환하지 않으면 그렇게 할 수있는 일은 거의 없습니다.


주소에 대해 묻기 위해 '업그레이드'할 수 있습니다 (예, 포인터로 ref \로 가져와야 함)-C에서 예를 들면 다음과 같습니다. int f (int & n) {static int * addr = & n; if (addr == & n) {return -n; } return n; }
IUnknownPointer

11

나는 31 비트를 상상의 ( i ) 비트로 사용하여 전체 범위의 절반을 지원하는 접근법을 상상할 수 있습니다.


이것은 더 복잡하지만 현재 가장 좋은 대답보다 더 효과적 일 것입니다
1800 정보

1
@ 1800 INFORMATION : 반면에, 도메인 [-2 ^ 30 + 1, 2 ^ 30-1]은 인접하여 수학적 관점에서 더 매력적입니다.
Jochen Walter

10

n =에서 작동합니다. [0 .. 2 ^ 31-1]

int f(int n) {
  if (n & (1 << 31)) // highest bit set?
    return -(n & ~(1 << 31)); // return negative of original n
  else
    return n | (1 << 31); // return n with highest bit set
}

10

이 문제는 "32 비트 부호있는 정수"를 나타내지 만 이들이 2의 보수 인지 1의 보수 인지를 지정하지는 않습니다 .

ones-complement를 사용하면 모든 2 ^ 32 값이 길이 4의 주기로 발생합니다. 0의 경우에는 특별한 경우가 필요하지 않으며 조건부도 필요하지 않습니다.

C에서 :

int32_t f(int32_t x)
{
  return (((x & 0xFFFFU) << 16) | ((x & 0xFFFF0000U) >> 16)) ^ 0xFFFFU;
}

이것에 의해 작동

  1. 높고 낮은 16 비트 블록 교환
  2. 블록 중 하나를 반전

두 번의 통과 후에 우리는 원래 값의 비트 역수를가집니다. 1의 보수 표현 중 어느 것이 부정과 동일합니다.

예 :

Pass |        x
-----+-------------------
   0 | 00000001      (+1)
   1 | 0001FFFF (+131071)
   2 | FFFFFFFE      (-1)
   3 | FFFE0000 (-131071)
   4 | 00000001      (+1)

Pass |        x
-----+-------------------
   0 | 00000000      (+0)
   1 | 0000FFFF  (+65535)
   2 | FFFFFFFF      (-0)
   3 | FFFF0000  (-65535)
   4 | 00000000      (+0)

1
다른 아키텍처에서 바이트 순서는 어떻습니까?
Steven

1
모든 산술은 32 비트입니다. 개별 바이트를 조작하지 않으므로 바이트 순서는 영향을 미치지 않습니다.
finnw

이것은 아주 가깝게 들립니다. 입력이 2 보수라고 가정 할 수 있습니다. 따라서 부호 비트 표현으로 변환합니다. 이제 마지막 비트에 따라 첫 번째 비트와 마지막 비트 또는 마지막 비트 만 뒤집습니다. 기본적으로 짝수 만 무시하고 항상 짝수 / 홀수로 순환합니다. 그래서 당신은 홀수에서 홀수로, 심지어 두 번의 호출 후에도 돌아옵니다. 마지막으로 2 보수로 다시 변환합니다. 이 코드를 아래 어딘가에 게시했습니다.
Stefan Haustein

9

:디

boolean inner = true;

int f(int input) {
   if(inner) {
      inner = false;
      return input;
   } else {
      inner = true;
      return -input;
   }
}

5
또한 글로벌 변수가 왜 당신을 인터뷰에서 쫓아 내지 않으면 나쁜지에 대한 토론을 얻을 수 있습니다!
palswim


7

수학자로서이 흥미로운 문제에 대한 견해를 나누고 싶습니다. 가장 효율적인 솔루션이 있다고 생각합니다.

올바르게 기억한다면 첫 번째 비트를 뒤집어 부호있는 32 비트 정수를 부정하십시오. 예를 들어, n = 1001 1101 1110 1011 1110 0000 1110 1010이면 -n = 0001 1101 1110 1011 1110 0000 1110 1010입니다.

그렇다면 부호있는 32 비트 정수를 취하고 f를 두 번 취하는 것이 첫 번째 비트를 뒤집는 것과 동일한 속성으로 다른 부호있는 32 비트 정수를 반환하는 함수 f를 어떻게 정의합니까?

정수와 같은 산술 개념을 언급하지 않고 질문을 다시 설명하겠습니다.

f를 두 번 취하는 것이 첫 번째 비트를 뒤집는 것과 같은 속성으로 0과 길이가 32 인 시퀀스를 취하고 0과 같은 길이의 시퀀스를 반환하는 함수 f를 어떻게 정의합니까?

관찰 : 32 비트의 경우 위의 질문에 대답 할 수 있다면 64 비트의 경우, 100 비트의 경우 등에도 대답 할 수 있습니다. 첫 32 비트에 f를 적용하면됩니다.

이제 2 비트 사례에 대한 질문에 대답 할 수 있다면 Voila!

그리고 그렇습니다. 처음 2 비트를 변경하면 충분합니다.

여기 의사 코드가 있습니다

1. take n, which is a signed 32-bit integer.
2. swap the first bit and the second bit.
3. flip the first bit.
4. return the result.

비고 : 2 단계와 3 단계를 함께 (a, b)-> (-b, a)로 합칠 수 있습니다. 익숙해 보이나요? 평면의 90도 회전과 -1의 제곱근에 의한 곱셈을 상기시켜야합니다.

긴 전주곡없이 의사 코드 만 제시하면 모자에서 토끼처럼 보일 것입니다. 어떻게 솔루션을 얻었는지 설명하고 싶었습니다.


6
네, 흥미로운 문제입니다. 당신은 당신의 수학을 알고 있습니다. 그러나 이것은 컴퓨터 과학 문제입니다. 따라서 컴퓨터를 공부해야합니다. 부호 크기 표현은 허용되지만 약 60 년 전에 스타일이 바뀌 었습니다. 2의 보수가 가장 인기가 있습니다.
Windows 프로그래머

5
(a, b)-> (-b, a)-> (-a, -b)와 같이 두 번 적용 할 때 함수가 두 비트에 대해 수행하는 작업은 다음과 같습니다. 그러나 우리는 (-a, -b)가 아닌 (-a, b)에 도달하려고합니다.
buti-oxa

@ buti-oxa, 맞습니다. 두 비트 연산은 00-> 01-> 10-> 11-> 00과 같이 진행되어야합니다. 그러나 Windows 알고리즘이 말했듯이 내 알고리즘은 현재 인기가없는 부호 크기 표현을 가정하므로 알고리즘은 거의 쓸모가 없다고 생각합니다. .
Yoo

그래서 한 번이 아니라 두 번만 단계를 수행 할 수 없습니까?
Nosredna 2016 년

4
buti-oxa는 완전히 옳습니다.이 함수는 두 번의 호출 후에 첫 번째 비트를 뒤집지 않고 처음 두 비트를 뒤집습니다. 모든 비트를 뒤집는 것은 2의 보수가하는 것과 비슷하지만 정확히 맞지는 않습니다.
redtuna
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.