我上次面试时遇到的一个问题:
设计一个函数
f
,这样:f(f(n)) == -n
哪里
n
是 32 位有符号整数;你不能使用复数算术。如果您不能为整个数字范围设计这样的函数,请尽可能设计最大范围。
有任何想法吗?
你没有说他们期望什么样的语言......这是一个静态解决方案(Haskell)。它基本上弄乱了 2 个最重要的位:
f :: Int -> Int
f 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()
怎么样:
f(n) = 符号(n) - (-1) n * n
在 Python 中:
def f(n):
if n == 0: return 0
if n >= 0:
if n % 2 == 1:
return n + 1
else:
return -1 * (n - 1)
else:
if n % 2 == 1:
return n - 1
else:
return -1 * (n + 1)
Python 自动将整数提升为任意长度的 long。在其他语言中,最大的正整数会溢出,因此它适用于除那个之外的所有整数。
要使其适用于实数,您需要将(-1) n 中的 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));
else
return ((long)Math.Floor(n) % 2 == 0) ? (n - 1) : (-1 * (n + 1));
}
如果不使用额外信息(32 位 int 除外),以下是为什么对于所有数字都不能存在这样的函数的证明:
我们必须有 f(0) = 0。(证明:假设 f(0) = x。那么 f(x) = f(f(0)) = -0 = 0。现在,-x = f(f(x )) = f(0) = x,这意味着 x = 0。)
此外,对于任何x
和y
,假设f(x) = y
。那时我们想要f(y) = -x
。和f(f(y)) = -y => f(-x) = -y
。总结一下:如果f(x) = y
, 那么f(-x) = -y
, 和f(y) = -x
, 和f(-y) = x
.
因此,我们需要将除 0 以外的所有整数分成 4 个一组,但我们有奇数个这样的整数;不仅如此,如果我们删除没有正对应的整数,我们仍然有 2(mod4) 个数字。
如果我们删除剩下的 2 个最大数(按绝对值),我们可以得到函数:
int sign(int n)
{
if(n>0)
return 1;
else
return -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 个数字作为奖励。(但这只是一个愚蠢的假设。)
由于 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));
}
或者,您可以滥用预处理器:
#define f(n) (f##n)
#define ff(n) -n
int main()
{
int n = -42;
cout << "f(f(" << n << ")) = " << f(f(n)) << endl;
}
这适用于所有负数。
f(n) = 绝对值(n)
因为对于二进制补码整数,负数比正数多一个,所以比与 相同的解f(n) = abs(n)
多一种情况有效。得到你一个... :Df(n) = n > 0 ? -n : n
f(n) = -abs(n)
更新
不,这对于一种情况无效,因为我刚刚从 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) => ...
现在有一个明显的长度为四的循环。代入x = f(n)
并注意到所获得的方程f(f(f(n))) = f(f(x)) = -x
成立,得到以下结果。
n => x => -n => -x => n => ...
所以我们得到一个长度为 4 的循环,其中有两个数字,两个数字取反。如果您将循环想象为一个矩形,则否定值位于对角。
构造这样一个循环的许多解决方案之一是以下从 n 开始。
n => 否定和减一 -n - 1 = -(n + 1) => 加一 -n => 取反加一 n + 1 => 减一 n
一个具体的例子就是这样一个循环+1 => -2 => -1 => +2 => +1
。我们快完成了。注意到构造的循环包含一个奇数正数,它的偶数后继,并且两个数都取反,我们可以很容易地将整数划分为许多这样的循环(2^32
是四的倍数),并找到了一个满足条件的函数。
但是我们有一个零问题。循环必须包含0 => x => 0
,因为零对其自身取反。并且因为循环状态已经0 => x
遵循它0 => x => 0 => x
。这只是一个长度为 2 的循环,并且x
在两次应用后变为自身,而不是变为-x
. 幸运的是,有一个案例可以解决这个问题。如果X
等于 0,我们得到一个长度为 1 的只包含零的循环,并且我们解决了这个问题,得出的结论是零是 的一个不动点f
。
完毕?几乎。我们有2^32
数字,零是留下2^32 - 1
数字的固定点,我们必须将该数字划分为四个数字的循环。不好,这2^32 - 1
不是四的倍数 - 将保留三个数字,不在任何长度为四的循环中。
我将使用较小的一组 3 位有符号迭代器来解释解决方案的其余部分,范围从-4
到+3
. 我们已经完成了零。我们有一个完整的周期+1 => -2 => -1 => +2 => +1
。现在让我们构造从 开始的循环+3
。
+3 => -4 => -3 => +4 => +3
出现的问题+4
是不能表示为 3 位整数。我们将+4
通过取反-3
来获得+3
- 仍然是一个有效的 3 位整数 - 但然后将+3
(binary 011
) 加 1 产生100
二进制。解释为无符号整数,+4
但我们必须将其解释为有符号整数-4
。所以实际上-4
对于这个例子或Int.MinValue
在一般情况下是整数算术否定的第二个固定点 -0
并且Int.MinValue
被映射到它们自己。所以循环实际上如下。
+3 => -4 => -3 => -4 => -3
它是一个长度为 2 的循环,另外+3
通过 进入循环-4
。结果-4
在两次函数应用后正确映射到自身,在两次函数应用后正确映射到自身+3
,-3
但-3
在两次函数应用后错误地映射到自身。
所以我们构造了一个函数,它适用于除一个以外的所有整数。我们能做得更好吗?不,我们不可以。为什么?我们必须构造长度为四的循环,并且能够覆盖整个整数范围,最多四个值。剩下的值是两个固定点0
,Int.MinValue
必须映射到它们自己和两个任意整数x
,-x
并且必须由两个函数应用程序相互映射。
要映射x
到-x
,反之亦然,它们必须形成一个四环,并且它们必须位于该环的对角。因此0
,Int.MinValue
也必须在对角。这将正确映射x
并-x
交换两个固定点0
,并Int.MinValue
在两个函数应用程序之后给我们留下两个失败的输入。所以不可能构造一个适用于所有值的函数,但是我们有一个适用于除一个之外的所有值的函数,这是我们能达到的最好的。
使用复数,您可以有效地将求反的任务分为两个步骤:
最棒的是您不需要任何特殊的处理代码。只需乘以 i 就可以了。
但是你不能使用复数。因此,您必须使用部分数据范围以某种方式创建自己的假想轴。由于您需要与初始值一样多的虚构(中间)值,因此您只剩下一半的数据范围。
假设有符号的 8 位数据,我试图在下图中对此进行可视化。您必须将其缩放为 32 位整数。初始 n 的允许范围是 -64 到 +63。以下是该函数对正 n 的作用:
对于负 n,函数使用中间范围 -65..-128。
作品除了 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));
else
return x - x / (Math.Abs(x));
}
这个问题没有说明函数的输入类型和返回值f
必须是什么(至少不是你提出的方式)......
...只是当 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
为真。
显然,这种方法可以扩展到更广泛的数字范围......
对于 javascript(或其他动态类型语言),您可以让函数接受一个 int 或一个对象并返回另一个。IE
function f(n) {
if (n.passed) {
return -n.val;
} else {
return {val:n, passed:1};
}
}
给予
js> f(f(10))
-10
js> f(f(-10))
10
或者,您可以在强类型语言中使用重载,尽管这可能会违反规则,即
int f(long n) {
return n;
}
long f(int n) {
return -n;
}
根据您的平台,某些语言允许您在函数中保持状态。VB.Net,例如:
Function f(ByVal n As Integer) As Integer
Static flag As Integer = -1
flag *= -1
Return n * flag
End Function
IIRC,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 循环与 ay => -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++ 版本,可能有点违反规则,但适用于所有数字类型(浮点数、整数、双精度数),甚至是重载一元减号的类类型:
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 asm(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(下溢)。
使用全局变量...但是这样吗?
bool done = false
f(int n)
{
int out = n;
if(!done)
{
out = n * -1;
done = true;
}
return out;
}
这个 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
没有人说过 f(x) 必须是同一类型。
def f(x):
if type(x) == list:
return -x[0]
return [x]
f(2) => [2]
f(f(2)) => -2
我实际上并没有试图解决问题本身,但确实有一些评论,因为问题表明这个问题是(工作?)面试的一部分:
int.MinValue
to int.MaxValue
,并且对于n
该范围内的每个调用f(f(n))
并检查结果是-n
),告诉我然后将使用测试驱动开发来获得这样的功能。哦,这个答案假设面试是针对 C# 编程相关职位的。如果面试是针对与数学相关的职位,那当然是一个愚蠢的答案。;-)
我会更改 2 个最重要的位。
00.... => 01.... => 10.....
01.... => 10.... => 11.....
10.... => 11.... => 00.....
11.... => 00.... => 01.....
正如你所看到的,它只是一个加法,省略了进位。
我是怎么得到答案的?我的第一个想法只是需要对称。4转回到我开始的地方。起初我以为,那是 2 位格雷码。然后我认为实际上标准二进制就足够了。
这是一个受需求启发或声称不能使用复数来解决此问题的解决方案。
乘以 -1 的平方根是一个想法,这似乎只是失败了,因为 -1 在整数上没有平方根。但是玩像mathematica这样的程序给出了例如方程
(1849436465 2 +1) 模 (2 32 -3) = 0。
这几乎和-1的平方根一样好。函数的结果需要是有符号整数。因此,我将使用修改后的模运算 mods(x,n) 返回整数 y,它与最接近 0 的 x 模 n 一致。只有极少数编程语言具有模运算,但可以轻松定义. 例如在python中是:
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
范围内的所有整数。的结果也在这个范围内,但是计算当然需要 64 位整数。[-2
31
-2, 2
31
-2]
f(x)
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 + 1
for (int i = -3; i <= 3 ; i++)
Console.WriteLine(f(f(i)));
Console.WriteLine(f(f(Int32.MaxValue))); // 2147483647
印刷:
2147483647
3
2
1
0
-1
-2
-3
-2147483647
本质上,该函数必须将可用范围划分为大小为 4 的循环,其中 -n 在 n 循环的另一端。但是,0 必须是大小为 1 的循环的一部分,否则0->x->0->x != -x
. 因为 0 是单独的,所以在我们的范围内必须有 3 个其他值(其大小是 4 的倍数),而不是在具有 4 个元素的正确循环中。
我选择了这些额外奇怪的值是MIN_INT
,MAX_INT
和MIN_INT+1
。此外,MIN_INT+1
将映射到MAX_INT
正确,但卡在那里而不是映射回来。我认为这是最好的折衷方案,因为它具有只有极值不能正常工作的优良特性。此外,这意味着它适用于所有BigInts。
int f(int n):
if n == 0 or n == MIN_INT or n == MAX_INT: return n
return ((Math.abs(n) mod 2) * 2 - 1) * n + Math.sign(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 的情况,这些都不是很好,但是除非允许您返回更广泛的类型,否则您对此无能为力。
我可以想象将第 31 位用作虚构 ( i ) 位将是一种支持一半总范围的方法。
适用于 n= [0 .. 2^31-1]
int f(int n) {
if (n & (1 << 31)) // highest bit set?
return -(n & ~(1 << 31)); // return negative of original n
else
return n | (1 << 31); // return n with highest bit set
}
问题说明“32 位有符号整数”,但没有指定它们是二进制补码还是二进制补码。
如果您使用补码,则所有 2^32 值都出现在长度为 4 的循环中 - 您不需要零的特殊情况,也不需要条件。
在 C 中:
int32_t f(int32_t x)
{
return (((x & 0xFFFFU) << 16) | ((x & 0xFFFF0000U) >> 16)) ^ 0xFFFFU;
}
这通过
经过两遍之后,我们得到了原始值的按位倒数。其中一个补码表示相当于否定。
例子:
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)
:D
boolean inner = true;
int f(int input) {
if(inner) {
inner = false;
return input;
} else {
inner = true;
return -input;
}
}
return x ^ ((x%2) ? 1 : -INT_MAX);
作为一名数学家,我想分享我对这个有趣问题的看法。我认为我有最有效的解决方案。
如果我没记错的话,您只需翻转第一位即可否定有符号的 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 的平方根。
如果我只是单独提出伪代码,没有冗长的前奏,那看起来就像一只脱了帽子的兔子,我想解释一下我是如何得到解决方案的。
在 PHP 中
function f($n) {
if(is_int($n)) {
return (string)$n;
}
else {
return (int)$n * (-1);
}
}
我相信您可以理解其他语言的这种方法的精神。我明确地将其转换回 int 以使不使用弱类型语言的人更清楚。对于某些语言,您必须重载该函数。
这个解决方案的巧妙之处在于,无论您以字符串还是整数开头,它都可以工作,并且在返回 f(n) 时不会明显改变任何内容。
在我看来,面试官会问,“这个候选人是否知道如何标记数据以供以后操作”,以及“这个候选人是否知道如何标记数据,同时最少改变它?” 您可以使用双精度、字符串或任何其他您喜欢转换的数据类型来执行此操作。
这很容易!
例子:
规则是:
0 → 0
±2³¹ → ±2³¹
奇数→偶数,偶数→-奇数:
对于所有 k, 0 < k < 2³⁰: (2k-1) → (2k) → (-2k+1) → (-2k) → (2k-1)
唯一不匹配的值是 ±(2³¹-1),因为只有两个。必须有两个无法匹配,因为在二进制补码系统中只有四的倍数,其中 0 和 ±2³¹ 已被保留。
在一个补码系统中,存在+0和-0。我们去:
对于所有 k, 0 < k < 2³⁰: (+2k) → (+2k+1) → (-2k) → (-2k-1) → (+2k)
我有另一个解决方案在一半的时间内有效:
def f(x):
if random.randrange(0, 2):
return -x
return x
简单的 Python 解决方案之所以成为可能,是因为对 f(x) 应该输出的内容没有限制,只有 f(f(x)):
def f(x):
return (isinstance(x, tuple) and -x[0]) or (x,)
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;
}
这个怎么样?
int nasty(int input)
{
return input + INT_MAX/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;
}
编辑:
这实际上是一个非常好的面试问题。最好的是那些难以或不可能回答的问题,因为它迫使人们仔细思考,您可以观察并寻找:
等等
除非你有一个非常好的答案,否则永远不要只回答行为问题。始终保持愉快并尝试让提问者参与进来。不要沮丧,不要早早放弃!如果你真的一无所获,尝试一些完全非法的可行的方法,你将获得几乎全部的功劳。
这也是一个解决方案(但我们稍微改变了规则):
def f(n):
if isinstance(n,int):
return str(n)
else:
return -int(n)
我认为这些问题的答案最好用图表来直观地解释。当我们忽略零时,我们可以将整数划分为 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 point
return (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。
#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;
}
使用问题中提供的信息,您可以
因此,您基本上是奇数->偶数->奇数或偶数->奇数->偶数,并且仅更改偶数的符号。唯一不起作用的数字是 -2^31
代码:
function f(x) {
var neg = x < 0;
x = Math.abs(x) ^ 1;
if (x & 1) {
neg = !neg;
}
return neg ? -x : x;
}
f(n) { return -1 * abs(n) }
我该如何处理这个溢出问题?还是我错过了重点?
在 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)
}
我不认为这是完全正确的想法。
Clojure 解决方案:
(定义宏 f [n] (如果(列表?n)`(-〜n)n))
也适用于任何大小、双倍和比率的正整数和负整数!
卢阿:
function f(n)
if type(n) == "number" then
return (-number) .. ""
else
return number + 0
end
end
在咖啡脚本中打高尔夫球:
f = (n)-> -n[0] or [n]
这是一个简短的 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
这组循环最大化了可以在以零为中心的任何机器类型中工作的输入范围,例如有符号 int32 或有符号 int64 类型。
兼容范围分析
x -> f(x)
实际上,映射必须形成长度循环2 * k
,其中x = 0
是一个特殊情况 1 长度循环,因为 -0 = 0。因此,k
当且仅当输入的范围 - 1(补偿 0)为2 * k 的倍数,正负范围相反。
对于有符号整数表示,我们总是有一个最小的负数,在该范围内没有正对应物,因此问题在整个范围内变得无法解决。例如,a 的signed char
范围为 [-128, 127],因此不可能f(f(-128)) = 128
在给定范围内。
不会在 MIN_INT 上失败:
int f(n) { return n < 0 ? -abs(n + 1) : -(abs(n) + 1); }
如上所述的问题并不要求该函数必须只接受 32 位整数,只有给定的 n 是一个 32 位整数。
红宝石:
def f( n )
return 0 unless n != 0
( n == n.to_i ) ? 1.0 / n : -(n**-1).to_i
end
这将适用于非常广泛的数字:
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,这意味着对于大多数情况这是第一次调用。如果最后一位与倒数第二位不同,那么我假设我们在第二次通话中,只需重新反转最后一位。显然,这不适用于使用最后两位的非常大的数字。但是,再一次,它适用于非常广泛的数字。
似乎很容易。
<script type="text/javascript">
function f(n){
if (typeof n === "string") {
return parseInt(n, 10)
}
return (-n).toString(10);
}
alert(f(f(1)));
</script>
也许作弊?(Python)
def f(n):
if isinstance(n, list):
return -n[0]
else:
return [n,0]
n = 4
print f(f(n))
--output--
-4
简单的:
function f($n) {
if ($n%2 == 0) return ($n+1)*-1;
else return ($n-1);
}
在 C 中,
int
f(int n) {
static int r = 0;
if (r == 1) {r--; return -1 * n; };
r++;
return n;
}
知道这是什么语言会有所帮助。我错过了什么吗?许多“解决方案”似乎过于复杂,而且坦率地说,不起作用(当我阅读问题时)。
这是rossfabricant答案的 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。当然,switch 语句中使用的文字必须更改。
这个怎么样(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 ValueError
else :
# send this integer to a tuple (representing ix)
return( (0,x) )
else :
# not an int or a tuple
raise 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)
>>> x
37
将 f 两次应用于零得到零:
>>> x=0
>>> x = f(x)
>>> x
(0, 0)
>>> x = f(x)
>>> x
0
我们处理一个问题没有解决方案的情况(在 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 f
raise ValueError
ValueError
如果您认为该函数通过模仿乘以 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 中看不到这样的解决方案。
这是在 Python 中的。适用于 n 的所有负值:
f = abs
这是我没有看到人们使用的变体。由于这是 ruby,32 位整数的东西有点出乎意料(当然可以添加检查)。
def f(n)
case n
when Integer
proc { n * -1 }
when Proc
n.call
else
raise "Invalid input #{n.class} #{n.inspect}"
end
end
(-10..10).each { |num|
puts "#{num}: #{f(f(num))}"
}
创建许多解决方案的一种方法是注意,如果我们将整数划分为两个集合 S 和 R st -S=S,-R=R,并且函数 g st 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 st 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);
}
}
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;
}
这适用于 -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 位有符号整数的可用范围一分为二。该函数的第一次迭代将n本身置于该范围之外。第二次迭代检查它是否在此范围之外 - 如果是,则将其放回范围内但使其为负。
它实际上是一种保留有关值 n 的额外“位”信息的方法。
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。这似乎与问题描述相符,但显然与谜题的精神背道而驰。
红宝石示例:
class Cheat
def ==(n)
true
end
end
def f(n)
Cheat.new
end
这给了我们:
>> 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;
}
}
骇人听闻,但正确。
有些是相似的,但只是想我会写下我的第一个想法(在 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; }
另一种方法是将状态保持在一个位并在负数的情况下小心翻转它的二进制表示......限制是2 ^ 29
诠释ffn(诠释n){
n = n ^ (1 << 30); //flip the bit
if (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;
}
}
}
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);
}
这个怎么样:
do
local function makeFunc()
local var
return function(x)
if x == true then
return -var
else
var = x
return true
end
end
end
f = makeFunc()
end
print(f(f(20000)))
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
蟒蛇2.6:
f = lambda n: (n % 2 * n or -n) + (n > 0) - (n < 0)
我意识到这对讨论没有任何帮助,但我无法抗拒。
整数 f(整数 n) { 静态 int x = 0; 结果 = -x; x = n; 返回结果; }
这是一个带否定的单项 FIFO。当然,它不适用于最大负数。
在 Python 中
f=lambda n:n[0]if type(n)is list else[-n]
好问题!
这花了我大约 35 秒的时间来思考和写下:
int f(int n){
static int originalN=0;
if (n!=0)
originalN=n;
return n-originalN;
}
斯卡拉:
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();
}
另一种利用短路的 Javascript 解决方案。
function f(n) {return n.inv || {inv:-n}}
f(f(1)) => -1
f(f(-1)) => 1
以为我会在不先查看其他人的答案的情况下尝试这个:
#include <stdio.h> #include <limits.h> #include <stdlib.h> int f(int n) { 如果(n > 0){ 如果(n % 2) 返回-(++n); 别的 { 返回(--n); } } 别的 { 如果(n % 2) 返回-(--n); 别的 { 返回 (++n); } } } int main(int argc, char* argv[]) { 诠释n; 对于(n = INT_MIN;n < INT_MAX;n++){ int N = f(f(n)); 如果(N!= -n){ fprintf(stderr, "失败!%i != %i\n", N, -n); } } n = INT_MAX; int N = f(f(n)); 如果(N!= -n){ fprintf(stderr, "失败!n = %i\n", n); } 返回0; }
输出:[无]
交流功能:
int f(int n) /* Treats numbers in the range 0XC0000000 to 0X3FFFFFFF as valid to
generate f(f(x)) equal to -x. If n is within this range, it will
project n outside the range. If n is outside the range, it will
return 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 进行测试,它似乎工作......如果这是一个问题,可以使线程安全,但它不是任务的一部分。
或者,也许我错过了什么?
int f(int n)
{
static long counter=0;
counter++;
if(counter%2==0)
return -n;
else
return 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;
}
}
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();
}
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;
}
我不知道这是否完全正确,但是一个简单的标志不起作用吗?在 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;
}
}
#include <cmath>
int f(int n)
{
static int count = 0;
return ::cos(M_PI * count++) * n;
}
这个想法已在其他答案中使用,但我将其放入了一行 Python:
def f(n):
return str(n) if type(n) == int else -int(n)
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)
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
我承认我会作弊,但仍然符合要求。这是编程魔法,而不是真正的数学。它适用于整个范围,除了-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;
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 逻辑存储在数字为奇数或偶数的状态下。
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
。
我试着打高尔夫罗德里克查普曼的这个答案。
无分支: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))
. 当我将基准更改为 measure:f(i)
时,分支版本仅快 28%。我认为这证明了分支预测在第一种情况下确实做了一些好事。更多证明:使用 进行测试时f(f(f(f(i))))
,分支版本的速度提高了 42%。
Wolfram 语言中的解决方案:
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中,将 1 加到x ,然后再次将其替换为n的最左边两位。
如果我们继续运行f(n)并将另一个f(n)作为参数n,最左边的两位将像这样旋转:
00 --> 01 --> 10 --> 11 --> 00 ...
请注意,最右边的 30 位不会改变。8 位整数的示例:
示例 1:
示例 2:
我认为可能的最大范围暗示了模块化算术解决方案。在一些模基 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)
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))
}
使用循环置换方法来做到这一点。
-bab -a
ab -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=nb=n+1
这将产生相同的排列,因为 n 和 f(n) 在同一组中。
注意排列为 P 返回 P(n)
对于 k=2t ,只做上面相同的事情,只是 MOD k。对于k=2t-1,虽然方法行得通,但是没有意义,啊?(f(n) = -n 没问题)
它通过保存状态来作弊,但它可以工作,将操作分成两部分: -n = (~n + 1) 用于整数
int f(int n) {
static int a = 1;
a = !a;
if (a) {
return (~n);
} else {
return (n+1);
}
}
我还没有看过其他答案,我假设已经彻底讨论了按位技术。
我以为我会在 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(); }
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)));
}
}
它有效(假设我正确理解了这个问题)
怎么样
int f(int n)
{
return -abs(n);
}