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

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

设计一个函数f,这样:

f(f(n)) == -n

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

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

有什么想法吗?

84056 次浏览

对于所有负数都是如此。

f(n) = abs(n)

因为对于两个补整数,负数比正数多1个,所以f(n) = abs(n)f(n) = n > 0 ? -n : n的解决方案多1个情况有效,这与f(n) = -abs(n)相同。让你得到一个…: D

更新

不,它不适用于一个案例,因为我刚刚通过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 : -n并将计算包含在unchecked块中。然后您可以像未经检查的否定一样将Int.Min映射到自身。

更新

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

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

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

问题要求f(f(n)) = -n,即f的两个连续应用否定了该论点。f的两个进一步应用-总共四个-再次否定该论点,再次产生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                 => negate and subtract one-n - 1 = -(n + 1)  => add one-n                 => negate and add onen + 1             => subtract onen

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

但是我们有一个零的问题。循环必须包含0 => x => 0,因为零对自身是负的。而且因为循环已经声明了0 => x,它跟随0 => x => 0 => x。这只是一个长度为2的循环,x在两次应用后变成了自己,而不是-x。幸运的是,有一种情况解决了这个问题。如果X等于零,我们得到一个长度为1的循环,只包含零,我们解决了这个问题,得出结论,零是f的一个固定点。

完成了吗?差不多了。我们有2^32个数字,零是留下2^32 - 1个数字的固定点,我们必须将该数字划分为四个数字的循环。糟糕的是2^32 - 1不是四的倍数-在任何长度为四的循环中都会有三个数字。

我将使用从-4+3的较小的3位有符号整数集来解释解决方案的其余部分。我们完成了零。我们有一个完整的循环+1 => -2 => -1 => +2 => +1。现在让我们从+3开始构建循环。

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

出现的问题是+4不能表示为3位整数。我们将通过否定-3+3来获得+4——这仍然是一个有效的3位整数——但然后将1加到+3(二进制011)产生100二进制。解释为无符号整数,它是+4,但我们必须将其解释为有符号整数-4。所以实际上,这个例子中的-4或一般情况下的+40是整数算术否定的第二个固定点——+41和+40映射到它们自己。所以循环实际上如下。

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

它是一个长度为2的循环,另外+3通过-4进入循环。结果-4在两次函数应用后正确映射到自身,+3在两次函数应用后正确映射到-3,但-3在两次函数应用后错误映射到自身。

所以我们构造了一个对除一个以外的所有整数都有效的函数。我们能做得更好吗?不,我们不能。为什么?我们必须构造长度为4的循环,并且能够覆盖多达四个值的整个整数范围。剩下的值是两个固定点0Int.MinValue,它们必须映射到它们自己,以及两个任意整数x-x,它们必须由两个函数应用程序相互映射。

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

根据您的平台,某些语言允许您在函数中保留状态。VB. Net,例如:

Function f(ByVal n As Integer) As IntegerStatic flag As Integer = -1flag *= -1
Return n * flagEnd Function

IIRC,C++允许这样做。我怀疑他们正在寻找一个不同的解决方案。

另一个想法是,由于它们没有定义第一次调用函数的结果,您可以使用奇数/偶数来控制是否反转符号:

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

给所有偶数的大小加1,从所有奇数的大小中减去1。两次调用的结果具有相同的大小,但是我们交换符号的一次调用是偶数。在某些情况下,这不起作用(-1、max或min int),但它比迄今为止建议的任何其他方法都要好得多。

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

适用于n=[0…2^31-1]

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

不如:

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

In Python:

def f(n):if n == 0: return 0if n >= 0:if n % 2 == 1:return n + 1else:return -1 * (n - 1)else:if n % 2 == 1:return n - 1else: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));elsereturn ((long)Math.Floor(n) % 2 == 0) ? (n - 1) : (-1 * (n + 1));}

这个问题没有说明函数#0的输入类型和返回值必须是什么(至少不是你呈现的方式)

