-5

Is it possible to write a function int f(int x) in C that satisfies f(f(x)) == -x? Without globals and static variables, of course.

Source : Gurmeet Puzzles

  • 2
    And what have you tried so far yourself? How did or didn't it work? – Some programmer dude Oct 19 '15 at 07:50
  • 1
    @RSahu `x` is `int` as I see it in the Question. – Psytho Oct 19 '15 at 07:52
  • x is int.I tried playing around with mods and if-else statements but all fail at one of the cases : i.e x > 0 or x<0. Also x*i (i.e sqrt of -1) would have been nice but output is only int..So I am out of options here – Parikshit Khanna Oct 19 '15 at 07:52
  • 1
    @Bathsheba Interesting as the question may be, it is still blatant code begging with no effort shown. – Lundin Oct 19 '15 at 08:19
  • @Lundin I don't exactly understand how to demonstrate my efforts in a question like this ,where all approaches ended in a dead lock. Am I supposed to enumerate all the function definitions I came up with ? Also,I was not asking for a code – Parikshit Khanna Oct 19 '15 at 08:32
  • 1
    I definitely remember we had a popular, interesting question very similar (identical?) to this on Stack Overflow at one point, but it's probably been deleted now by the same ilk of self-important busybodies who would dismiss a fun puzzle as "blatant code begging". – Boann Oct 19 '15 at 08:32
  • @ParikshitKhanna Yes, that would be one way, show them and comment about why they don't work. – Lundin Oct 19 '15 at 08:34
  • @Boann SO isn't about posting "fun puzzles", there are posting policies and they have to be applied universally, with no bias just because a question is "funny". There is also a site http://codegolf.stackexchange.com/ which is dedicated to "fun puzzles", where questions like this will get a better reception, as they are on-topic there. – Lundin Oct 19 '15 at 08:38
  • Found it! It survives! https://stackoverflow.com/questions/731832/designing-function-ffn-n – Boann Oct 19 '15 at 08:39
  • Well that is almost a duplicate... except that one forbids complex numbers. – Lundin Oct 19 '15 at 08:40
  • @Boann That question was posted in '09 when the rules for posting on SO were very much different to what they are today. It's was also posted (or more likely moved) to community wiki – tddmonkey Oct 19 '15 at 17:11
  • @MrWiggles The rules aren't really the issue. If people simply ignore them they will have no effect, à la jury nullification. – Boann Oct 19 '15 at 17:16

3 Answers3

5

One solution is to define the function F as a quarter rotation of the argument in the Argand plane (in either direction in your particular case). To do this we need to model a complex number in a single int.

We are free to regard x as split into two halves. One half is the real part, the other half the imaginary part.

In the following analysis I am then free to adopt the notation x = y + iz (where i * i = -1), and y and z are both 1/2-sized ints.

Your function F models a multiplication by i, which is little more than an exchange of the two halves of the int.

F(F(x)) = F(F(y + iz)) = F(iy + iiz) = iiy + iiiz = -(y + iz) = -x.

