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

设计函数f,使得:f(f(n))==-n其中n是32位有符号整数;你不能用复数算术。如果你不能为整个数字范围设计这样的函数,那么就尽可能为最大范围设计它。

有什么想法吗?


当前回答

MIN_INT不会失败:

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

其他回答

也许我错过了什么?

这不是简单的事情吗

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

丑陋但有效。

我试着打高尔夫,这是罗德里克·查普曼的回答。

无分支: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%。

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

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

备选方案:静态数据成员

在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作为状态信息

这种方法的优点是

它愿意为您提供“负零”,对于绝对值小于2^53的整数,简单地添加加号,即+f(f(_))函数调用本身将具有隐式代表您完成类型铸造,结果值将再次为数字对于大整数,只需减去()任何前导空格轻松处理大整数,而不会丢失精度从类型转换为双精度浮点

`

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

 2  orig      -1239999999999999999999999999999 
    f(n)....  -1239999999999999999999999999999                   
               1239999999999999999999999999999
              -1239999999999999999999999999999
               1239999999999999999999999999999
              -1239999999999999999999999999999`
number f( number n)
{
  static count(0);
  if(count > 0) return -n;
  return n;
}

f(n) = n

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