…只是当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为true。

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

在MIN_INT不会失败:

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

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

你没有说他们想要什么样的语言……这是一个静态解决方案(Haskell)。它基本上弄乱了两个最重要的部分:

f :: Int -> Intf 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()

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

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

给予

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

或者,您可以在强类型语言中使用重载,尽管这可能会违反规则

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

对于所有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 => 01 => 6 => -1 => -6 => 12 => 5 => -2 => -5 => 23 => 4 => -3 => -4 => 3

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

ruby:

def f( n )return 0 unless n != 0( n == n.to_i ) ? 1.0 / n : -(n**-1).to_iend

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

    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,这意味着在大多数情况下这是第一次调用。如果最后一位与倒数第二位不同,那么我假设我们处于第二次调用,只需重新反转最后一位。显然,这对使用最后两位的非常大的数字不起作用。但是,再一次,它适用于非常广泛的数字。

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 + 1for (int i = -3; i <= 3  ; i++)Console.WriteLine(f(f(i)));Console.WriteLine(f(f(Int32.MaxValue))); // 2147483647

印刷品:

21474836473210-1-2-3-2147483647

: D

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

本质上,该函数必须将可用范围划分为大小为4的循环,其中-n位于n循环的另一端。然而,0必须是大小为1的循环的一部分,因为否则0->x->0->x != -x。因为0是单独的,所以我们的范围内必须有3个其他值(其大小是4的倍数)不在具有4个元素的正确循环中。

我选择这些额外的奇怪值为MIN_INTMAX_INTMIN_INT+1。此外,MIN_INT+1将正确映射到MAX_INT,但会卡在那里而不会映射回来。我认为这是最好的妥协,因为它只有极值不能正常工作的好属性。此外,这意味着它适用于所有 BigInts。

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

Scala中使用隐式转换的奇怪且仅略微聪明的解决方案:

sealed trait IntWrapper {val n: Int}
case class First(n: Int) extends IntWrappercase class Second(n: Int) extends IntWrappercase 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)}

但我不认为这是一个正确的想法。

在php

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

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

这个解决方案的巧妙之处在于,无论您以字符串还是整数开头,它都有效,并且在返回f(n)时不会明显更改任何内容。

在我看来,面试官是在问,“这位候选人是否知道如何标记数据以供以后操作”,以及“这位候选人是否知道如何标记数据,同时最少地改变它?”你可以用双精度,字符串或任何其他你喜欢的数据类型来做到这一点。

看起来很容易。

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

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

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

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

使用全局变量……但是呢?

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

怎么样

int f(int n){return -abs(n);}
return x ^ ((x%2) ? 1 : -INT_MAX);

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

  • n乘以i,得到n*i,逆时针旋转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文本

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

我实际上并没有试图给出问题本身的解决方案,但确实有一些评论,因为问题指出这个问题是(工作?)面试的一部分:

  • 我会先问“为什么要做这个功能?这个功能最大的问题是什么?”而不是当场回答这个实际提出的问题。这会展示出我是怎么思考的,我是怎么处理这类问题的。谁知道呢?这甚至可能是面试中被问到这个问题的实际原因。如果答案是“没关系,假设你需要这个功能,给我看看你会如何设计这个功能。”我会继续这样问。
  • 然后,我会编写我将使用的C#测试用例代码(显而易见的:从int.MinValueint.MaxValue的循环,对于该范围内的每个n调用f(f(n))并检查结果为-n),然后告诉我将使用测试驱动开发来实现这样的函数。
  • 只有当面试官继续要求我解决提出的问题时,我才会在面试过程中开始尝试涂鸦伪代码,试图得到某种答案。然而,如果面试官能说明公司的情况,我真的不认为我会跳槽接受这份工作…

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

也许作弊?(python)

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

容易:

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

Clojure解决方案:

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

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

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

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的情况,但是除非允许返回更宽的类型,否则对此几乎无能为力。

