前回のインタビューで私が受けた質問:
以下のように関数
f
を設計します。f(f(n)) == -n
n
は32ビットです。符号付き整数;あなたは複素数演算を使用することはできません。このような関数を全範囲の数に対して設計することができない場合は、できるだけ広い範囲で設計してください。
何か案は?
Objective-C
これは "-1"以外のすべての数値に対して機能します。
int
を使ってNSInt
を使うことに移行する場合は、-1の値をNULL
に設定してから2回目に+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;
}
もちろん、これはすべて1行のように短くすることができますが、それから他の人々はそれに沿って読むことができないかもしれません...
とにかく、私はブーリアンのロジックを奇数か偶数のどちらかの状態で保存しました。
私はゴルフしてみました この答え byRodrick 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%速くなった。 (正数と負数のランダムなデータセット、十分な繰り返し、ウォームアップによりコードを最適化するためのコンパイラの防止)これは、ブランチ化されていないバージョンでの操作数と可能性のある良好な分岐予測を考えると、それほど驚くことではありません関数が2回呼び出されていること:f(f(i))
。ベンチマークを「f(i)
」に変更すると、分岐バージョンはわずか28%速くなります。私はこれが分岐予測が実際に最初のケースでいくらか良いことをしたことを証明すると思います。もっと証明する:f(f(f(f(i))))
でテストすると、分岐版は42%高速です。
f[f[n_]] := -n
応用:
In[2]:= f[f[10]]
Out[2]= -10
In[3]:= f[10]
Out[3]= f[10]
質問はf(n)の値を何も言わないので、f [n]は未評価のままです。
C++のもう1つの不正な解決策、演算子のオーバーロード。
struct func {
int n;
func operator()(int k) { n = -k; return *this; }
int operator()(const func &inst) { return inst.n; }
} f;
f(x)= 2Dデカルト座標系で原点を中心に反時計回りに90度回転した点(x)。ただ1つの数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
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
のようになります。
ジャバスクリプト
function f(n) {
return typeof n === "number" ?
function() {return -n} :
n();
}
Microsoft/Googleのインタビュアーがインタビューの中で通常尋ねる質問に基づいて、私はその質問者はそれらの複雑なハイレベルな答えではなく、ビットごとの操作を使用する革新的で軽量のシンプルなソリューションを意味すると思います。
@eipipuzの答えに触発されて、私はこのC++関数を書きました(まだそれを実行しませんでした):
int32_t f(int32_t n){
int32_t temp = n & 00111111111111111111111111111111;
x = n >> 30;
x++;
x = x << 30;
return x | temp;
}
nの左端2ビットをxに格納し、に1を加えたものx、そしてそれをnの左端の2ビットに置き換えます。 。
f(n)を別のf(n)と実行し続ける場合パラメータnとして、左端の2ビットは次のように回転します。
00 - > 01 - > 10 - > 11 - > 00 ...
右端の30ビットは変化しません。 8ビット整数の例
例1
例2
私はカンニングをするが、それでも要件を満たしていると自白する。これはプログラミングの魔法使いであり、実際は数学ではありません。 -2 ^ 31を除いて、それは範囲全体に働きます。
int f(int n)
{
static bool eFlag = false; // Only executed once
eFlag = !eFlag;
return eFlag?-n:n;
}