回答:
どうですか:
f(n)= sign(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に昇格させます。他の言語では、最大の正の整数はオーバーフローするため、それ以外のすべての整数で機能します。
作るために、それはあなたが交換する必要が実数のために働くのnを(-1)でのnで{ ceiling(n) if n>0; floor(n) if n<0 }
。
C#では(オーバーフローの状況を除いて、すべてのdoubleで機能します):
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));
}
あなたは彼らが期待する言語の種類を言っていませんでした...これが静的な解決策です(Haskell)。それは基本的に2つの最も重要なビットをいじっています:
f :: Int -> Int
f x | (testBit x 30 /= testBit x 31) = negate $ complementBit x 30
| otherwise = complementBit x 30
動的言語(Python)の方がはるかに簡単です。引数がXかどうかを確認し、-Xを返すラムダを返します。
def f(x):
if isinstance(x,int):
return (lambda: -x)
else:
return x()
class C a b | a->b where { f :: a->b }
ます。instance C Int (()->Int) where { f=const.negate }
; instance C (()->Int) Int where { f=($()) }
。
追加情報を使用しない場合(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
andについてy
、と仮定しますf(x) = y
。我々はしたいf(y) = -x
、その後。そしてf(f(y)) = -y => f(-x) = -y
。要約すると、if f(x) = y
、then 、、および。f(-x) = -y
f(y) = -x
f(-y) = x
したがって、0を除くすべての整数を4のセットに分割する必要がありますが、そのような整数の数は奇数です。それだけでなく、正の対応がない整数を削除しても、2(mod4)の数値が残っています。
残りの2つの最大数を(abs値によって)削除すると、次の関数を取得できます。
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);
}
}
もちろん、もう1つのオプションは、0に準拠せず、ボーナスとして削除した2つの数値を取得することです。(しかし、それはばかげています。)
n = -2147483648
(最小値)に注意してください。abs(n)
その場合はできません。結果は未定義(または例外)になります。
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)= abs(n)
2の補数整数の正の数よりも負の数が1つ多いため、は、と同じである解f(n) = abs(n)
よりも1つ多いケースで有効です。一つずつ手に入れました...:Df(n) = n > 0 ? -n : n
f(n) = -abs(n)
更新
いいえ、私はlitbのコメントで認識しただけなので、それは1つのケースでは無効です... abs(Int.Min)
オーバーフローするだけです...
私もmod 2の情報を使用することを考えましたが、結論として、それは動作しません...早くまで。正しく実行すると、Int.Min
オーバーフローする場合を除いて、すべての数値で機能します。
更新
私はしばらくそれを使って、素敵なビット操作のトリックを探しましたが、mod 2ソリューションが1つに収まるのに、素敵なワンライナーを見つけることができませんでした。
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
否定する2つの連続したアプリケーションを要求します。さらに2つのアプリケーション(f
合計4つ)は、議論を否定し、再びn
します。
n => f(n)=> -n => f(f(f(n)))=> n => f(n)=> ...
さて、長さ4の明らかなサイクルがあります。代入x = f(n)
して、得られた方程式f(f(f(n))) = f(f(x)) = -x
が成立することに注意すると、次のようになります。
n => x => -n => -x => n => ...
したがって、2つの数値と2つの数値が否定された長さ4のサイクルが得られます。サイクルが長方形であると想像すると、負の値は対角にあります。
このようなサイクルを構築する多くの解決策の1つは、nから始まる次のとおりです。
n =>否定して1を引く -n-1 =-(n + 1)=> 1つ追加 -n =>否定して1を追加 n + 1 => 1を減算 ん
そのようなサイクルの具体例はです+1 => -2 => -1 => +2 => +1
。あと少しで完了です。構築されたサイクルには奇数の正の数、その偶数の後続、および両方の数が否定されることに注意して、整数をそのような多くのサイクル(2^32
4の倍数)に簡単に分割し、条件を満たす関数を見つけました。
しかし、ゼロに関する問題があります。0 => x => 0
ゼロはそれ自体に否定されるので、サイクルは含まれている必要があります。そして、サイクルの状態はすでに0 => x
続いているため0 => x => 0 => x
です。これは長さ2のサイクルにすぎず、x
2回の適用後にそれ自体に変わり、ではありません-x
。幸いにも、問題を解決する1つのケースがあります。場合X
等号がゼロ我々は、ゼロを含む長さ1の周期を求めるとゼロの固定点であることを、我々はこの問題の結論を解きますf
。
できた?ほとんど。我々は持っている2^32
ゼロを残して固定小数点で、数字を2^32 - 1
数字を、私たちは4つの数字のサイクルにその数を分割する必要があります。2^32 - 1
4の倍数ではない悪い-長さ4のどのサイクルにもない3つの数値が残ります。
からまでの3ビットの符号付きイテガーの小さいセットを使用して、ソリューションの残りの部分を説明-4
し+3
ます。ゼロで終わりました。1つの完全なサイクルがあり+1 => -2 => -1 => +2 => +1
ます。次に、から始まるサイクルを作成し+3
ます。
+3 => -4 => -3 => +4 => +3
発生する問題は、+4
3ビット整数として表現できないことです。当社は、取得します+4
否定することで-3
に+3
まだ有効な3ビットの整数であるものを- -しかし、その後に1を加える+3
(バイナリこと011
)が得られる100
バイナリ。符号なし整数として解釈されますが、符号付き整数として+4
解釈する必要があり-4
ます。その-4
ため、実際には、この例またはInt.MinValue
一般的なケースでは、整数算術否定の2番目の固定小数点が0
あり、Int.MinValue
それらにマッピングされています。したがって、サイクルは実際には次のようになります。
+3 => -4 => -3 => -4 => -3
これは長さ2のサイクルで、さらに+3
を介してサイクルに入ります-4
。結果として-4
、2つの関数アプリケーションの後に+3
正しくマッピングされ-3
、2つの関数アプリケーションの後に正しくマッピング-3
されますが、2つの関数アプリケーションの後に誤って自身にマッピングされます。
したがって、1つを除くすべての整数に対して機能する関数を作成しました。もっと上手くできる?いいえ、できません。どうして?長さ4のサイクルを構築する必要があり、最大4つの値までの整数範囲全体をカバーできます。残りの値は、2つの固定点である0
とInt.MinValue
それ自身と任意の二つの整数にマップされなければならないx
し、-x
その2つの機能のアプリケーションによって相互にマップされなければなりません。
にマッピングx
する-x
、またはその逆を行うには、4つのサイクルを形成し、そのサイクルの反対側のコーナーに配置する必要があります。結果として0
、Int.MinValue
反対側のコーナーにもいる必要があります。これは正しくマップされますx
と-x
が、2つの固定ポイントを交換0
し、Int.MinValue
2つの機能のアプリケーションの後と2つの失敗の入力を私たちに残して。したがって、すべての値に対して機能する関数を作成することはできませんが、1つを除くすべての値に対して機能する関数があり、これが実現可能な最高の方法です。
複素数を使用すると、数値を否定するタスクを2つのステップに効果的に分割できます。
優れた点は、特別な処理コードを必要としないことです。iを掛けるだけで十分です。
ただし、複素数を使用することはできません。したがって、データ範囲の一部を使用して、なんとかして独自の仮想軸を作成する必要があります。初期値とまったく同じ数の虚数(中間)値が必要なので、データ範囲は半分しか残りません。
符号付き8ビットデータを想定して、次の図でこれを視覚化してみました。これを32ビット整数にスケーリングする必要があります。初期nの許容範囲は-64〜+63です。正のnに対して関数が行うことは次のとおりです。
負のnの場合、関数は中間範囲-65 ..- 128を使用します。
float
vsを使用しますint
)。多くの回答が説明する「4要素リング」は、4つの状態を必要とします。これは、2つの状態を持つ2つの次元として表すことができます。この回答の問題は、追加の処理スペースが必要で(-64..63の場合は「機能する」だけですが、-128..127のスペースが必要です)、書かれた式を明示的に述べていないことです。
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));
}
0
する0
と-2147483648
し-2147483648
、否定演算子のためのこれらの二つの数が固定されているので、点x => -x
。残りの数値については、上の画像の矢印に従ってください。SurDinの答えとそのコメントから明らかなように、この場合には、二つの数があるでしょう2147483647
し、-2147483647
とのスワップに委ねていない他のペアで。
質問は、入力タイプと関数の戻り値は何について何も言うことはありません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またはオブジェクトのいずれかを受け入れ、もう一方を返すようにすることができます。すなわち
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を加え、すべての奇数の大きさから1を引きます。2つの呼び出しの結果の大きさは同じですが、1つの呼び出しで符号を交換するだけです。これが機能しない場合もあります(-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 ++バージョン。おそらくルールは多少曲げられますが、すべての数値型(float、int、double)と、単項マイナスをオーバーロードするクラス型でも機能します。
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
n
、文字列の場合、548を "First_Time_548"にして、次に関数を実行するときに... if(prefix == First_Time_ ")で" First_Time_ "を"-"に置き換える
私は実際には問題自体に解決策を提供しようとはしていませんが、この問題が引き起こされたのは(ジョブ?)インタビューの一部であるとの質問があるので、いくつかコメントがあります:
int.MinValue
から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)mod(2 32 -3)= 0。
これは、平方根が-1とほぼ同じです。関数の結果は、符号付き整数である必要があります。したがって、0に最も近いnを法とするxに合同な整数yを返す、修正された法演算mods(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)
2 ^ 32-1の範囲のC#、(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だけであるため、4つの要素を持つ適切なサイクルではなく、範囲内に他の3つの値(サイズは4の倍数)が存在する必要があります。
私はあることをこれらの余分な奇妙な値を選んだMIN_INT
、MAX_INT
とMIN_INT+1
。さらに、正しくMIN_INT+1
マッピングされMAX_INT
ますが、そこに行き詰まり、元に戻りません。私はこれが最良の妥協案だと思います。それは、正常に動作していない極端な値のみの良い特性を持っているからです。また、すべての BigIntで機能することを意味します。
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の補数か1の補数かを指定していません。
1の補数を使用する場合、すべての2 ^ 32値は長さ4のサイクルで発生します-ゼロの特別なケースは必要なく、条件も必要ありません。
C:
int32_t f(int32_t x)
{
return (((x & 0xFFFFU) << 16) | ((x & 0xFFFF0000U) >> 16)) ^ 0xFFFFU;
}
これは
2つのパスの後、元の値のビットごとの逆になります。これは、1の補数表現で否定と同等です。
例:
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;
}
}
数学者としてのこの興味深い問題についての私の見解を共有したいと思います。私は最も効率的な解決策を持っていると思います。
私が正しく覚えている場合は、最初のビットを反転するだけで、符号付き32ビット整数を無効にします。たとえば、n = 1001 1101 1110 1011 1110 0000 1110 1010の場合、-n = 0001 1101 1110 1011 1110 0000 1110 1010になります。
それでは、符号付き32ビット整数を受け取り、fを2回取ることが最初のビットを反転することと同じであるという特性を持つ別の符号付き32ビット整数を返す関数fをどのように定義しますか?
整数のような算術の概念については触れずに、質問を言い換えます。
長さ32のゼロと1のシーケンスを取り、同じ長さのゼロと1のシーケンスを返す関数fをどのように定義しますか。fを2回取得することは、最初のビットを反転することと同じであるという特性があります。
観察:32ビットの場合について上記の質問に答えることができれば、64ビットの場合、100ビットの場合などについても答えることができます。最初の32ビットにfを適用するだけです。
2ビットケースの質問に答えられるとしたら、Voila!
そして、はい、最初の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の平方根による乗算を思い出させるはずです。
長いプレリュードなしで疑似コードだけを提示した場合、それは帽子をかぶったウサギのように思えるので、私はどのようにして解決策を得たかを説明したいと思いました。