在C中,

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

这将有助于了解这是什么语言。我错过了什么吗?许多“解决方案”似乎过于复杂,坦率地说,没有工作(当我阅读问题时)。

我认为最大范围暗示了一个模算术解。在一些模基中,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=1849436465if abs( x ) >halfm:raise "too big"if x<0:x+=mx=(i_mod_m*x) % mif (x>halfm):x-=mreturn x;

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

这是ross制造答案的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就被涵盖了。当然,开关语句中使用的文字必须更改。

这个怎么样(C语言):

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

刚试过了,然后

f(f(1000))

返回-1000

f(f(-1000))

返回1000

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

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

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

这里我允许我的Python函数对要么元组整数进行操作并返回。任务规范承认这一点,它只指定该函数的两个应用程序应该返回一个等于原始对象的对象,如果它是int32。(我会要求有关规范的更多详细信息。)

这允许我的轨道很好和对称,并覆盖所有输入整数(除了minint)。我最初设想循环访问半整数值,但我不想被舍入误差所困扰。因此使用元组表示。这是一种将复杂旋转作为元组潜入的方法,而不使用复杂的算术机制。

请注意,在调用之间不需要保留状态,但调用者确实需要允许返回值是元组或int。

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 ValueErrorelse :# send this integer to a tuple (representing ix)return( (0,x) )else :# not an int or a tupleraise 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)>>> x37

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

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

我们处理一个问题没有解决方案的情况(在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 fraise ValueErrorValueError

如果你认为这个函数通过模仿乘以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中看不到这样的解决方案。

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

如果我没记错的话,您只需翻转第一位即可否定一个有符号的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的零和一序列,并返回一个长度相同的零和一序列,其属性是取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的平方根相乘。

如果我只是单独提出伪代码,而没有冗长的前奏,这看起来就像是从帽子里出来的兔子,我想解释我是如何得到解决方案的。

int f(int x){if (x < 0)return x;return ~x+1; //two's complement}

这个在Python中。适用于n的所有负值:

f = abs

这是一个我没见过有人使用的变体。由于这是ruby,32位整数的东西有点过时了(当然可以添加检查)。

def f(n)case nwhen Integerproc { n * -1 }when Procn.callelseraise "Invalid input #{n.class} #{n.inspect}"endend
(-10..10).each { |num|puts "#{num}: #{f(f(num))}"}

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

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

此外,对于任何xy,假设f(x) = y。然后我们想要f(y) = -x。和f(f(y)) = -y => f(-x) = -y。总结一下:如果f(x) = y,然后f(-x) = -yf(y) = -xf(-y) = x

因此,我们需要将除0之外的所有整数划分为4的集合,但我们有奇数个这样的整数;不仅如此,如果我们删除没有正对应的整数,我们仍然有2(mod4)个数字。

如果我们删除剩下的2个最大数字(由abs值),我们可以得到函数:

int sign(int n){if(n>0)return 1;elsereturn -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个数字作为奖励。(但这只是一个愚蠢的如果。)

创建许多解的一种方法是注意,如果我们将整数划分为两个集合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。

简单,只需让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);}}

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

如果你使用ONEne-补码,那么所有2^32的值都发生在长度为4的循环中——你不需要零的特殊情况,你也不需要条件。

在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)
const unsigned long Magic = 0x8000000;
unsigned long f(unsigned long n){if(n > Magic ){return Magic - n;}
return n + Magic;}

0~2^31

int j = 0;
void int f(int n){j++;
if(j==2){j = 0;return -n;}
return n;}

: D

以下是什么:

int f (int n){static bool pass = false;pass = !pass;return pass? n : -n;}
int f( int n ){return n==0?0:(n&1?n:-n)+(n<0?-1:1);}

我的答案是正确的……50%的时间,所有的时间

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

这将适用于范围-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的额外“位”信息的方法。

我要你改变两个最重要的位。

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

