设计函数 f(f(n)) == -n

Designing function f(f(n)) == -n

提问人: 提问时间:4/9/2009 最后编辑:13 revs, 9 users 27%Gumbo 更新时间:8/13/2022 访问量:84587

问:

我在上次面试中遇到的一个问题:

设计一个函数,使得:f

f(f(n)) == -n

其中是 32 位有符号整数;不能使用复数算术。n

如果无法为整个数字范围设计这样的函数,请将其设计为尽可能大的范围。

有什么想法吗?

数学 整数

评论

6赞 tymtam 8/2/2017
这次面试是为了什么工作?

答:

106赞 12 revs, 2 users 96%Daniel Brückner #1

所有负数都是如此。

    f(n) = abs(n)

因为对于二进制补码整数,负数比正数多一个,所以比与 相同的解多一个情况有效。把你弄到一个... :Df(n) = abs(n)f(n) = n > 0 ? -n : nf(n) = -abs(n)

更新

不,它对另一种情况无效,因为我刚刚通过 litb 的评论认识到...... 只会溢出......abs(Int.Min)

我也考虑过使用 mod 2 信息,但得出的结论是,它不起作用......到早期。如果操作得当,它将适用于所有数字,除非这会溢出。Int.Min

更新

我玩了一段时间,寻找一个不错的操作技巧,但我找不到一个好的单行,而 mod 2 解决方案适合其中之一。

    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 : -nuncheckedInt.Min

更新

受到另一个答案的启发,我将解释该函数的工作原理以及如何构造这样的函数。

让我们从头开始。该函数被重复应用于给定值,从而产生一系列值。fn

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

问题要求,即否定论证的两次连续应用。另外两个应用(总共四个)再次否定了该论点,再次产生。f(f(n)) = -nffn

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

现在有一个明显的长度四的循环。代入并注意得到的方程成立,得到以下结果。x = f(n)f(f(f(n))) = f(f(x)) = -x

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

因此,我们得到一个长度为四的循环,其中有两个数字,两个数字被否定。如果将循环想象为矩形,则否定值位于相对的角处。

构建这种循环的众多解决方案之一是从 n 开始的以下解决方案。

 n                 => negate and subtract one
-n - 1 = -(n + 1)  => add one
-n                 => negate and add one
 n + 1             => subtract one
 n

这种循环的一个具体例子是。我们快完成了。注意到构造的循环包含一个奇数正数,它的偶数后继数,并且两个数都否定,我们可以轻松地将整数划分为许多这样的循环(是 4 的倍数),并找到了满足条件的函数。+1 => -2 => -1 => +2 => +12^32

但是我们有一个零的问题。循环必须包含,因为零本身被否定。而且因为循环状态已经存在,所以它遵循.这只是一个长度为二的循环,并且在两次应用后变成它自己,而不是变成 .幸运的是,有一个案例可以解决这个问题。如果等于零,我们得到一个长度为 1 的循环,其中只包含零,我们解决了这个问题,得出的结论是零是 的不动点。0 => x => 00 => x0 => x => 0 => xx-xXf

做?几乎。我们有数字,零是留下数字的不动点,我们必须将该数字划分为四个数字的循环。不好的是,这不是 4 的倍数 - 将剩下三个数字,而不是在任何长度为 4 的循环中。2^322^32 - 12^32 - 1

我将使用较小的 3 位有符号迭代器集来解释解决方案的其余部分,范围从 到 。我们完成了零。我们有一个完整的周期。现在让我们从 .-4+3+1 => -2 => -1 => +2 => +1+3

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

出现的问题是不能表示为 3 位整数。我们将通过否定 - 仍然是一个有效的 3 位整数 - 但随后将一个添加到 (二进制) 会产生二进制。它被解释为无符号整数,但我们必须将其解释为有符号整数。因此,实际上对于这个例子,或者在一般情况下,是整数算术否定的第二个不动点 - 并且映射到它们本身。所以循环其实是这样的。+4+4-3+3+3011100+4-4-4Int.MinValue0Int.MinValue

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

它是一个长度为二的循环,另外通过 进入循环。因此,在两个函数应用程序之后被正确映射到自身,在两个函数应用程序之后被正确映射到自身,但在两个函数应用程序之后被错误地映射到自身。+3-4-4+3-3-3

因此,我们构造了一个函数,该函数适用于除一个整数之外的所有整数。我们能做得更好吗?不,我们不能。为什么?我们必须构造长度为 4 的周期,并且能够覆盖最多四个值的整个整数范围。其余值是两个固定点,必须映射到它们自身和两个任意整数,并且必须由两个函数应用程序相互映射。0Int.MinValuex-x

要映射到,反之亦然,它们必须形成一个四个循环,并且它们必须位于该循环的相对角。因此,也必须处于对立的角落。这将正确映射,但在两个函数应用程序之后交换两个固定点,并给我们留下两个失败的输入。因此,不可能构造一个适用于所有值的函数,但我们有一个适用于除一个值之外的所有值的函数,这是我们能实现的最好的函数。x-x0Int.MinValuex-x0Int.MinValue

评论

0赞 Dan Olson 4/9/2009
不符合条件:abs(abs(n)) != -n
0赞 jalf 4/9/2009
当然,对于所有负数,就像他说的那样。这是问题的一部分:如果你不能想出一个通用的,那就想出一个适用于最广泛范围的。
0赞 1800 INFORMATION 4/9/2009
这个答案至少和 Marj Synowiec 和 Rowland Shaw 的答案一样好,它只是适用于不同的数字范围
20赞 Andres Jaan Tack 6/9/2010
伙计,你还不如去掉“更新”,只写一个有凝聚力的正确答案。底部的 3/4(“灵感来自另一个答案”)很棒。
1赞 Thorbjørn Ravn Andersen 1/19/2011
我真的很喜欢负数的 abs 解决方案。简单易懂。
46赞 5 revsJoel Coehoorn #2

根据您的平台,某些语言允许您在函数中保留状态。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赞 Airsource Ltd 5/5/2009
我相信它确实对MAX_INT有用,因为这总是很奇怪。它不适用于 MIN_INT 和 -1。
9赞 nos 8/8/2009
如果它有副作用,它就不是一个功能。
12赞 Ryan Lundy 8/27/2009
这在数学中可能是正确的,但在编程中却无关紧要。所以问题是他们在寻找数学解决方案还是编程解决方案。但鉴于这是为了编程工作......
0赞 phkahler 2/15/2010
+1 我打算在 C 中发布一个带有“static int x”的 FIFO 并否定输出。但这已经足够接近了。
2赞 Clark Gaebel 9/11/2010
@nos:是的,它只是在引用上不透明。
11赞 llamaoo7 #3

我可以想象使用第 31 位作为假想 (i) 位将是一种支持总范围一半的方法。

评论

0赞 1800 INFORMATION 4/9/2009
这将更复杂,但不会比当前的最佳答案更有效
1赞 Jochen Walter 4/9/2009
@1800 信息:另一方面,域 [-2^30+1, 2^30-1] 是连续的,从数学的角度来看更具吸引力。
10赞 MartinStettner #4

适用于 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
}
378赞 14 revs, 7 users 31%RossFabricant #5

怎么样:

f(n) = sign(n) - (-1)ⁿ * n

在 Python 中:

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)

Python 会自动将整数提升为任意长度的长整型。在其他语言中,最大的正整数将溢出,因此它适用于除该整数之外的所有整数。


要使它适用于实数,您需要将 (-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赞 Joel Coehoorn 4/9/2009
为 -1 中断,因为 -1 * 0 仍然是 0
3赞 1800 INFORMATION 4/9/2009
不,不是。f(-1) = 0。f(0) = 1
5赞 1800 INFORMATION 4/9/2009
然而,它被破坏了 1。f(1) = 0。f(0) = 1
18赞 Unknown 4/9/2009
嗯,用偶数和奇数保存状态,我应该想到这一点。
38赞 isekaijin 4/13/2009
我认为最重要的不是实际函数(有无限多的解决方案),而是构造这样一个函数的过程。
50赞 2 revsDaniel LeCheminant #6

这个问题没有说明函数 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 位整数,则该语句将为 true。f(f(n)) == -n

显然,这种方法可以扩展到更广泛的数字......

评论

2赞 jalf 4/9/2009
是的,我正在研究类似的方法。不过你打败了我。+1 :)
1赞 Kirk Broadhurst 8/26/2009
非常聪明!这与使用复数非常接近(实际上相同),这将是显而易见和理想的解决方案,但它明确不允许。在允许的数字范围之外工作。
1赞 2 revsBen Blank #7

在MIN_INT上不会失败:

int f(n) { return n < 0 ? -abs(n + 1) : -(abs(n) + 1); }

评论

0赞 Hannes Ovrén 4/15/2009
@Stephan202,对我来说似乎很好:f(0) = -1,f(-1) = 0
0赞 comonad 7/10/2011
但是现在它在 f(f(-1))==-1 上失败了
2赞 Joe Phillips #8
f(n) { return -1 * abs(n) }

如何处理溢出问题?还是我错过了重点?

评论

0赞 SilentGhost 4/9/2009
即使输入为负值,也始终返回负值
1赞 JasonTrue 4/9/2009
考虑到问题的要求,这可能是一个适当的回答。它并没有说 f(n) 必须返回不同的值。这个问题是一个谜题,所以你要寻找可以成为可行解决方案的漏洞(漏洞)。
0赞 David Archer 4/9/2009
@Jason:问题在于,对于负输入 n,f(f(n)) 应该返回一个正数(根据问题),而您的解决方案无法做到这一点,因为它总是返回一个负数。
3赞 Steven 8/26/2009
在您的例子中,f(f(-1)) = f(1) = -1。因此,对于 n<0,f(f(n)) = n。
1赞 user unknown 5/30/2011
适用于近 50% 的输入。可重现且线程安全。:)有一些劣质的解决方案获得了更多的赞成票。:)
445赞 2 revs, 2 users 81%viraptor #9

你没有说他们期待什么样的语言......这是一个静态解决方案(Haskell)。它基本上弄乱了 2 个最重要的位:

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

在动态语言(Python)中要容易得多。只需检查参数是否为数字 X 并返回返回 -X 的 lambda:

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

评论