The fact that F(x) gives you a seemingly unrelated value to x is of little consequence: the subsequent application of F will yield the sign reversal on the original number.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
  • 2
    I am finding it hard to translate the idea into code. – R Sahu Oct 19 '15 at 08:02
  • 1
    It is a bit fiddly but I trust the guidance above is sufficient. I will upvote any implementer. – Bathsheba Oct 19 '15 at 08:03
  • @Bathsheba: You could just use one bit to denote which part (real or imaginary) is the nonzero part, the integer thus encoding a value always on either the real or imaginary axis. Personally, I prefer a cyclic permutation approach; see my [answer](http://stackoverflow.com/a/33220022/1475978) below, but you could explain my solution using the one-bit real/imaginary flag, too. If you happen to have the time and inclination, I'd appreciate it if you could check if I missed anything important/interesting in my answer. – Nominal Animal Oct 19 '15 at 17:08
  • ^cyclic *mapping*, not permutation, of course. – Nominal Animal Oct 19 '15 at 17:30
3

Funnily enough, for the typical N-bit two's complement signed integers, the answer is no, there is no such function that works for all representable integers.

The issue boils down to zero being a special number, and there not being an even number of representable positive, and negative, values.

Let me explain. The requirement for the mapping is

f(f(x)) = -x

which, due to symmetries, means that

f(f(f(f(x)))) = x

In other words, we have integer cycles of length 4. Consider the cycles for small integers:

-4:  f(-4) = F-4,  f(F-4) = 4,  f(4) = F4,  f(F4) = -4
-3:  f(-3) = F-3,  f(F-3) = 3,  f(3) = F3,  f(F3) = -3
-2:  f(-2) = F-2,  f(F-2) = 2,  f(2) = F2,  f(F2) = -2
-1:  f(-1) = F-1,  f(F-1) = 1,  f(1) = F1,  f(F1) = -1
0:  f(0) = 0
1:  f(1) = F1,  f(F1) = -1,  f(-1) = F-1,  f(F-1) = 1
2:  f(2) = F2,  f(F2) = -2,  f(-2) = F-2,  f(F-2) = 2
3:  f(3) = F3,  f(F3) = -3,  f(-3) = F-3,  f(F-3) = 3
4:  f(4) = F4,  f(F4) = -4,  f(-4) = F-4,  f(F-4) = 4

Note the symmetries; each of the f(-4), f(-3), .., f(3), f(4) is listed twice in the above table.

Because the cycle length must be 4, Fii or -i, and this means full cycles require an even number of negative representable integers, and an even number of positive representable integers.

If signed integer overflow were well defined, for example INT_MIN - 1 == INT_MAX && INT_MAX + 1 == INT_MIN, then we only need an even number of total nonzero representable integers. Unfortunately, for typical N-bit two's complement signed integers, there are 2N-1 nonzero representable integers -- an odd number.

(If we could distinguish between +0 and -0, then we would require odd and even number of negative and positive representations (or even and odd, respectively).)


If we only consider some limited range of integers representable by the int type used, say -L to +L inclusive, where L > 1, INT_MIN < -L and L < INT_MAX, and we use exactly one zero, there is a trivial solution.

(Note that the function might work outside that interval, too, depending on the overflow rules and internal representation, but the above range is guaranteed to work, regardless of the implementation details or limits on the architecture. This is very portable code, in other words.)

For illustration, consider this table:

f(-4) = +3
f(-3) = -4
f(-2) = +1
f(-1) = -2
f(0) = 0
f(1) = +2
f(2) = -1
f(3) = +4
f(4) = -3

(The cycles resulting from above are -4 ⇒ +3 ⇒ +4 ⇒ -3 ⇒ -4, and -2 ⇒ +1 ⇒ +2 ⇒ -1 ⇒ -2, with zero always mapping to zero. In essence, the sign and whether the number is odd or not, determine the "quadrant" (place in the cycle) the value lies in. Every second step, the sign changes.)

We treat positive and negative values differently, and odd and even values differently, and zero always maps to zero. For positive integers x, we can use (x & 1) to check if x is odd, regardless of what the internal representation of x is.

So, here is my suggestion that implements the above table:

int f(const int x)
{
    if (x < 0) {
        if ((-x) & 1)
            return -1 + x;
        else
            return -1 - x;
    } else
    if (x > 0) {
        if (x & 1)
            return 1 + x;
        else
            return 1 - x;
    } else
        return 0;
}

Note that because x is only negated, and incremented or decremented by one, the specified range -L to +L inclusive is guaranteed to work (given L > 1, INT_MIN < -L, and L < INT_MAX; i.e. -L-1 and L+1 being representable).

Questions?

Nominal Animal
  • 38,216
  • 5
  • 59
  • 86
  • I'll certainly read through but note that negation is not defined for all 2's complement either (the most negative has no transformation). – Bathsheba Oct 19 '15 at 17:13
  • Plus one, this works beautifully. – Bathsheba Oct 19 '15 at 17:19
  • @Bathsheba: absolutely, but I do only guarantee the solution to work within the range -*L* to *L*, inclusive, with `INT_MIN` < -*L*, *L* < `INT_MAX`, and *L* > 1, where `INT_MIN` and `INT_MAX` define the architecture-dependent range of representable `int` integers; negation is then valid for all integers *x* within *L* -1 to *L* +1, inclusive. – Nominal Animal Oct 19 '15 at 17:36
  • That's fine imo: negation has constraints too for 2's complement. – Bathsheba Oct 19 '15 at 18:40
1

Implementing the solution suggested by @Bathsheba.

The following code is assuming 32 bit int and that the casting of unsigned to signed is defined as the reverse of casting signed to unsigned:

int F(int x) {
    unisgned real, im, ux = (unsigned) x;

    // Split into real and imaginary part
    real = ux >> 16;  // Real part is upper 16 bits
    im = ux & 0xFFFF; // Imaginary part is lower 16 bits

    ux = real | (-im) << 16;  // Multiply by i 

    return (int) ux;  // return the result
}

Note: The last cast is implementation defined if ux is to large to be a signed int value.

Community
  • 1
  • 1
Klas Lindbäck
  • 33,105
  • 5
  • 57
  • 82
  • You see, it wasn't *that* bad! Plus one. Isn't << 16 UB for a signed short though ;-) – Bathsheba Oct 19 '15 at 08:09
  • Yes, so I changed them to `int`. I cheated by skipping on the portability. You can make it work with any size `int` but the code is a bit trickier and somewhat harder to read. – Klas Lindbäck Oct 19 '15 at 08:11
  • Left shift on any negative number invokes undefined behavior. One way around it is to use uint32_t internally and handle the sign separately. – Lundin Oct 19 '15 at 08:18
  • To be very pedantic, you need to worry about 1's complement `int`. – Bathsheba Oct 19 '15 at 08:22
  • Changed to use `unsigned` internally, but didn't fix the sign bit, so it isn't guaranteed to work in all C implementations. – Klas Lindbäck Oct 19 '15 at 08:49