正如你所看到的,这只是一个添加,省略了携带位。

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

PHP,不使用全局变量:

function f($num) {static $mem;
$answer = $num-$mem;
if ($mem == 0) {$mem = $num*2;} else {$mem = 0;}
return $answer;}

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

刚意识到这做了一些不必要的工作,但是,不管怎样

void f(int x){Console.WriteLine(string.Format("f(f({0})) == -{0}",x));}

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

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

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

Ruby示例:

class Cheatdef ==(n)trueendend
def f(n)Cheat.newend

这给了我们:

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

但也(不太令人惊讶)

>> f(f(1)) == "hello world"=> true
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;}}

哈克什,但正确。

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

int f (int n){//if countstatic 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))}

有些是相似的,但我只是想写下我的第一个想法(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))实际调用两个不同的函数

JavaScript单行代码:

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

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

我想我会想出一些邪恶的东西在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 constreturn ImplicitlyConvertibleToInt();}

整个ImplicitlyConvertibleToInt类型和重载是必要的,因为临时对象不能绑定到非const引用。

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

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

struct ComparesTrueToInt{ComparesTrueToInt(int) { } // implicit construction from int};bool operator == (ComparesTrueToInt, int) const { return true; }
ComparesTrueToInt f(ComparesTrueToInt ct) { return ComparesTrueToInt(); }
int f(int n){static long counter=0;counter++;if(counter%2==0)return -n;elsereturn n;}

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

int ffn(int n)

    n = n ^ (1 << 30); //flip the bitif (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;}}

}

x86 ASM(AT&T风格):

; input %edi; output %eax; clobbered regs: %ecx, %edxf:testl   %edi, %edije  .zero
movl    %edi, %eaxmovl    $1, %ecxmovl    %edi, %edxandl    $1, %eaxaddl    %eax, %eaxsubl    %eax, %ecxxorl    %eax, %eaxtestl   %edi, %edisetg    %alshrl    $31, %edxsubl    %edx, %eaximull   %ecx, %eaxsubl    %eax, %edimovl    %edi, %eaximull   %ecx, %eax.zero:xorl    %eax, %eaxret

代码检查,所有可能的32位整数都通过,错误为-2147483647(下限溢位)。

这个怎么样?

int nasty(int input){return input + INT_MAX/2;}
number f( number n){static count(0);if(count > 0) return -n;return n;}
f(n) = n
f(f(n)) = f(n) = -n
int f(int n) {return ((n>0)? -1 : 1) * abs(n);}

这个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 20 01 -11.1 -1.1-3.3 3.3foo -foo-bar +bar

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

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

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

虽然问题说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;}

编辑:

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

  • 他们只是放弃了吗?
  • 他们说这很愚蠢吗?
  • 他们尝试独特的方法吗?
  • 他们在处理问题的时候会和你交流吗?
  • 他们是否要求进一步完善要求?

永远不要只回答行为问题,除非你有一个非常好的答案。永远要愉快,试着让提问者参与进来。不要沮丧,不要过早放弃!如果你真的没有任何进展,尝试一些完全非法的可能有效的东西,你会得到几乎全部的学分。

这个怎么样:

dolocal function makeFunc()local varreturn function(x)if x == true thenreturn -varelsevar = xreturn trueendend
endf = makeFunc()endprint(f(f(20000)))

这里有一个解决方案,灵感来自于要求或声称复数不能用于解决这个问题。

乘以-1的平方根是一个想法,它似乎失败了,因为-1在整数上没有平方根。但是玩像数学这样的程序会给出例如等式

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

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

def mods(x, n):y = x % nif y > n/2: y-= nreturn y

使用上面的等式,这个问题现在可以解决为

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

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

f(n) { return IsWholeNumber(n)? 1/n : -1/n }

C++

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

Value f(Value input){return input;}

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

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

备选:static datamembers

python2.6:

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

我知道这对讨论没有任何帮助,但我无法抗拒。

我有另一个解决方案,工作一半的时间:

def f(x):if random.randrange(0, 2):return -xreturn x

这也是一个解决方案(但我们稍微弯曲了一下规则):

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

这是一个带负数的单项FIFO。当然它不适用于最大负数。

在python

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

我相信这符合所有要求。没有说参数必须是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;}}

好问题!

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

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

scala:

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() {@Overridepublic int hashCode() {return -(Integer)x;}};}return x.hashCode();}

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

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

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

Tcl:

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

按照其他一些答案的思路……如果它是一个整数,返回一个返回该数字负数的命令。如果它不是一个数字,对其进行评估并返回结果。

少于50个字符(C#)

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

更容易阅读:

static int f(int n) {if (n <= 0)return n;elsereturn 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)));}}

它起作用了(假设我正确理解了这个问题)

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

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

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

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

我想我会先试试这个,而不看别人的答案:

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

输出:[无]

lua:

function f(n)if type(n) == "number" thenreturn (-number) .. ""elsereturn number + 0endend

利用JavaScript异常。

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

f(f(0)) => 0

f(f(1)) => -1

工程除了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));elsereturn x - x / (Math.Abs(x));}

图片

int func(int a){static int p = 0;int ret = a;
if ( p ) ret *= -1;p ^= 1;
return ret;}