23赞 Mark Renouf 4/9/2009
很酷,我喜欢这个......JavaScript 中的相同方法: var f = function(n) { return (typeof n == 'function') ? n() : function() { return -n; } }
0赞 Darius Bacon 4/9/2009
可能只是我的 Haskell 非常生锈,但你有没有检查过 (f 0)?看起来这应该产生与 (f 0x80000000) 相同的结果,至少如果我们处理的是带有环绕算术的 32 位整数(在否定运算上)。那会很糟糕。
11赞 Jeremy Powell 8/27/2009
普通面试官甚至会知道 lambda 结构是什么吗?
4赞 leftaroundabout 3/17/2013
当然,这种类型作弊的伎俩在 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=($()) }
4赞 Harry 4/25/2013
什么?你从哪里得到 typeof f(n) === 'function' 的想法,特别是 n 是一个数字,你期望返回一个数字?我不明白实例案例怎么可能在这里适用。我不会说 Python,但在这种情况下,在 JS 中检查函数类型的参数是完全错误的。这里只适用数值解。f 是一个函数,f(n) 是数字。
49赞 2 revscobbal #10

对于 JavaScript(或其他动态类型语言),您可以让函数接受 int 或 object 并返回另一个。即

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;
}

评论

0赞 Drew 4/9/2009
后者并不意味着“a”(奇异)函数的要求。:)
0赞 jmucchiello 4/9/2009
去掉答案的后半部分,这是一个正确的答案。
0赞 cobbal 4/9/2009
@Drew这就是为什么我提到它可能会违反规则
2赞 Nosredna 6/4/2009
在 JavaScript 中,函数是一个对象,因此可以保持状态。
1赞 SamGoody 6/25/2013
IMO: function f(n){ return n.passed ? -n.val : {val:n, passed:1} } 可读性更强,更短。
23赞 3 revs, 2 users 79%Eclipse #11

对于所有 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 循环与 y => -y => y 循环配对。所以我配对了 .split

例如,对于 4 位整数:

0 => 7 => -8 => -7 => 0
1 => 6 => -1 => -6 => 1
2 => 5 => -2 => -5 => 2
3 => 4 => -3 => -4 => 3
1赞 2 revsMatt Rogish #12

如前所述的问题并不要求函数必须只接受 32 位整数,只要求给定的 n 是 32 位整数。

红宝石:

def f( n )
  return 0 unless n != 0 
  ( n == n.to_i ) ? 1.0 / n : -(n**-1).to_i
end
1赞 Rui Craveiro #13

这将在非常广泛的数字范围内起作用:

    static int f(int n)
    {
        int lastBit = int.MaxValue;
        lastBit++;
        int secondLastBit = lastBit >> 1;
        int tuple = lastBit | secondLastBit;
        if ((n & tuple) == tuple)
            return n + lastBit;
        if ((n & tuple) == 0)
            return n + lastBit;
        return -(n + lastBit);
    }

我最初的方法是使用最后一个位作为检查位,以了解我们在第一次或第二次调用中的位置。基本上,我会在第一次调用后将此位置于 1,以表示第一次调用已经通过的第二个调用。但是,这种方法被负数打败了,负数的最后一位在第一次调用时已经到达 1。

同样的理论也适用于大多数负数的倒数第二位。但是,通常发生的事情是,大多数时候,最后一位和倒数第二位是相同的。它们要么是负数的 1,要么是正数的 0。

因此,我的最后一个方法是检查它们是 1 还是都是 0,这意味着在大多数情况下,这是第一个调用。如果最后一位与倒数第二位不同,那么我假设我们在第二次调用时,只需重新反转最后一位。显然,这不适用于使用最后两位的非常大的数字。但是,再一次,它适用于非常广泛的数字。

13赞 Pop Catalin #14

C# 范围为 2^32 - 1 个数字,除 (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

评论

0赞 Dinah 8/27/2009
这也不适用于 f(0),它是 1073741824。f(1073741824) = 0。f(f(1073741824)) = 1073741824
0赞 slacker 8/28/2010
通常可以证明,对于任何位大小的 2 补码整数类型,该函数必须不适用于至少两个输入值。
9赞 Drew #15

:D

boolean inner = true;

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

评论

5赞 palswim 8/13/2010
可能还会让你讨论一下,如果全局变量没有把你踢出面试,为什么它们会很糟糕!
12赞 4 revs, 2 users 86%Strilanc #16

从本质上讲,该函数必须将可用范围划分为大小为 4 的周期,其中 -n 位于 n 周期的另一端。但是,0 必须是大小为 1 的循环的一部分,否则 .由于 0 是单独的,因此我们的范围内必须有 3 个其他值(其大小是 4 的倍数)不在具有 4 个元素的适当循环中。0->x->0->x != -x

我选择了这些额外的奇怪值 , , 和 。此外,将正确映射到,但卡在那里而不是映射回来。我认为这是最好的折衷方案,因为它具有只有极值无法正常工作的良好属性。此外,这意味着它适用于所有 BigInt。MIN_INTMAX_INTMIN_INT+1MIN_INT+1MAX_INT

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)
2赞 2 revs, 2 users 97%Daniel Spiewak #17

Scala 中一个奇怪的、只有一点点聪明的解决方案,使用隐式转换:

sealed trait IntWrapper {
  val n: Int
}

case class First(n: Int) extends IntWrapper
case class Second(n: Int) extends IntWrapper
case class Last(n: Int) extends IntWrapper

implicit def int2wrapper(n: Int) = First(n)
implicit def wrapper2int(w: IntWrapper) = w.n

def f(n: IntWrapper) = n match {
  case First(x) => Second(x)
  case Second(x) => Last(-x)
}

不过,我不认为这是完全正确的想法。

评论

0赞 user unknown 5/30/2011
我把你的'Final(-x)'改成了'Last (-x)',但f(f(8))的结果不是-8,而是Last (-8)。对不起。:)
0赞 Daniel Spiewak 6/2/2011
Last(-8)隐式可转换为 ,这就是使解决方案起作用的原因。-8
6赞 2 revsFrank Crook #18

在PHP中

function f($n) {
    if(is_int($n)) {
        return (string)$n;
    }
    else {
        return (int)$n * (-1);
    }
}

我相信你能理解这种方法对其他语言的精神。我显式地转换回 int,以便让不使用弱类型语言的人更清楚。对于某些语言,您必须重载该函数。

这个解决方案的巧妙之处在于,无论你从字符串还是整数开始,它都能正常工作,并且在返回 f(n) 时不会明显改变任何内容。

在我看来,面试官是在问,“这个候选人是否知道如何标记数据以供以后操作”,以及“这个候选人是否知道如何标记数据,同时至少更改数据?您可以使用双精度、字符串或任何其他您想要强制转换的数据类型来执行此操作。

评论

0赞 jrockway 8/27/2009
但是,当然,在实际代码中使用这样的黑客是一个糟糕的主意。如果需要将状态与数据存储在一起,请创建一个新类型(如 (s, a),并使用绑定运算符仅处理结果。(这与 Haskell 的状态单子非常相似,但并不相同。
1赞 Kristof Neirynck #19

似乎很容易。

<script type="text/javascript">
function f(n){
    if (typeof n === "string") {
        return parseInt(n, 10)
    }
    return (-n).toString(10);
}

alert(f(f(1)));
</script>

评论

1赞 joshcomley 5/30/2009
它特别指出“其中 n 是有符号的 32 位 int”,而不是字符串
0赞 Nosredna 5/31/2009
不,我认为他没事。您可以输入任何 32 位 int。它并没有说 f(n) 不能是字符串。
21赞 3 revs, 3 users 68%Skizz #20

C++ 版本,可能稍微扭曲了规则,但适用于所有数值类型(浮点数、整数、双精度值),甚至是重载一元减号的类类型:

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;
}

评论

0赞 Imbue 4/10/2009
好主意。作为替代方案,您可能会丢失结构,而是让一个函数返回指针,另一个函数取消引用和否定。
137赞 5 revs, 4 users 76%Skizz #21

或者,您可以滥用预处理器:

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

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

评论

0赞 Skizz 4/10/2009
那么你会是康拉德·鲁道夫(Konrad “Le Chiffre” Rudolph)吗?我去拿我的外套。是的,我知道整个“void main”的事情,但是添加一个“return 0;”只是很多额外的努力;-)
25赞 Dan Olson 4/10/2009
@Skizz,即使有 int 返回值,C++ 也不需要从 main 返回 0......因此,通过正确地操作,您实际上可以少输入一个字符!
10赞 Arnis Lapsa 7/29/2009
Skizz 总是滥用预处理器:D
23赞 smerlin 3/19/2010
这不是一个函数。所以这不是一个有效的解决方案
3赞 Jon Purdy 4/27/2010
@smerlin:从技术上讲,它是一个返回内联函数的内联函数:两者的主体在编译时展开,或者更确切地说是在编译之前展开的。没有比这更高效的了。
19赞 teeks99 #22

使用全局...但事实呢?

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

评论

3赞 Liran Orevi 5/4/2009
不确定这是否是提问者的意图,但 +1 表示“跳出框框思考”。
5赞 Chris Lutz 7/27/2009
与其有条件地说“done = true”,不如始终说“done = !done”,这样你的函数就可以多次使用。
0赞 nsayer 4/27/2010
@Chris,由于将 done 设置为 true 位于 if(!done) 块内,因此它等效于 done=!done,但 !done 不需要计算(如果编译器足够聪明,也不需要由编译器优化)。
1赞 Alderath 7/12/2010
我的第一个想法也是使用全局变量来解决这个问题,尽管这有点像在这个特定问题上作弊。然而,我认为,鉴于问题中的规范,全局变量解决方案是最佳解决方案。使用全局可以非常容易地理解正在发生的事情。不过,我同意完成 != 完成会更好。只需将其移到 if 子句之外即可。
3赞 Ted Hopp 12/19/2011
从技术上讲,任何维持状态的东西都不是函数,而是状态机。根据定义,函数总是为相同的输入提供相同的输出。
-4赞 Alex #23

怎么样

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

评论

1赞 palswim 8/13/2010
好吧,您只需要指定它仅适用于正整数(0 <= n < 2^31)。
9赞 Mike Meehan #24
return x ^ ((x%2) ? 1 : -INT_MAX);
101赞 3 revs, 3 users 83%geschema #25

使用复数,可以有效地将否定数字的任务分为两个步骤:

  • 将 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。

alt text

评论

4赞 jwfearn 4/13/2009
@geschema,你用什么工具来创建这些漂亮的图形?
10赞 Rui Craveiro 4/14/2009
对不起,这个问题明确说没有复数。
6赞 geschema 5/5/2009
@Liran:我使用了 OmniGraffle(仅限 Mac)
40赞 jrista 9/26/2009
+1 我认为这是最好的答案。我认为人们读得不够多,因为他们都注意到这个问题说不能使用复数。我读了整篇文章,你用复数描述了解决方案,为所提问题的非复杂解决方案奠定了基础。做得非常好。
1赞 Kirk Broadhurst 11/30/2011
@jrista所有的解决方案都使用第二个维度,这就是“复数”的真正含义(大多数使用奇数与偶数,& 上面使用 vs )。许多答案描述的“4 元素环”需要 4 种状态,这些状态可以表示为 2 个维度,每个维度有 2 个状态。这个答案的问题在于它需要额外的处理空间(仅适用于 -64..63,但需要 -128..127 空间),并且没有明确说明书面公式!floatint
150赞 2 revs, 2 users 91%Comptrol #26

感谢 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赞 isekaijin 4/11/2009
不幸的是,由于名称篡改,您称为“f”的函数实际上具有更奇怪的名称。
1赞 Liran Orevi 5/4/2009
我想过类似的东西,但在 C 中思考,这被扔掉了......干得好!
0赞 Kredns 6/4/2009
@Rui Craverio:它在 .NET 3.5+ 中不起作用,因为作者选择使用 var 关键字作为变量名称。
75赞 elcuco 8/26/2009
技术。。。这不是问题所要求的。您定义了 2 个 f() 函数 f(int) 和 f(float),问题问“设计一个函数 f() ..."
4赞 Mark Toman 6/25/2013
@elcuco 当然,从技术上讲,但从逻辑上讲,它是一个具有多个重载的函数(你可以用它来做 f(f(42))。由于该定义没有说明参数和返回值,因此我很难接受它作为技术定义。
16赞 2 revspeSHIr #27

我实际上并不是要为问题本身提供解决方案,但确实有几点评论,因为问题指出这个问题是(工作?)面试的一部分:

  • 我首先会问:“为什么需要这样的功能?这是其中更大的问题是什么?“而不是试图当场解决实际提出的问题。这显示了我的想法以及我如何处理这样的问题。谁知道呢?这甚至可能是最初在面试中被问到这个问题的真正原因。如果答案是“没关系,假设它是需要的,并告诉我你将如何设计这个功能。然后我会继续这样做。
  • 然后,我将编写我将使用的 C# 测试用例代码(显而易见的:循环从 到 ,对于该范围内的每个调用并检查结果是 ),告诉我将使用测试驱动开发来获得这样的函数。int.MinValueint.MaxValuenf(f(n))-n
  • 只有当面试官继续要求我解决提出的问题时,我才会真正开始尝试在面试过程中潦草地编写伪代码,以试图得到某种答案。但是,如果面试官能表明公司是什么样的,我真的不认为我会跳出来接受这份工作......

哦,这个答案假设面试是针对 C# 编程相关职位的。如果面试是与数学相关的职位,那当然是一个愚蠢的答案。;-)

评论

7赞 alex2k8 4/13/2009
你很幸运,他们要求 32 int,如果是 64 位,那么在你运行测试后,面试将永远不会继续;-)
0赞 peSHIr 4/14/2009
事实上,如果我真的能写出那个测试并在面试中运行它。;-)我的观点是:我会尽量不要在采访中谈到这一点。在我看来,编程更像是“一种思维方式”,而不是“他如何写代码行”。
7赞 Stefan Haustein 6/25/2013
不要在实际面试中遵循这个建议。面试官希望你真正回答这个问题。质疑问题的相关性不会给你带来任何好处,但可能会惹恼面试官。设计一个微不足道的测试不会让你离答案更近一步,你不能在面试中运行它。如果您获得额外的信息(32 位),请尝试弄清楚它如何有用。
1赞 peSHIr 8/16/2013
当我要求提供更多信息时,面试官会感到恼火(同时可能会质疑他的问题在过程中的相关性),这不是我一定想为之工作的面试官。所以我会在采访中继续问这样的问题。如果他们不喜欢,我可能会结束采访,不再浪费我们俩的时间。有点不喜欢“我只是服从命令”的心态。是吗。。?
1赞 nilamo #28

也许是作弊?(蟒蛇)

def f(n):    
    if isinstance(n, list):
        return -n[0]
    else:
        return [n,0]    
n = 4
print f(f(n))

--output--
-4
1赞 Mitch #29

容易:

function f($n) {
   if ($n%2 == 0) return ($n+1)*-1;
   else return ($n-1);
}

评论

0赞 user unknown 5/30/2011
f(f(8)) => 8%2 == 0 为真 => (8+1)*-1 = 9 * -1 = -9。=> -9 -1 = -10。这看起来很不对劲(但至少很简单)。
2赞 Brian Carper #30

Clojure 解决方案:

(defmacro f [n]
  (if (list? n) `(- ~n) n))

也适用于任何大小、双精度和比率的正整数和负整数!

12赞 2 revs, 2 users 97%Christopher Smith #31

没有人说它必须是无国籍的。

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的情况来说,这两种方法都不太有效,但是除非允许返回更广泛的类型,否则您对此无能为力。

评论

0赞 IUnknownPointer 6/28/2010
你可以“升级”它来询问地址(是的,你必须通过 ref\ 作为指针来获取它)——在 C 中,例如: int f(int& n) { static int* addr = &n; if (addr == &n) { return -n; } return n; }
1赞 xcramps #32

在 C 语言中,

int 
f(int n) {
     static int r = 0;
     if (r == 1) {r--; return -1 * n; };
     r++;
     return n;
}

知道这是针对什么语言会有所帮助。 我错过了什么吗?许多“解决方案”似乎过于复杂,坦率地说,并非如此 工作(当我读到问题时)。

评论

0赞 Bill K 8/29/2009
这是一个有效的解决方案,但正如我在另一篇文章中提到的,这取决于他们想要如何定义函数(真正的函数不是有状态的,它对任何给定的输入值只有一个返回值)。观察你对在这样的函数中实现状态的感受也很有价值(它应该让你感到恶心,你应该自然而然地尽量避免它,直到它成为唯一可能的解决方案 IMO)
-1赞 2 revs, 2 users 71%paperhorse #33

我认为尽可能大的范围暗示了模块化算术解决方案。在一些模基 M 中,有一个数字,当平方时与 M-1 全等(与 -1 全等)。例如,如果 M=13, 5*5=25, 25 mod 13=12 (= -1)
无论如何,这里有一些 M=2**32-3 的 python 代码。

def f(x):
    m=2**32-3;
    halfm=m//2;
    i_mod_m=1849436465
    if abs( x ) >halfm:
        raise "too big"
    if x<0:
        x+=m
    x=(i_mod_m*x) % m
    if (x>halfm):
        x-=m
    return x;

请注意,有 3 个值不适用于 2 ** 31-1、-(2 ** 31-1) 和 -(2 ** 31)

1赞 splicer #34

这是 rossfabricant 答案的 C 实现。请注意,由于我一直坚持使用 32 位整数,因此 f( f( 2147483647 ) ) == 2147483647,而不是 -2147483647。

int32_t f( int32_t n )
{
    if( n == 0 ) return 0;
    switch( n & 0x80000001 ) {
        case 0x00000000:
            return -1 * ( n - 1 );
        case 0x00000001:
            return n + 1;
        case 0x80000000:
            return -1 * ( n + 1 );
        default:
            return n - 1;
    }
}

如果将问题定义为允许 f() 接受并返回int64_t,则涵盖2147483647。当然,switch 语句中使用的文字必须更改。

评论

0赞 Steven 8/26/2009
字节顺序可能存在一些可移植性问题。
1赞 Xander #35

这个怎么样(C语言):

int f(int n)
{
    static int t = 1;
    return (t = t ? 0 : 1) ? -n : n;
}

刚刚尝试过,然后

f(f(1000)) 

返回 -1000

f(f(-1000)) 

返回 1000

这是正确的还是我错过了重点?

评论

1赞 Tarnay Kálmán 9/26/2009
这不是线程安全的,并且在多次调用时不起作用。
1赞 2 revsAndrej Panjkov #36

实际上,这些问题更多的是关于看到面试官与规范、设计、错误处理、边界情况和解决方案的合适环境选择等搏斗,而不是关于实际解决方案。但是: :)

这里的函数是围绕封闭的 4 个周期的想法编写的。如果函数 f 只允许落在有符号的 32 位整数上,那么上面的各种解决方案都将起作用,除了其他人指出的三个输入范围数字。minint 永远不会满足函数式,因此如果这是输入,我们将引发异常。

在这里,我允许我的 Python 函数操作并返回元整数。任务规范承认这一点,它只指定函数的两个应用程序应该返回一个对象,如果它是 int32,则该对象等于原始对象。(我会询问有关规范的更多细节。

这使得我的轨道很好且对称,并覆盖了所有输入整数(最小值除外)。我最初设想循环访问半整数值,但我不想纠结于舍入错误。因此,元组表示形式。这是一种将复杂旋转作为元组潜入的方法,而无需使用复杂的算术机制。

请注意,在调用之间不需要保留任何状态,但调用方确实需要允许返回值为元组或整数。

def f(x) :
  if isinstance(x, tuple) :
      # return a number.
      if x[0] != 0 :
        raise ValueError  # make sure the tuple is well formed.
      else :
        return ( -x[1] )

  elif isinstance(x, int ) :
    if x == int(-2**31 ):
      # This value won't satisfy the functional relation in
      # signed 2s complement 32 bit integers.
      raise ValueError
    else :
      # send this integer to a tuple (representing ix)
      return( (0,x) )
  else :
    # not an int or a tuple
    raise TypeError

因此,将 f 应用于 37 两次得到 -37,反之亦然:

>>> x = 37
>>> x = f(x)
>>> x
(0, 37)
>>> x = f(x)
>>> x
-37
>>> x = f(x)
>>> x
(0, -37)
>>> x = f(x)
>>> x
37

将 f 应用于零两次得到零:

>>> x=0
>>> x = f(x)
>>> x
(0, 0)
>>> x = f(x)
>>> x
0

我们处理一个问题没有解决方案的情况(在 int32 中):

>>> x = int( -2**31 )
>>> x = f(x)

Traceback (most recent call last):
  File "<pyshell#110>", line 1, in <module>
    x = f(x)
  File "<pyshell#33>", line 13, in f
    raise ValueError
ValueError

如果您认为该函数通过模仿乘以 i 的 90 度旋转来打破“无复杂算术”规则,我们可以通过扭曲旋转来改变它。这里的元组表示半整数,而不是复数。如果在数轴上跟踪轨道,则将得到满足给定函数关系的不相交环。

f2: n -> (2 abs(n) +1, 2 sign( n) ) if n is int32, and not minint.
f2: (x, y) -> sign(y) * (x-1) /2  (provided y is \pm 2 and x is not more than 2maxint+1

练习:通过修改 f 来实现此 f2。还有其他解决方案,例如,让中间着陆点是除半整数之外的有理数。有一个分数模块可能会被证明是有用的。您需要一个符号函数。

这个练习真的让我领悟到了动态类型语言的乐趣。我在 C 中看不到这样的解决方案。

7赞 2 revsYoo #37

作为一个数学家,我想分享我对这个有趣问题的看法。我认为我有最有效的解决方案。

如果我没记错的话,你只需翻转第一位就否定了一个有符号的 32 位整数。例如,如果 n = 1001 1101 1110 1011 1110 0000 1110 1010,则 -n = 0001 1101 1110 1011 1110 0000 1110 1010。

那么我们如何定义一个函数 f,它接受一个有符号的 32 位整数并返回另一个有符号的 32 位整数,其属性是取 f 两次与翻转第一位相同?

让我重新表述这个问题,而不提及整数等算术概念。

我们如何定义一个函数 f,它接受长度为 32 的 0 和 1 序列并返回相同长度的 0 和 1 序列,其属性是取 f 两次与翻转第一位相同?

观察:如果你能回答上面的32位大小写问题,那么你也可以回答64位大小写、100位大小写等。您只需将 f 应用于前 32 位。

现在,如果您能回答 2 位大小写的问题,瞧!

是的,事实证明,更改前 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赞 Windows programmer 4/21/2009
是的,这是一个有趣的问题。你知道你的数学。但这是一个计算机科学问题。所以你需要学习计算机。符号大小表示是允许的,但它在大约 60 年前就过时了。2'S-complement 最受欢迎。
5赞 buti-oxa 5/31/2009
以下是函数在应用两次时对这两个位执行的操作:(a,b) --> (-b, a) --> (-a, -b)。但是,我们试图得到(-a,b),而不是(-a,-b)。
0赞 Yoo 6/1/2009
@buti-oxa,你是对的。两位操作应如下所示:00 -> 01 -> 10 -> 11 -> 00。但是,正如我 Windows 程序员所说,我的算法假设了现在不流行的符号大小表示,所以我认为我的算法没什么用。
0赞 Nosredna 6/4/2009
那么,他就不能只做两次而不是一次吗?
4赞 redtuna 6/23/2010
Buti-Oxa 是完全正确的:该函数在两次调用后甚至不会翻转第一位,而是翻转前两位。翻转所有位更接近 2 的补码,但它并不完全正确。
-1赞 Alex #38
int f(int x){
    if (x < 0)
        return x;
    return ~x+1; //two's complement
}

评论

0赞 Windows programmer 4/23/2009
-1 失败。f(f(-1)) 应该是 -(-1) 即 +1,但你的结果是 -1。
1赞 pi. #39

这个是用 Python 编写的。适用于 n 的所有负值:

f = abs

评论

0赞 comonad 7/10/2011
也许如果它是用 Haskell 而不是 Python 编写的,那么它的一半就不会是错误了。(双关语:你在 Haskell 中写得完全一样。)
1赞 tommym #40

这是我从未见过人们使用的变体。由于这是 ruby,所以 32 位整数的东西有点消失了(当然可以添加检查)。

def f(n)
    case n
    when Integer
        proc { n * -1 }
    when Proc
        n.call
    else
        raise "Invalid input #{n.class} #{n.inspect}"
    end
end

(-10..10).each { |num|
    puts "#{num}: #{f(f(num))}"
}
290赞 6 revs, 5 users 75%SurDin #41

这里有一个证明,为什么这样的函数不能存在,对于所有数字,如果它不使用额外的信息(除了 32 位的 int):

我们必须有 f(0) = 0。(证明:假设 f(0) = x。则 f(x) = f(f(0)) = -0 = 0。现在,-x = f(f(x)) = f(0) = x,这意味着 x = 0。

此外,对于任何 和 ,假设 .我们想要那时。和。总而言之:如果 、 那么 、 和 和 。xyf(x) = yf(y) = -xf(f(y)) = -y => f(-x) = -yf(x) = yf(-x) = -yf(y) = -xf(-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 个数字作为奖励。(但这只是一个愚蠢的假设。

评论

30赞 Kyle Simek 5/10/2009
我简直不敢相信我必须深入阅读才能找到一个好的程序解决方案,该解决方案可以处理负数,而无需诉诸全局变量或混淆代码的技巧。如果我能不止一次地投票给你,我会的。
0赞 Andres Jaan Tack 6/9/2010
很好的观察,在任何 n 个有符号位中都有奇数个非零整数。
0赞 Kirk Broadhurst 11/30/2011
这也是我的答案,但要注意边缘情况(最小值);在这种情况下,您不能这样做,并且结果将是 undefined(或异常)。n = -2147483648abs(n)
1赞 ShreevatsaR 6/25/2013
@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 不能是 -2³¹,因为不存在这样的 x)。再进一步,让 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³¹ 必须与其余部分断开连接(而不是其他任何图像)。
1赞 goffrie 6/26/2013
@will 没有带符号的零,如果(正如我假设的那样)我们谈论的是 2 的补码 32 位整数。
1赞 2 revsIvan #42

创建许多解决方案的一种方法是注意,如果我们将整数分为两个集合 S 和 R s.t -S=S,-R=R,以及一个函数 g s.t g(R) = S

然后我们可以按如下方式创建 F:

如果 x 在 R 中,则 f(x) = g(x)

如果 x 在 S 中,则 f(x) = -invg(x)

其中 invg(g(x))=x 所以 invg 是 g 的反函数。

上面提到的第一种解是分区R=偶数,R=奇数,g(x)=x+1。

我们可以取任意两个无限集合 T,P s.t T+U= 整数集合,取 S=T+(-T), R=U+(-U)。

然后 -S=S 和 -R=R 根据它们的定义,我们可以将 g 视为从 S 到 R 的任何 1-1 对应关系,这必须存在,因为这两个集合都是无限的和可数的,将起作用。

因此,这将为我们提供许多解决方案,但是并非所有解决方案都可以编程,因为它们不会被有限定义。

可以的一个例子是:

R= 可以被 3 整除的数字,S = 不能被 3 整除的数字。

然后我们取 g(6r) = 3r+1,g(6r+3) = 3r+2。

1赞 Daniel Earwicker #43

很简单,只需返回看起来等于任何整数的东西,并且可以从整数转换。f

public class Agreeable
{
    public static bool operator==(Agreeable c, int n)
        { return true; }

    public static bool operator!=(Agreeable c, int n)
        { return false; }

    public static implicit operator Agreeable(int n)
        { return new Agreeable(); }
}

class Program
{
    public static Agreeable f(Agreeable c)
        { return c; }

    static void Main(string[] args)
    {
        Debug.Assert(f(f(0)) == 0);
        Debug.Assert(f(f(5)) == -5);
        Debug.Assert(f(f(-5)) == 5);
        Debug.Assert(f(f(int.MaxValue)) == -int.MaxValue);
    }
}
10赞 2 revsfinnw #44

该问题指出“32 位有符号整数”,但没有指定它们是二进制补码还是一补码

如果您使用 1-complement,则所有 2^32 值都出现在长度为 4 的循环中 - 您不需要 0 的特殊情况,也不需要条件。

在 C 语言中:

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

这工作原理是

  1. 交换高低 16 位块
  2. 反转其中一个块

经过两次传递后,我们得到原始值的按位倒数。在一补表示中,这等同于否定。

例子:

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 8/26/2009
不同架构之间的字节顺序又如何呢?
1赞 finnw 8/26/2009
所有的算术都是 32 位的。我不操作单个字节,因此字节顺序不会影响它。
0赞 Stefan Haustein 6/25/2013
这听起来很接近。您可以假设输入是 2 补码。因此,您可以转换为符号位表示。现在,根据最后一位,您可以翻转第一位和最后一位,或者只翻转最后一位。基本上,你只否定偶数,并一直循环偶数/奇数。因此,您可以在 2 次调用后从奇数返回奇数,甚至数到偶数。最后,您转换回 2-补码。在下面某处发布了此代码。
1赞 DraculaW #45
const unsigned long Magic = 0x8000000;

unsigned long f(unsigned long n)
{    
    if(n > Magic )
    {
        return Magic - n;
    }

    return n + Magic;
} 

0~2^31

评论

0赞 user unknown 5/30/2011
有点复杂的说法 -(abs(n)),不是吗?
1赞 4 revs, 3 users 56%Omu #46
int j = 0;

void int f(int n)
{    
    j++;

    if(j==2)
    {
       j = 0;
       return -n;
    }

    return n;
}

:D

1赞 boko #47

以下情况如何:

int f (int n)
{
    static bool pass = false;
    pass = !pass;
    return pass? n : -n;
}

评论

0赞 user unknown 5/30/2011
为什么这不是一个糟糕的答案?因为它很常见?
3赞 2 revsmateusza #48
int f( int n ){
    return n==0?0:(n&1?n:-n)+(n<0?-1:1);
}

评论

1赞 Dinah 8/27/2009
n&1 的使用类似于布尔值,但会产生一个 int
3赞 Kip 8/27/2009
@Dinah假设这是 C 或 C++,那就没问题了
3赞 2 revs, 2 users 88%Dan #49

我的给出了正确的答案......50%的时间,一直如此

int f (int num) {
    if (rand () / (double) RAND_MAX > 0.5)
         return ~num + 1;
    return num;
}

评论

0赞 user unknown 5/30/2011
输入 7 可以引导第一步到 -7+1,即 -6 或 7。恕我直言,这是错误的,100% 的时间。我投了反对票。
0赞 user unknown 6/7/2011
哦,对不起,我把波浪号~num和-num混淆了。好的 - 没错。
1赞 joshcomley #50

这适用于 -1073741823 到 1073741822 的范围:

int F(int n)
{
    if(n < 0)
    {
        if(n > -1073741824)
            n = -1073741824 + n;
        else n = -(n + 1073741824);
    }
    else
    {
        if(n < 1073741823)
            n = 1073741823 + n;
        else n = -(n - 1073741823);
    }
    return n;
}

它的工作原理是将 32 位有符号 int 的可用范围一分为二。该函数的第一次迭代将 n 单独置于该范围之外。第二次迭代检查它是否超出此范围 - 如果是,则将其放回该范围内,但将其设为负数。

它实际上是一种保留有关值 n 的额外“位”信息的方法。

评论

0赞 joshcomley 5/30/2009
P.S. 没有全局变量,并且具有足够简单的实现,可以在多种语言中工作的优点
16赞 2 revs, 2 users 77%eipipuz #51

我会你改变 2 个最重要的位。

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

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

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

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

正如你所看到的,它只是一个补充,省略了携带的部分。

我是怎么得到答案的?我的第一个想法只是对对称性的需要。4 圈回到我开始的地方。起初我以为,那是 2 位格雷码。然后我认为实际上标准的二进制就足够了。

评论

0赞 Tamas Czinege 11/24/2009
这种方法的问题在于它不适用于 2 的赞美负数(这是每个现代 CPU 都使用的)。这就是为什么我删除了相同的答案。
0赞 Jeffrey L Whitledge 11/24/2009
该问题指定了 32 位有符号整数。此解决方案不适用于 32 位有符号整数的 2 个补码或 1 个补码表示形式。它仅适用于符号和幅度表示,这在现代计算机中非常罕见(浮点数除外)。
1赞 Jeffrey L Whitledge 11/24/2009
@DrJokepu - 哇,六个月后——jinx!
0赞 Bill Michell 11/24/2010
难道您不需要将数字转换为函数内部的符号和大小表示,执行转换,然后在返回之前转换回本机整数表示形式吗?
0赞 jabirali 12/7/2011
我喜欢你基本上通过引入一个虚位:)来实现复数
-1赞 Henrik Paul #52

PHP,不使用全局变量:

function f($num) {
  static $mem;

  $answer = $num-$mem;

  if ($mem == 0) {
    $mem = $num*2;
  } else {
    $mem = 0;
  }

  return $answer;
}

适用于整数、浮点数和数字字符串!

刚刚意识到这做了一些不必要的工作,但是,无论如何

1赞 sebagomez #53
void f(int x)
{
     Console.WriteLine(string.Format("f(f({0})) == -{0}",x));
}

对不起,伙计们...这太诱人了;)

1赞 Viktor Sehr #54

C++解决方案;

long long f(int n){return static_cast <long long> (n);}
int f(long long n){return -static_cast <int> (n);}

int n = 777;
assert(f(f(n)) == -n);
1赞 waxwing #55

另一个作弊解决方案。我们使用一种允许运算符重载的语言。然后我们让 f(x) 返回一些重载的东西,以始终返回 true。这似乎与问题描述相符,但显然违背了谜题的精神。==

Ruby 示例:

class Cheat
  def ==(n)
     true
  end
end

def f(n)
  Cheat.new
end

这给了我们:

>> f(f(1)) == -1
=> true

但也(不太令人惊讶)

>> f(f(1)) == "hello world"
=> true
1赞 2 revs, 2 users 68%Alex #56
int f(const int n)  {
    static int last_n;

    if (n == 0)
        return 0;
    else if (n == last_n)
        return -n;
    else
    {
        last_n = n;
        return n;
    }
}

骇人听闻,但正确。

-1赞 2 revsHooked #57

记住你上一个状态不是一个足够好的答案吗?

int f (int n)
{
    //if count 
    static int count = 0;

    if (count == 0)
        { 
            count = 1;
            return n;
        }

    if (n == 0)
        return 0;
    else if (n > 0)
    {
        count = 0;
        return abs(n)*(-1);
    } 
    else
    {
        count = 0;
        return abs(n);
    }
}

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

评论

0赞 Bill K 8/29/2009
取决于面试官对“功能”的定义。这是一个方法——函数实际上没有状态。但也许他们并不是字面上的意思。
0赞 user unknown 5/30/2011
它不是线程安全的,并且对于表达状态 != 状态过于复杂
1赞 Graphics Noob #58

有些是相似的,但只是想写下我的第一个想法(用 C++ )

#include <vector>

vector<int>* f(int n)
{
  returnVector = new vector<int>();
  returnVector->push_back(n);
  return returnVector;
}

int f(vector<int>* n) { return -(n->at(0)); }

只需使用重载即可使 f(f(n)) 实际调用两个不同的函数

1赞 Ates Goral #59

JavaScript 单行代码:

function f(n) { return ((f.f = !f.f) * 2 - 1) * n; }

评论

0赞 Anurag 6/27/2010
线程-什么?JavaScript 是单线程的,至少在浏览器上是这样
-2赞 2 revsMarsh Ray #60

我还没有看过其他答案,我认为按位技术已经被彻底讨论过了。

我以为我会在 C++ 中想出一些邪恶的东西,希望不是骗子:

struct ImplicitlyConvertibleToInt
{
    operator int () const { return 0; }
};

int f(const ImplicitlyConvertibleToInt &) { return 0; }

ImplicitlyConvertibleToInt f(int & n)
{
    n = 0; // The problem specification didn't say n was const
    return ImplicitlyConvertibleToInt();
}

整个类型和重载是必需的,因为临时引用不能绑定到非常量引用。ImplicitlyConvertibleToInt

当然,现在看它之前是否执行是不确定的。f(n)-n

也许对于这种程度的邪恶,更好的解决方案很简单:

struct ComparesTrueToInt
{
    ComparesTrueToInt(int) { } // implicit construction from int
};
bool operator == (ComparesTrueToInt, int) const { return true; }

ComparesTrueToInt f(ComparesTrueToInt ct) { return ComparesTrueToInt(); }

评论

0赞 Tim Leaf 5/6/2010
C++因为像你这样的人而名声不好。这是一种可怕的做法......虽然它在某种程度上解决了比较问题,但它对任何其他用途都是完全无用和破坏性的。你是对的,它是邪恶的;但这绝不是一件好事。此外。。。如果有人像这样测试你的函数,你会怎么做: 结果是未定义的。在我的 GCC 版本中,它每行打印零。被当场抓获!f(f(x)) == -xcout << x << (f(f(x)) == -x ? " works." : " doesn't work.") << endl;
0赞 4 revs, 4 users 62%rplusg #61
int f(int n)
{
  static long counter=0;
  counter++;
  if(counter%2==0)
    return -n;
  else
    return n;
}
1赞 Alin #62

另一种方法是将状态保持在一位,并在负数的情况下注意二进制表示来翻转它...... 限制为 2^29

int ffn(整数 n) {

    n = n ^ (1 << 30); //flip the bit
    if (n>0)// if negative then there's a two's complement
    {
        if (n & (1<<30))
        {
            return n;
        }
        else
        {
            return -n;
        }
    }
    else
    {
        if (n & (1<<30))
        {
            return -n;
        }
        else
        {
            return n;
        }
    }


}
20赞 LiraNuna #63

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(下溢)。

3赞 Billy ONeal #64

这个怎么样?

int nasty(int input)
{
    return input + INT_MAX/2;
}

评论

0赞 Billy ONeal 1/31/2010
嗯。。。我相信我打错了字。从本质上讲,关键是溢出整数变量,直到它处于负区域。但我相信我在这里搞砸了......忽视。
0赞 user unknown 5/30/2011
有趣的解决方案,我也有类似的解决方案。但我意识到,这不是一个解决方案。谈论字节:1+63=64,64+63=127。甚至 127+1 = -128,而不是 -1。为什么这个错误的解决方案被点赞?
0赞 Billy ONeal 5/30/2011
@user:我不认识我自己。我相信我承认我在评论中搞砸了一些东西。我认为它被点赞了,因为即使这个确切的代码并不完美,这一点也是合理的。我本来会删除这个答案,但决定不删除,因为它是 CW。
1赞 2 revs, 2 users 67%Sam #65
number f( number n)
{
  static count(0);
  if(count > 0) return -n;
  return n;
}

f(n) = n

f(f(n)) = f(n) = -n

评论

3赞 sth 8/24/2009
我想你在某个地方错过了count++
0赞 Sam 8/27/2009
@sth - 哈哈!是的,一定是我喝杯茶的时候了!还有巧克力饼干!
1赞 Steven #66
int f(int n) {
    return ((n>0)? -1 : 1) * abs(n);
}

评论

0赞 Steven 8/26/2009
适用于除 MIN_INT 之外的所有 int 值(因为 MIN_INT * -1 > MAX_INT),因此它没有有效的返回值。
1赞 Sanjay Manohar 9/25/2009
当然,这只是返回 f(n)=-n ?那么 f(f(n)) = n,而不是问题所问的 -n
19赞 4 revsFMc #67

这个 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

评论

0赞 Albert Renshaw 7/13/2014
但它不会保持 int。您实际上是在将全局变量数据存储在 int “n” 本身中......除非它不是 int,否则你不能这样做。例如,如果是一个字符串,我可以使 548 变为“First_Time_548”,然后下次它通过函数运行时......if (prefix == First_Time_“) 将”First_Time_“替换为”-”n
0赞 FMc 7/13/2014
@AlbertRenshaw 不知道你从哪里得到这些想法。(1)这里绝对不涉及全局变量。(2)如果你给函数一个 int,你会得到一个 int ——或者如果你调用函数奇数次,你会得到一个对 int 的引用。(3)也许最根本的是,这就是Perl。出于所有实际目的,int 和 strings 是完全可以互换的。在大多数情况下,看起来像数字的字符串将像数字一样完美地发挥作用,并且每当被问到时,数字都会愉快地串起来。
0赞 Albert Renshaw 7/14/2014
对不起,我不太了解perl,看来你使用的是全局数组哈哈
18赞 Dial Z #68

从来没有人说过f(x)必须是同一类型。

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


f(2) => [2]
f(f(2)) => -2
3赞 2 revsBill K #69

虽然问题说 n 必须是 32 位的 int,但它并没有说参数或返回类型必须是 32 位的 int。这应该在 java 中编译——在 c 中,你可以去掉 != 0

private final long MAGIC_BIT=1<<38;
long f(long n) {
    return n & MAGIC_BIT != 0 ? -(n & !MAGIC_BIT) : n | MAGIC_BIT;
}

编辑:

这实际上是一个非常好的面试问题。最好的问题是那些难以或不可能回答的问题,因为它迫使人们仔细思考,你可以观察和寻找:

  • 他们只是放弃了吗?
  • 他们说这很愚蠢吗?
  • 他们是否尝试独特的方法?
  • 他们在解决问题时是否与您沟通?
  • 他们是否要求进一步完善要求?

等。

永远不要只回答行为问题,除非你有一个很好的答案。始终保持愉快,并尝试让提问者参与进来。不要沮丧,不要过早放弃!如果你真的一无所获,尝试一些完全非法的可能工作,你会得到几乎全部的信用。

1赞 RCIX #70

这个怎么样:

do
    local function makeFunc()
        local var
        return function(x)
            if x == true then
                return -var
            else
                var = x
                return true
            end
        end

    end
    f = makeFunc()
end
print(f(f(20000)))
16赞 3 revs, 3 users 95%Accipitridae #71

这是一个受复数不能用于解决此问题的要求或声明启发的解决方案。

乘以 -1 的平方根是一个想法,它似乎只是失败了,因为 -1 在整数上没有平方根。但是,使用像 mathematica 这样的程序可以给出等式

(18494364652+1) mod (232-3) = 0.

这几乎与平方根 -1 一样好。函数的结果必须是有符号整数。因此,我将使用修改后的模运算 mods(x,n),该运算返回整数 y 与最接近 0 的 x 模 n 一致。只有极少数编程语言具有 suc 模运算,但它可以很容易地定义。例如,在 python 中它是:

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)

这满足 31 31 范围内的所有整数。的结果也在这个范围内,但计算当然需要 64 位整数。f(f(x)) = -x[-2-2, 2-2]f(x)

1赞 Peter #72
f(n) { return IsWholeNumber(n)? 1/n : -1/n }

评论

0赞 user unknown 5/30/2011
1/1是整数吗?-1?从我这里给 -1。:)
1赞 Scott Langham #73

C++

struct Value
{
  int value;
  Value(int v) : value(v) {}
  operator int () { return -value; }
};


Value f(Value input)
{
  return input;
}
1赞 2 revs, 2 users 96%Markus #74

与函数重载解决方案类似,在 python 中:

def f(number):
 if type(number) != type([]):
  return [].append(number)
 else:
  return -1*number[0]

另类:static datamembers

1赞 recursive #75

蟒蛇 2.6:

f = lambda n: (n % 2 * n or -n) + (n > 0) - (n < 0)

我意识到这对讨论没有任何帮助,但我无法抗拒。

5赞 Alexandru #76

我还有另一个解决方案,可以在一半的时间内工作:

def f(x):
    if random.randrange(0, 2):
        return -x
    return x
3赞 asmaier #77

这也是一个解决方案(但我们正在稍微改变规则):

def f(n):
    if isinstance(n,int):
        return str(n)
    else:
        return -int(n)
1赞 phkahler #78
int f(int n)
{
   static int x = 0;
   result = -x;
   x = n;
   return result;
}

这是一个带否定的单条目 FIFO。当然,它不适用于最大负数。

评论

0赞 user unknown 5/30/2011
它不适用于线程,但是嘿,谁需要线程?:)
1赞 John La Rooy #79

在 Python 中

f=lambda n:n[0]if type(n)is list else[-n]
0赞 jcoder #80

我相信这符合所有要求。没有说参数必须是 32 位有符号整数,只是说您传入的值“n”是。

long long f(long long n)
{
    int high_int = n >> 32;
    int low_int  = n & 0xFFFFFFFF;

    if (high_int == 0) {
        return 0x100000000LL + low_int;
    } else {
        return -low_int;
    }
}
1赞 Cam #81

好问题!

我花了大约 35 秒的时间来思考和写作:

int f(int n){
    static int originalN=0;
    if (n!=0)
        originalN=n;
    return n-originalN;
}

评论

0赞 Tim Leaf 5/6/2010
绝对是作弊......但绝对适用于 的所有值,并且很简单。x) 起初我以为它只会在您第一次调用时起作用(因为静态初始化),但它实际上也适用于每次连续调用。+1nf(f(n))
0赞 Cam 5/9/2010
谢谢:)从本质上讲,这个答案的想法是,这是对一个同样厚颜无耻的问题的厚颜无耻的回答。真的应该根据这样的问题雇用(或不雇用)计算机科学家/软件工程师吗?
1赞 2 revsmissingfaktor #82

斯卡拉:

def f(x: Any): Any = x match {
  case i: Int => new { override def hashCode = -i }
  case i @ _  => i.hashCode
}

在Java中也是如此:

public static Object f(final Object x) {
  if(x instanceof Integer) {
    return new Object() {
      @Override 
      public int hashCode() {
        return -(Integer)x;
      }
    };
  }
  return x.hashCode();
}
0赞 2 revsDinah #83

C# 重载:

string f(int i) {
  return i.ToString();
}

int f(string s) {
  return Int32.Parse(s) * -1;
}

object f(object o) {
  if (o.ToString.StartsWith("s"))
    return Int32.Parse(s.Substring(1)) * -1;
  return "s" + i.ToString();
}
1赞 2 revsAnurag #84

另一个利用短路的 Javascript 解决方案。

​function f(n) {return n.inv || {inv:-n}}

f(f(1)) => -1
f(f(-1)) => 1
0赞 RHSeeger #85

Tcl:

proc f {input} {
    if { [string is integer $input] } {
      return [list expr [list 0 - $input]]
    } else {
      return [eval $input]
    }
}

% f [f 1]
-1

按照其他一些答案的思路......如果是整数,则返回一个返回该数字负数的命令。如果它不是数字,请计算它并返回结果。

-2赞 Armstrongest #86

少于 50 个字符 (C#)

int f(int n) { return (n <= 0) ? n : f(-n); }

或更易于阅读:

static int f(int n) { 
  if (n <= 0)
    return n;
  else 
    return f(-n);
}

测试

static void Main(string[] args) {
    for (int n = int.MinValue; n < int.MaxValue; n+=1) {
        Console.Out.WriteLine("Value: " + n + " Result: " + f(f(n)));
    }
}

它有效(假设我正确理解了这个问题)

评论

0赞 Tim Leaf 5/6/2010
这仅在最初为正(或零)时才有效。这个问题清楚地表明,这意味着 n 也可以以负数开始。nWhere n is a 32 bit *signed integer*
0赞 Tim Leaf #87

这是一个 C/C++ 解决方案,它不使用任何按位运算符,也不需要任何数学库,尽管它有点作弊......

double f(double n)
{
    if (n == (double)(int)n)
        return n + 0.5;
    else
        return -(n - 0.5);
}

这适用于所有 32 位整数,但只有一个例外(因为它的反面不能存储在 32 位整数系统中)。 将始终是,除非在那种情况下。0x80000000f(f(n)) == -ntrue

不过,我相信有一种更简单、更快速的实现方法。这只是我想到的第一件事。

1赞 2 revs, 2 users 69%anthony-arnold #88

我以为我会在不先看其他人的答案的情况下尝试这个:

#include <stdio.h>
#include <limits.h>
#include <stdlib.h>

int f(int n) {
    if(n > 0) {  
        if(n % 2)
            return -(++n);
        else {
            return (--n);

        }
    }
    else {
        if(n % 2)
            return -(--n);
        else {
            return (++n);

        }
    }
}

int main(int argc, char* argv[]) {
    int n;
    for(n = INT_MIN; n < INT_MAX; n++) {
        int N = f(f(n));

        if(N != -n) {
            fprintf(stderr, "FAIL! %i != %i\n", N, -n);
        }
    }
    n = INT_MAX;
    int N = f(f(n));
    if(N != -n) {
        fprintf(stderr, "FAIL! n = %i\n", n);
    }
    return 0;
}

输出:[nothing]

2赞 RCIX #89

Lua:

function f(n)
    if type(n) == "number" then
        return (-number) .. ""
    else
        return number + 0
    end
end
27赞 Anurag #90

利用 JavaScript 异常。

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

f(f(0)) => 0

f(f(1)) => -1

评论

0赞 NoBugs 4/6/2013
我怀疑以前有过这样的例外...... :)
0赞 6/25/2013
+1 开箱即用的思维。凉!但是在生产代码中,为了安全起见,我会使用 typeof。
68赞 4 revs, 2 users 97%Rodrick Chapman #91

除 int 外有效。MaxValue 和 int。最小值

    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));
    }

pictorial

评论

0赞 Rodrick Chapman 6/27/2010
不知道为什么这被否决了。它失败了什么输入?
0赞 comonad 7/9/2011
为什么不使用 signum 函数?!?
1赞 Jeppe Stig Nielsen 6/29/2013
图像真的很好。Send to 和 to,因为这两个数字是否定运算符的固定点,因此 .对于其余数字,请按照上图中的箭头进行操作。从SurDin的回答及其评论中可以清楚地看出,在这种情况下,将有两个数字,并且没有其他数字可以交换。00-2147483648-2147483648x => -x2147483647-2147483647
0赞 Anshul 3/14/2014
它看起来像一个笑脸 - 有很多皱纹
0赞 2 revsArtur #92
int func(int a)  
{   
    static int p = 0;  
    int ret = a;  

    if ( p ) ret *= -1;  
    p ^= 1;  

    return ret;  
}  
3赞 2 revsElian Ebbing #93

我认为这类问题的答案最好通过使用图表直观地解释。当我们忽略零时,我们可以将整数分成 4 个数字的小集合:

 1  → 2    3  → 4    5  → 6
 ↑    ↓    ↑    ↓    ↑    ↓   ...
-2 ← -1   -4 ← -3   -6 ← -5

这很容易转换为代码。请注意,偶数会改变符号,奇数会增加或减少 1。在 C# 中,它看起来像这样:

public static int f(int x)
{
    if(x == 0)
        return 0;

    if(x > 0)
        return (x % 2 == 0) ? -x+1 : x+1;

    // we know x is negative at this point
    return (x % 2 == 0) ? -x-1 : x-1;
}

当然,你可以通过使用巧妙的技巧来缩短这种方法,但我认为这段代码最好地解释了自己。

然后关于范围。32 位整数的范围从 -2^31 到 2^31-1。数字 2^31-1、-2^31-1 和 -2^31 超出了 f(x) 的范围,因为缺少数字 2^31。

0赞 2 revs, 2 users 98%Eudis Duran #94

我不知道这是否完全正确,但一个简单的标志难道不起作用吗?在 C 语言中,使用静态局部变量,我成功地做到了这一点:

int main()
{
    int n = -256; // 32-bit signed integer
    printf("%d", f(f(n)));
}

int f(int n){
    static int x = 0; // not returning negative;
    switch(x){
        case 0:
            x = 1;
            return n;
            break;

        case 1:
            x = 0;
            return -n;
            break;
        default:
            return -999;
            break;
    }
}

评论

0赞 Rohan Monga 12/7/2010
我认为这行不通。f(3) 返回 3,然后 f(4) 返回 -4
3赞 user534212 #95
  1. 将 n 转换为符号和大小表示;
  2. 添加 1/4 的范围;
  3. 转换回来。

    #define STYPE int
    STYPE sign_bit = (unsigned STYPE) 1 << ( sizeof ( STYPE ) * 8  - 1 );
    STYPE f ( STYPE f )
    {
        unsigned STYPE smf = f > 0 ? f : -f | sign_bit;
        smf += sign_bit >> 1;
        return smf & sign_bit ? -( smf & ~sign_bit ) : smf;
    }
0赞 2 revs, 2 users 63%lemontea #96
#include <cmath>

int f(int n)
{
    static int count = 0;
    return ::cos(M_PI * count++) * n;
}

评论

3赞 Tommy Andersen 12/21/2010
应该注意的是,这可能会在多线程环境中产生问题。
6赞 3 revscomonad #97

这很简单!

每个数字以 4 个周期映射到另一个数字,其中所需的条件成立。

例:

规则是:

  • 0 → 0

  • ±2³¹ → ±2³¹

  • 奇数→偶数,偶数→ -奇数:

    对于所有 K, 0 < K < 2³⁰: (2K-1) → (2K) → (-2K+1) → (-2K) → (2K-1)

唯一不匹配的值是 ±(2³¹-1),因为只有两个。必须有两个不能匹配,因为在二的补码系统中只有 4 的倍数,其中 0 和 ±2³¹ 已经保留。

在 1 的补码系统中,存在 +0 和 -0。我们开始了:

对于所有 K, 0 < K < 2³⁰: (+2K) → (+2K+1) → (-2K) → (-2K-1) → (+2K)

0赞 user240515 #98

这个想法已经在其他答案中使用过,但我把它写进了 Python 的一行中:

def f(n):
    return str(n) if type(n) == int else -int(n)
1赞 9 revsAfshin #99

A C 函数:

int f(int n) /* Treats numbers in the range 0XC0000000 to 0X3FFFFFFF as valid to
                generate f(f(x)) equal to -x. If n is within this range, it will
                project n outside the range. If n is outside the range, it will
                return the opposite of the number whose image is n. */
{
    return n ? n > 0 ? n <= 0X3FFFFFFF ? 0X3FFFFFFF + n : 0X3FFFFFFF - n :\
           n >= 0XC0000000 ? 0XC0000000 + n : 0XC0000000 - n : 0;
}

用于测试和下载的 Ideone 链接

1赞 3 revsddriver #100

好吧,我既不是数学家,也不是编程专家,但这不是很容易吗?

int f(int i) {
    static bool b;
    if (b) {
        b = !b;
        return i;
    } else {
        b = !b;
        return -i;
    }
}

用大大小小的正负值、INT_MIN、INT_MAX进行测试,它似乎有效......如果这是一个问题,可以使线程安全,但它不是任务的一部分。

或者也许我错过了什么?

评论

0赞 kybernetikos 6/25/2013
我不认为这是一个可以接受的答案。例如,想象一下:var x = f(1), y = f(2); console.log(f(x));
0赞 dtech 6/25/2013
@kybernetikos:条件是 - 注意调用的格式,使得无法实现你的方案所实现的目标。如果你非常关心像你一样调用它 - 只需创建一个包装器方法来为你执行嵌套调用......f(f(n)) == -n
0赞 2 revs, 2 users 75%olodnad #101

F# 中的简单解决方案(不使用“技巧”)

let rec f n =
    if n = 0 then 0
    elif n > 0 then
        if (f (n - 1) <> n) then n + 1
        else -(n - 1)
    else
        if (f (-(n - 1)) = n) then n - 1
        else -(n + 1) 
0赞 Vishwanath Dalvi #102

SQL Server 中的解决方案

create function dbo.fn_fo(@num int) -- OUTER FUNCTION
RETURNS int
AS
begin
RETURN @num * -1
end
GO

create function dbo.fn_fi(@num int) -- INNER FUNCTION
RETURNS int
AS
begin
RETURN @num * -1
end
GO

declare @num AS int = -42
SELECT dbo.fn_fo(dbo.fn_fi(@num)) -- Gives (-42)
2赞 Ricardo Tomasi #103

用 coffeescript 打高尔夫球:

f = (n)-> -n[0] or [n]

评论

0赞 Ricardo Tomasi 6/25/2013
(编译为function f(n) { return -n[0] || [n] })
-1赞 4 revsLatyas #104

使用循环排列方法来执行此操作。

-b a b -a

一、乙、一、-乙

在微不足道的情况下 f(0) 返回 0

对不起,我用手机粗略地回答了,28 日之后我将发布完整版(现在正在检查...... 简单地说,认为f(n)是一个循环排列,问题是如何构造它。

定义 fk = f(f(f(f(...f(n))))) (k fs) 情况 k=2 0.琐碎情况 f(0) 返回 0 1.在情况k=2的情况下,使组: {0} {1,2} {3,4} ...{n,n+1 |(n+1)%2 = 0 } ,注意:我只使用Z+,因为构造不需要使用负数。 2.构造排列: 如果 n % 2 = 0,则 a=n-1 b=n 如果 n % 2 = 1,则 a=n b=n+1

这将产生相同的排列,因为 n 和 f(n) 位于同一组中。

注意排列为 P 返回 P(n)

对于 k=2t ,只做上面相同的事情,只是 MOD k。 对于 k=2t-1,虽然方法有效,但毫无意义,啊?(f(n) = -n 就可以了)

3赞 4 revsStefan Haustein #105

使用问题中给出的信息,您可以

  1. 从 2-补码转换为符号位表示
  2. 如果设置了最后一个位,则翻转符号位和最后一个位;否则,只翻转最后一点
  3. 转换回 2-补码。

所以你基本上是奇数 -> 偶数 -> 奇数或偶数 -> 奇数 -> 偶数,并且只更改偶数的符号。唯一不起作用的数字是 -2^31

法典:

function f(x) {
  var neg = x < 0;
  x = Math.abs(x) ^ 1;
  if (x & 1) {
    neg = !neg;
  }
  return neg ? -x : x;
}
0赞 3 revsDarknight #106

也许我错过了什么?

这难道不是这么简单吗?

    function f(n)
    {
        if(n ==0 || n < 0){return n;}
        return n * -1;
    }

编辑:

所以我错过了这个问题,哼哼,所以:

    function f(n)
    {
        if(!c(n,"z")&&!c(n,"n")){if(n==0){return "z"+n;}return "n"+n;}
        if( c(n,"z")){return 0;}return parseInt(n.replace("n",""))*-1;
    }
    function c(x,y){return x.indexOf(y) !==-1;}

丑陋但有效。

评论

0赞 Darknight 6/25/2013
-1 的答案应该是什么?
1赞 Kane 6/25/2013
问题不在于f(n),而在于f(f(n))。f(f(-1)) 应返回 1,您的解决方案将返回 -1
0赞 Darknight 6/25/2013
这取决于你如何解释这个问题?
0赞 Ingo 6/25/2013
不,它没有。您的函数计算 f(f(-1)) = -1,但需要 1。
0赞 Ingo 6/25/2013
需求为 f(f(n)) = -n。现在代入 n=-1。因此 f(f(-1)) 应该是 -(-1),即 1。那里没有什么可解释的。
-1赞 wobmene #107

它通过保存状态来作弊,但它有效,将操作一分为二:-n = (~n + 1) 表示整数

int f(int n) {
    static int a = 1;
    a = !a;
    if (a) {
        return (~n);
    } else {
        return (n+1);
    }
}
0赞 David Stein #108

f(x) = 在二维笛卡尔坐标系中围绕原点逆时针旋转 90 度的点 (x)。假定只有一个数字 x 的输入为 (x, 0),而具有 y=0 的输出作为单个数字 x 提供。

object f: (object) x {
    if (x.length == 1)
        x = (x, 0)
    swap = x[0]
    x[1] = x[0]
    x[0] = -swap
    if (x[1] == 0)
        x = x[0]
    return x

评论

0赞 The Mask 6/26/2013
它是什么编程语言?
0赞 Zecc 6/26/2013
“其中 n 是一个 32 位有符号整数;你不能使用复数算术。
0赞 David Stein 6/26/2013
@Mask:这是伪代码。可以很容易地翻译成任何语言。
0赞 David Stein 6/26/2013
@Zecc:我不认为你理解“复数算术”的概念。我发布的代码没有使用数学意义上的复杂数学,也没有使用口语意义上的复杂数学:它很容易理解和应用。
0赞 Zecc 6/26/2013
@DavidStein 是的,我知道。我引用太多,评论太少,我为此道歉。我的意思是它应该是一个 32 位有符号整数。现在我意识到这就是为什么你开始测试.伪代码把我甩了出去。xx.length == 1
5赞 3 revsrein #109

简单的 Python 解决方案之所以成为可能,是因为对 f(x) 应该输出的内容没有限制,只有 f(f(x)):

def f(x):
    return (isinstance(x, tuple) and -x[0]) or (x,)

评论

0赞 kreativitea 6/26/2013
当我第一次听到这个问题时,我立即想到了这一点。
1赞 seppo0010 6/26/2013
(x)返回。我想你的意思是x(x,)
0赞 user229044 2/11/2016
最后。我简直不敢相信有多少“作弊”解决方案依赖于某种全局状态,即使对于状态可以“存储”在函数本身的返回值中的语言也是如此。
0赞 Hameer Abbasi #110

我承认我会作弊,但仍然符合要求。这是编程魔法,而不是真正的数学。它适用于整个范围,除了 -2^31。

int f(int n)
{
    static bool eFlag = false; // Only executed once
    eFlag = !eFlag;
    return eFlag?-n:n;
}
0赞 skies457 #111

C++ 中的另一个作弊解决方案,运算符重载。

struct func {
    int n;
    func operator()(int k) { n = -k; return *this; }
    int operator()(const func &inst) { return inst.n; }
} f;
2赞 Tyler #112

下面是一个简短的 Python 答案

def f(n):
  m = -n if n % 2 == 0 else n
  return m + sign(n)

一般案例

对上述内容稍作调整可以处理我们希望自调用否定输入的情况——例如,如果 ,这意味着:kk = 3g(g(g(n))) = -n

def g(n):
  if n % k: return n + sign(n)
  return -n + (k - 1) * sign(n)

其工作原理是将 0 保留在原位并创建长度为 2 * k 的循环,因此,在任何循环中,n 和 -n 的距离为 k。具体来说,每个周期如下所示:

N * k + 1, N * k + 2, ... , N * k + (k - 1), - N * k - 1, ... , - N * k - (k - 1)

或者,为了更容易理解,以下是以下示例循环:k = 3

1, 2, 3, -1, -2, -3
4, 5, 6, -4, -5, -6

这组循环最大化了在以零为中心的任何机器类型(例如有符号 int32 或有符号 int64 类型)中工作的输入范围。

兼容范围分析

映射实际上必须形成长度的循环,其中是一个特例 1-长度循环,因为 -0 = 0。因此,当且仅当输入的范围 - 1(补偿 0)是 2 * k 的倍数,并且正负范围相反时,一般问题是可解的。x -> f(x)2 * kx = 0k

对于有符号整数表示,我们总是有一个最小的负数,在范围内没有正对应数,因此问题在整个范围内变得无法解决。例如,a 的范围为 [-128, 127],因此不可能在给定范围内。signed charf(f(-128)) = 128

0赞 Albert Renshaw #113

Objective-C语言

这适用于除“-1”以外的所有数字。

如果您要从使用到使用,那么您可以将 -1 值设置为,然后第二次将它们转换为 +1,但我觉得这欺骗了提问者的意图。intNSIntNULLNSInt


f(n):

-(int)f:(int)n {
    if (abs(n)==1) {
        n = -1;
    } else {
        if (abs(n)%2) {//o
            if (n>0) {//+
                n--;
                n*=+1;
            } else if (n<0) {//-
                n++;
                n*=+1;
            }
        } else {//e
            if (n>0) {//+
                n++;
                n*=-1;
            } else if (n<0) {//-
                n--;
                n*=-1;
            }
        }
    }
    return n;
}

当然,这一切都可以缩短为一行,但其他人可能无法继续阅读......


无论如何,我将布尔逻辑存储在数字为奇数或偶数的状态下。

0赞 Tajomaru #114

F#

let f n =
    match n with
    | n when n % 2 = 0 -> -n + System.Math.Sign n
    | _ -> n - System.Math.Sign -n

这样.nSystem.Int32.MinValue < n < System.Int32.MaxValue

0赞 6 revsMartijn Courteaux #115

我试着用罗德里克·查普曼(Rodrick Chapman)的这个答案打高尔夫球。

无分支:74 个字符

int f(int i){return(-((i&1)<<1)|1)*i-(-((i>>>31)<<1)|1)*(((i|-i)>>31)&1);}

带分支,Java 样式:58 个字符

int f(int i){return i==0?0:(((i&1)==0?i:-i)+(i>0?-1:1));}

带分支,C 型:52 个字符

int f(int i){return i?(((i&1)?-i:i)+(i>0?-1:1)):0;}

经过快速但有效的基准测试后,分支版本在我的机器上速度提高了 33%。(正数和负数的随机数据集,足够的重复,并防止编译器优化代码,并进行预热。这并不奇怪,因为该函数被调用了两次,因此无分支版本中的操作数量和可能的良好分支预测: .当我更改基准以测量时: ,分支版本仅快 28%。我认为这证明了分支预测在第一种情况下实际上起到了一定的作用。更多证明:当测试时,事实证明分支版本快了 42%。f(f(i))f(i)f(f(f(f(i))))

0赞 sakra #116

Wolfram 语言中的解决方案:

f[f[n_]] := -n

应用:

In[2]:= f[f[10]]                                                                                                                                                                                                                                                                              
Out[2]= -10
In[3]:= f[10]                                                                                                                                                                                                                                                                                 
Out[3]= f[10]

因为问题没有说明f(n)的值,所以f[n]仍然未计算。

0赞 ssh #117

Javascript的

function f(n)  { 
        return typeof n === "number" ? 
        function() {return -n} : 
        n();
}
0赞 Alisa #118

根据Microsoft/Google的面试官在面试中通常会问的问题,我相信提问者是指一种创新的、轻量级的、简单的解决方案,它将使用按位运算,而不是那些复杂的高级答案。

受到@eipipuz答案的启发,我编写了这个C++函数(但没有运行它):

int32_t f(int32_t n){
    int32_t temp = n & 00111111111111111111111111111111;
    x = n >> 30;
    x++;
    x = x << 30;
    return x | temp;
}

它将 n 的最左边两位存储在 x 中,将 1 加到 x,然后再次将其替换为 n 的最左边两位。

如果我们继续运行 f(n) 并使用另一个 f(n) 作为参数 n则最左边的两个位将像这样旋转:

00 --> 01 --> 10 --> 11 --> 00 ...

请注意,最右边的 30 位不会改变。8 位整数的示例:

示例 1:

  • > f(00001111) = 01001111
  • > f(01001111) = 10001111 [这是原始值的负数,00001111]

示例 2:

  • > f(11101010) = 00101010
  • > f(00101010) = 01101010 [这是原始值的负数,11101010]
0赞 RARE Kpop Manifesto #119

在 中,由于几乎没有任何信息被传递下来,因此必须求助于允许状态信息作为函数返回的一部分传递的方法,而不会危及传递内容的可用性:awk

jot - -5 5 | mawk 'function _(__,___) { 

     return (__~(___=" ")) \
      \
      ? substr("",sub("^[ ]?[+- ]*",\
        substr(" -",__~__,index("_"___,___)-\
              (__~"[-]")),__))\
            (__~"[-]"?"":___)__\
      : (+__<-__?___:(___)___)__ 

  } BEGIN { CONVFMT=OFMT="%.17g" 
  } { 
      print "orig",           +(__=$(__<__))<-__?__:" "__,
            "f(n)....",        _(__),_(_(__)),_(_(_(__))),
                         _(_(_(_(__)))), _(_(_(_(_(__))))) 

  }' |gcat -n | lgp3 5 

 1  orig -5 f(n)....  -5   5  -5   5  -5
 2  orig -4 f(n)....  -4   4  -4   4  -4
 3  orig -3 f(n)....  -3   3  -3   3  -3
 4  orig -2 f(n)....  -2   2  -2   2  -2
 5  orig -1 f(n)....  -1   1  -1   1  -1

 6  orig  0 f(n)....   0  -0   0  -0   0
 7  orig  1 f(n)....   1  -1   1  -1   1
 8  orig  2 f(n)....   2  -2   2  -2   2
 9  orig  3 f(n)....   3  -3   3  -3   3
10  orig  4 f(n)....   4  -4   4  -4   4

11  orig  5 f(n)....   5  -5   5  -5   5

因此,这样做的限制是只有已经采用字符串格式的整数或浮点值才能使用而不会有风险,因为额外的 ASCII 空格被作为状态信息预置\040

这种方法的好处是

  1. 它愿意为你提供“负零”,

  2. 对于小于绝对值的整数, 只需添加一个加号,2^53

    +f(f(_))

    对函数调用本身将具有隐式 代表您完成的类型转换, 结果值将再次为数字

    对于大整数,只需去掉任何前导空格substr()

  3. 轻松处理 Bigintegers,而不会有丢失的风险 从类型铸造到双精度浮子的精度

`

    1   orig -99999999999999999999999999999999 
        f(n).... 
             -99999999999999999999999999999999   
              99999999999999999999999999999999
             -99999999999999999999999999999999   
              99999999999999999999999999999999  
             -99999999999999999999999999999999

 2  orig      -1239999999999999999999999999999 
    f(n)....  -1239999999999999999999999999999                   
               1239999999999999999999999999999
              -1239999999999999999999999999999
               1239999999999999999999999999999
              -1239999999999999999999999999999`
0赞 2 revsPhil #120

我来晚了,现在可能是一个墓地。但我有 2 个贡献,灵感来自 viraptor 之前使用 lambda 的 Python 答案。读者可能会认为该解决方案仅在非类型化语言中可行,而在类型化语言中,它需要一些显式的额外标记。

但以下是 Haskell 中的解决方案 1(我不是 Haskell 专家)。它有点作弊,因为从技术上讲,两者是两种不同的实现。(一个 ,另一个ff :: Int -> () -> Intf :: (() -> Int) -> Int)

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-}

module Main where

class Tran σ τ | σ -> τ where
  tran :: σ -> τ

instance Tran Int (() -> Int) where
  tran n = \_ -> (-n)

instance Tran (() -> Int) Int where
  tran g = g ()

f :: Tran σ τ => σ -> τ
f = tran

main :: IO ()
main = do
  print $ f (f (42 :: Int)) -- --> -42
  print $ f (f (0 :: Int)) -- --> 0
  print $ f (f (-69 :: Int)) -- --> 69

接下来是 Typed Racket 中的解决方案 2。这个满足最大域的属性,因为在 Racket 中最多包含复数:Number

#lang typed/racket

(: f (case->
      [Number -> (-> Number)]
      [(-> Number) -> Number]))
(define (f x)
  (if (number? x) (λ () (- x)) (x)))

(f (f 42))    ; --> -42
(f (f 0))     ; --> 0
(f (f -69))   ; --> 69
(f (f 3/4))   ; --> -3/4
(f (f 8+7i))  ; --> -8-7i