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

849

我上次面试时收到的一个问题:

设计一个函数f,使得:

f(f(n)) == -n

n 是一个32位的有符号整数时,你不能使用复数运算。

如果你不能为所有数字范围设计这样的函数,那就尽可能地为最大范围设计。

有什么想法吗?


6
这次面试是为了什么职位? - tymtam
120个回答

3
  1. 将n转换为符号-大小表示法;
  2. 加上范围的四分之一;
  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;
    }

3
使用问题中提供的信息,您可以:
  1. 从二进制补码转换为符号位表示法
  2. 如果最后一位设置了,翻转符号位和最后一位;否则,只翻转最后一位
  3. 转换回二进制补码。
因此,您基本上会进行奇数 -> 偶数 -> 奇数或偶数 -> 奇数 -> 偶数的转换,并仅对偶数更改符号。唯一无法使用此方法转换的数字是-2^31.
代码:
function f(x) {
  var neg = x < 0;
  x = Math.abs(x) ^ 1;
  if (x & 1) {
    neg = !neg;
  }
  return neg ? -x : x;
}

2
Lua:
function f(n)
    if type(n) == "number" then
        return (-number) .. ""
    else
        return number + 0
    end
end

2
一个使用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)
}

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


我把你的“Final(-x)”改成了“Last (-x)”,但是f(f(8))的结果不是-8,而是Last (-8)。对不起。 :) - user unknown
Last(-8) 隐式转换为 -8,因此解决方案有效。 - Daniel Spiewak

2
f(n) { return -1 * abs(n) }

我该如何处理这个溢出问题?还是我错过了什么重要的点?

你总是返回负值,即使输入是负数。 - SilentGhost
1
考虑到问题的要求,这可能是一个足够的回答。它并没有说f(n)必须返回不同的值。这个问题是一个谜题,所以你要寻找可以使解决方案可行的漏洞。 - JasonTrue
@Jason:问题在于对于负数输入n,根据问题要求,f(f(n))应该返回一个正数,而你的解决方案未能做到这一点,因为它总是返回一个负数。 - David Archer
3
对于你的情况,f(f(-1))=f(1)=-1。因此对于n<0,有f(f(n))=n。 - Steven
1
适用于近50%的输入。可重现且线程安全。 :) 有些劣质的解决方案却获得了更多的赞。 :) - user unknown
显示剩余2条评论

2

Clojure解决方案:

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

适用于任何大小的正负整数、双精度浮点数和比例!


2

使用Coffeescript编写高尔夫球游戏:

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

编译成 function f(n) { return -n[0] || [n] } 的函数。 - Ricardo Tomasi

2
这是一个简短的Python答案:
def f(n):
  m = -n if n % 2 == 0 else n
  return 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, -3
4, 5, 6, -4, -5, -6

这组循环可以使得任何以零为中心的机器类型(如signed int32或signed int64类型)最大化输入范围。

兼容范围的分析

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

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


1

有些相似,但我想写下我的第一个想法(用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

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接