我认为这类问题的答案最好用图表直观地解释。当我们忽略零时,我们可以将整数划分为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 pointreturn (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缺失。

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

int main(){int n = -256; // 32-bit signed integerprintf("%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;}}
  1. 将n转换为符号和大小表示;
  2. 增加1/4的范围;
  3. 转换回来。
#define STYPE intSTYPE 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;}
#include <cmath>
int f(int n){static int count = 0;return ::cos(M_PI * count++) * n;}

那很容易!

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

示例:

这些规则是:

  • 0→0

  • ±2的1/2→±2的1/2

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

    对于k,0

唯一不匹配的值是±(2〇1),因为只有两个.必须有两个不能匹配的,因为只有四个数字在二元互补系统中的倍数,其中0和±2〇1已经被保留。

在一个人的补码系统中,存在+0和-0。我们去:

对于k,0

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

def f(n):return str(n) if type(n) == int else -int(n)

一个C函数:

int f(int n) /* Treats numbers in the range 0XC0000000 to 0X3FFFFFFF as valid togenerate f(f(x)) equal to -x. If n is within this range, it willproject n outside the range. If n is outside the range, it willreturn 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;}

用于测试和下载的想法链接

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

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

通过大小正负值,INT_MIN,INT_MAX测试,它似乎可以工作……如果这是一个问题,可以使线程安全,但它不是任务的一部分。

也许我错过了什么?

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

let rec f n =if n = 0 then 0elif n > 0 thenif (f (n - 1) <> n) then n + 1else -(n - 1)elseif (f (-(n - 1)) = n) then n - 1else -(n + 1)

服务器SQL解决方案

create function dbo.fn_fo(@num int) -- OUTER FUNCTIONRETURNS intASbeginRETURN @num * -1endGO
create function dbo.fn_fi(@num int) -- INNER FUNCTIONRETURNS intASbeginRETURN @num * -1endGO
declare @num AS int = -42SELECT dbo.fn_fo(dbo.fn_fi(@num)) -- Gives (-42)

在coffeescript中播放它:

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

使用循环排列方法来做到这一点。

-b a b-a

a b-a-b

在琐碎的情况下f(0)返回0

很抱歉我的手机粗略的回答,28日之后我会发布完整版本(现在正在考试…)简单地说,认为f(n)是一个循环排列,问题是如何构造它。

定义fk=f(f(f(… f(n))))) (k fs))情况k=20.trivial情况f(0)返回01.在k=2的情况下,分组:{0}{1,2}{3,4}…{n, n+1|(n+1)%2=0},注意:我只使用Z+,因为构造不需要使用负数。2.construct排列:如果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,虽然方法有效,但是没什么意义,ahh?(f(n)=-n就可以了)

使用问题中提供的信息,您可以

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

也许我遗漏了什么?

这难道不是这么简单:

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

丑陋,但工作。

它通过保存状态来作弊,但它有效,将操作分成两部分:-n=(~n+1)对于整数

int f(int n) {static int a = 1;a = !a;if (a) {return (~n);} else {return (n+1);}}

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] = -swapif (x[1] == 0)x = x[0]return x

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

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

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

int f(int n){static bool eFlag = false; // Only executed onceeFlag = !eFlag;return eFlag?-n:n;}

另一个作弊解决方案在C++,运营商重载。

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

这是一个简短的Python回答

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

一般案例

对上面的稍微调整可以处理我们希望k自调用否定输入的情况-例如,如果k = 3,这意味着g(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, -34, 5, 6, -4, -5, -6

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

兼容范围分析

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

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

Objective-c

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

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


f(n):

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

当然,这一切都可以缩短到一行,但是其他人可能无法阅读…


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

F#

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

其中n使得System.Int32.MinValue < n < System.Int32.MaxValue

我尝试了高尔夫球这个答案 by罗德里克·查普曼

没有分支: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%。(正数和负数的随机数据集,足够的重复,并阻止编译器通过预热优化代码。)考虑到非分支版本中的操作数量以及可能良好的分支预测,这并不奇怪,因为该函数被调用了两次:f(f(i))。当我将基准更改为测量:f(i)时,分支版本仅快了28%。我认为这证明分支预测在第一种情况下确实做了一些好事。更多证明:当使用f(f(f(f(i))))测试时,分支版本快了42%。

Wolfram语言中的解决方案:

f[f[n_]] := -n

应用场景:

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

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

javascript

function f(n)  {return typeof n === "number" ?function() {return -n} :n();}

根据微软/谷歌面试官在面试中通常会问的问题,我认为提问者意味着一个创新的、轻量级的、简单的解决方案,它将使用按位运算,而不是那些复杂的高级答案。

受到@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]

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  -52  orig -4 f(n)....  -4   4  -4   4  -43  orig -3 f(n)....  -3   3  -3   3  -34  orig -2 f(n)....  -2   2  -2   2  -25  orig -1 f(n)....  -1   1  -1   1  -1
6  orig  0 f(n)....   0  -0   0  -0   07  orig  1 f(n)....   1  -1   1  -1   18  orig  2 f(n)....   2  -2   2  -2   29  orig  3 f(n)....   3  -3   3  -3   310  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 -99999999999999999999999999999999f(n)....-9999999999999999999999999999999999999999999999999999999999999999-9999999999999999999999999999999999999999999999999999999999999999-99999999999999999999999999999999
2  orig      -1239999999999999999999999999999f(n)....  -12399999999999999999999999999991239999999999999999999999999999-12399999999999999999999999999991239999999999999999999999999999-1239999999999999999999999999999`

我参加这个聚会迟到了,现在可能是个墓地。但是我有两个贡献,受到盗龙上一个使用lambda的Python答案的启发。读者可能会认为这个解决方案只在无类型语言中可行,而在类型语言中它需要一些明确的额外标记。

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

{-# LANGUAGE MultiParamTypeClasses, FlexibleInstances, FunctionalDependencies #-}
module Main where
class Tran σ τ | σ -> τ wheretran :: σ -> τ
instance Tran Int (() -> Int) wheretran n = \_ -> (-n)
instance Tran (() -> Int) Int wheretran g = g ()
f :: Tran σ τ => σ -> τf = tran
main :: IO ()main = doprint $ f (f (42 :: Int)) -- --> -42print $ f (f (0 :: Int)) -- --> 0print $ f (f (-69 :: Int)) -- --> 69

接下来是类型化球拍中的解决方案2。这个满足尽可能大的域的属性,因为球拍中的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