【发布时间】:2010-10-18 10:12:31
【问题描述】:
我上次面试时遇到的一个问题:
设计一个函数
f,这样:f(f(n)) == -n
n是一个 32 位 有符号整数;你不能使用复数算术。如果您无法为整个数字范围设计这样的函数,请尽可能设计最大范围。
有什么想法吗?
【问题讨论】:
-
这次面试是为了什么工作?
我上次面试时遇到的一个问题:
设计一个函数
f,这样:f(f(n)) == -n
n是一个 32 位 有符号整数;你不能使用复数算术。如果您无法为整个数字范围设计这样的函数,请尽可能设计最大范围。
有什么想法吗?
【问题讨论】:
Tcl:
proc f {input} {
if { [string is integer $input] } {
return [list expr [list 0 - $input]]
} else {
return [eval $input]
}
}
% f [f 1]
-1
按照其他一些答案的思路...如果它是一个整数,则返回一个返回该数字的负数的命令。如果不是数字,则对其求值并返回结果。
【讨论】:
这是一个 C/C++ 解决方案,不使用任何位运算符,也不需要任何数学库,尽管它有点作弊......
double f(double n)
{
if (n == (double)(int)n)
return n + 0.5;
else
return -(n - 0.5);
}
这适用于所有 32 位整数,但 0x80000000 是一个例外(因为它的反面不能存储在 32 位整数系统中)。 f(f(n)) == -n 将始终为 true,除非在这种情况下。
不过,我确信有一种更简单、更快捷的方法来实现它。这只是浮现在脑海中的第一件事。
【讨论】:
int func(int a)
{
static int p = 0;
int ret = a;
if ( p ) ret *= -1;
p ^= 1;
return ret;
}
【讨论】:
这个想法已经在其他答案中使用过,但我在一行 Python 中得到了它:
def f(n):
return str(n) if type(n) == int else -int(n)
【讨论】:
int f(int n)
{
static long counter=0;
counter++;
if(counter%2==0)
return -n;
else
return n;
}
【讨论】:
#include <cmath>
int f(int n)
{
static int count = 0;
return ::cos(M_PI * count++) * n;
}
【讨论】:
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)
【讨论】:
也许我遗漏了什么?
这不就是这么简单吗:
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;}
丑陋但有效。
【讨论】:
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
【讨论】:
x 应该是一个 32 位有符号整数。现在我意识到这就是为什么你要开始测试x.length == 1。伪代码把我吓跑了。
我承认我会作弊,但仍然符合要求。这是编程魔法,而不是真正的数学。它适用于整个范围,除了 -2^31。
int f(int n)
{
static bool eFlag = false; // Only executed once
eFlag = !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;
【讨论】:
我不知道这是否完全正确,但是一个简单的标志就不会起作用吗?在 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;
}
}
【讨论】:
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) {//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;
}
当然,这都可以缩短到像一行,但其他人可能无法阅读......
无论如何,我将 BOOLEAN 逻辑存储在数字为奇数或偶数的状态下。
【讨论】:
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)
【讨论】:
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。
【讨论】:
我尝试打高尔夫球 this answer 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%。 (正数和负数的随机数据集,足够的重复并阻止编译器优化代码,并进行预热。)这并不奇怪,考虑到非分支版本中的操作数量和可能的良好分支预测,因为事实上该函数被调用两次:f(f(i))。当我将基准更改为测量:f(i) 时,分支版本仅快 28%。我认为这证明了分支预测在第一种情况下确实做了一些好事。更多证明:使用f(f(f(f(i)))) 进行测试时,分支版本的速度提高了 42%。
【讨论】:
Wolfram Language中的解决方案:
f[f[n_]] := -n
应用:
In[2]:= f[f[10]]
Out[2]= -10
In[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中,将x加1,然后替换为n的最左边两位n 再次。
如果我们以另一个 f(n) 作为参数 n 继续运行 f(n),最左边的两位将像这个:
00 --> 01 --> 10 --> 11 --> 00 ...
请注意,最右边的 30 位不会改变。 8位整数示例:
示例 1:
示例 2:
【讨论】:
int f(int x){
if (x < 0)
return x;
return ~x+1; //two's complement
}
【讨论】:
PHP,不使用全局变量:
function f($num) {
static $mem;
$answer = $num-$mem;
if ($mem == 0) {
$mem = $num*2;
} else {
$mem = 0;
}
return $answer;
}
适用于整数、浮点数和数字字符串!
刚刚意识到这做了一些不必要的工作,但是,无论如何
【讨论】:
记住你最后的状态不是一个足够好的答案吗?
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))
}
【讨论】:
我认为可能的最大范围暗示了一种模算术解决方案。在一些模基 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)
【讨论】:
它通过保存状态来作弊,但它有效,将操作分成两部分:-n = (~n + 1) 用于整数
int f(int n) {
static int a = 1;
a = !a;
if (a) {
return (~n);
} else {
return (n+1);
}
}
【讨论】:
使用循环置换方法来做到这一点。
-b a b -a
a b -a -b
在琐碎的情况下 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 没问题)
【讨论】:
我还没有查看其他答案,我认为按位技术已经被彻底讨论过。
我以为我会在 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(); }
【讨论】:
f(f(x)) == -x,但它对于任何其他用途都是完全无用和破坏性的。你说得对,它是邪恶的;但这绝不是一件好事。此外...如果有人像这样测试您的功能,您会怎么做:cout << x << (f(f(x)) == -x ? " works." : " doesn't work.") << endl; 结果未定义。在我的 GCC 版本中,它每行打印零。被当场抓获!
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)));
}
}
它有效(假设我正确理解了这个问题)
【讨论】:
n 最初为正(或零)时有效。问题明确指出Where n is a 32 bit *signed integer*,这意味着 n 也可以从否定开始。
怎么样
int f(int n)
{
return -abs(n);
}
【讨论】: