我上次面试时收到的一个问题:
设计一个函数
f
,使得:f(f(n)) == -n
当
n
是一个32位的有符号整数时,你不能使用复数运算。如果你不能为所有数字范围设计这样的函数,那就尽可能地为最大范围设计。
有什么想法吗?
根据你所使用的平台,有些语言允许在函数中保持状态。例如,VB.Net:
Function f(ByVal n As Integer) As Integer
Static flag As Integer = -1
flag *= -1
Return n * flag
End Function
据我所知,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,max或min int),但它比迄今为止提出的任何其他方法都要好。
利用JavaScript异常。
function f(n) {
try {
return n();
}
catch(e) {
return function() { return -n; };
}
}
f(f(0)) => 0
f(f(1)) => -1
对于所有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
这是一个C++版本的代码,可能有些违反规则,但适用于所有数值类型(floats,ints,doubles)甚至是重载一元减号的类类型:
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;
}
x86汇编语言(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出现了错误(下溢)。
这个 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
使用全局变量...那又怎样?
bool done = false
f(int n)
{
int out = n;
if(!done)
{
out = n * -1;
done = true;
}
return out;
}
没有人说f(x)必须是相同的类型。
def f(x):
if type(x) == list:
return -x[0]
return [x]
f(2) => [2]
f(f(2)) => -2
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)
对于范围内所有整数,f(f(x)) = -x
。 f(x)
的结果也在此范围内,但计算需要使用64位整数。
我希望你更改最高有效位的2个比特。
00.... => 01.... => 10.....
01.... => 10.... => 11.....
10.... => 11.... => 00.....
11.... => 00.... => 01.....
正如您所看到的,这只是一种加法,忽略了进位位。
我是怎么得出答案的呢?我的第一个想法只是需要对称性。需要4个转弯才能回到起点。起初我认为这是2位格雷码。然后我想实际上标准二进制就足够了。