設計関数f(f(n))== -n


841

私が最後のインタビューで得た質問:

次のfような関数を設計します。

f(f(n)) == -n

n32ビットの符号付き整数はどこにありますか。複素数演算は使用できません。

数値の全範囲に対してこのような関数を設計できない場合は、可能な限り最大の範囲に対して設計してください。

何か案は?


2
この面接は何の目的でしたか?
ティムタム

回答:


377

どうですか:

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));
}

10
-1 * 0がまだ0であるため、-1で
失敗

3
いいえ、そうではありません。f(-1)=0。f(0)= 1
1800情報

5
しかしそれは1のために壊れています。f(1)=0。f(0)= 1
1800情報

18
うーん、状態を偶数と奇数で保存しているので、私はそれを考えるべきでした。
不明

38
最も重要なことは、実際の関数(無限に多くの解がある)ではなく、そのような関数を構築できるプロセスだと思います。
ピヨン2009

440

あなたは彼らが期待する言語の種類を言っていませんでした...これが静的な解決策です(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()

23
かっこいい、これが好き... JavaScriptでの同じアプローチ:var f = function(n){return(typeof n == 'function')?n():function(){return -n; }}
Mark Renouf、

おそらく私のHaskellが非常に錆びているだけですが、(f 0)について確認しましたか?(negate演算で)ラップアラウンド演算で32ビット整数を処理している場合は、(f 0x80000000)と同じ結果が生成されるようです。そしてそれは悪いでしょう。
ダライアスベーコン

11
平均的なインタビュアー、ラムダ構造何であるかさえ知っていますか?
Jeremy Powell、

4
もちろん、そのようなタイプチートのトリックは、たとえ静的であってもHaskellでも機能しclass C a b | a->b where { f :: a->b }ます。instance C Int (()->Int) where { f=const.negate }; instance C (()->Int) Int where { f=($()) }
-leftaroundabout

4
何?typeof f(n)=== 'function'、特にnは数値であり、数値が返されることを期待しているという考えはどこで得ましたか?ここでインスタンスケースがどのように適用されるのか理解できません。私はPythonをうまく話せませんが、JSでは関数型の引数のチェックはこの場合は明らかに間違っています。ここでは数値解のみが適用されます。fは関数、f(n)は数値です。
ハリー

284

追加情報を使用しない場合(32ビットのintを除く)、すべての数値に対してこのような関数が存在できない理由を以下に示します。

f(0)= 0でなければなりません(証明:f(0)= xと仮定します。次にf(x)= f(f(0))= -0 = 0とします。今度は-x = f(f(x ))= f(0)= x、つまりx = 0です。)

さらに、すべてのxandについてy、と仮定しますf(x) = y。我々はしたいf(y) = -x、その後。そしてf(f(y)) = -y => f(-x) = -y。要約すると、if f(x) = y、then 、、および。f(-x) = -yf(y) = -xf(-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つの数値を取得することです。(しかし、それはばかげています。)


29
グローバル変数やコードを難読化するトリックに頼らずに、負の数を処理する優れた手続き型ソリューションを見つけるために、ここまで読んでいる必要があるとは思えません。もし私があなたに複数回投票することができれば、私はそうします。
カイル・シメック

素晴らしい観察、任意のn個の符号付きビットに非ゼロ整数の奇数があること。
Andres Jaan Tack

これも私の答えですが、エッジケースn = -2147483648(最小値)に注意してください。abs(n)その場合はできません。結果は未定義(または例外)になります。
カークブロードハースト

1
@ a1kmm:すみません、上の-2³²は-2³¹であるはずです。とにかく、f(0)≠0(およびf(0)=-2³¹)のケースは、実際には簡単なケースです。考慮する必要があるもう1つのケースは、f(0)= 0ですが、一部のx≠0、x≠-2³¹ではf(x)=-2³¹です。その場合、f(-2³¹)= f(f(x))=-x(このようなxが存在しないため、-xを-2³¹にすることはできません)。さらに、f(-x)= yとします。次に、f(y)= f(f(-x))= xです。この場合も、yを-2³¹にすることはできません(f(y)= xと同じですが、f(-2³¹)=-xであり、xは0ではありません)。したがって、-2³¹= f(x)= f(f(y))=-yであり、不可能です。したがって、0と-2³¹は、他のものから切り離す必要があります(他のイメージではありません)。
ShreevatsaR 2013年

1
@will 2の補数の32ビット整数について話している場合(想定どおり)、符号付きゼロはありません。
goffrie 2013年

146

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));
}

4
残念ながら、名前のマングリングのため、 "f"を呼び出す関数は実際には奇妙な名前を持っています。
ピョン2009

1
私はそのようなことを考えましたが、Cで考えると、これは捨てられました...よくできました!
Liran Orevi 2009年

@Rui Craverio:作成者が変数名としてvarキーワードを使用することを選択したため、.NET 3.5以降では機能しません。
クレデンス2009年

72
技術的には...これは質問が要求するものではありません。2つのf()関数、f(int)とf(float)を定義し、質問に「関数f()を設計する...」と
尋ねる

2
@elcuco技術的にはもちろんですが、論理的には複数のオーバーロードを持つ1つの関数です(これでf(f(42))を実行できます)。定義はパラメータや戻り値について何も述べていないため、技術的な定義として受け入れることはほとんどできません。
Marek Toman 2013年

135

または、プリプロセッサを悪用することもできます。

#define f(n) (f##n)
#define ff(n) -n

int main()
{
  int n = -42;
  cout << "f(f(" << n << ")) = " << f(f(n)) << endl;
}

それで、あなたはコンラッド「ル・シフレ」ルドルフになるでしょうか?コートを着ます。ええ、私は「void main」の全体について知っていますが、「return 0;」を追加します。非常に多くの努力が必要です;-)
Skizz 2009

25
@Skizz、メインから0を返すことは、戻り値がintの場合でもc ++では必要ありません...したがって、正しく実行することで、実際に入力する文字が1つ少なくなります。
Dan Olson、

10
Skizzは常にプリプロセッサを悪用します:D
Arnis Lapsa 09

23
これは関数ではありません。したがって、これは有効な解決策ではありません
smerlin

3
@smerlin:技術的にはインライン関数を返すインライン関数です。両方の本体がコンパイル時に、または直前に展開されます。これより効率を上げることはできません。
ジョンパーディ2010

103

これは、すべての負の数に当てはまります。

    f(n)= abs(n)

2の補数整数の正の数よりも負の数が1つ多いため、は、と同じである解f(n) = abs(n)よりも1つ多いケースで有効です。一つずつ手に入れました...:Df(n) = n > 0 ? -n : nf(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 : -nuncheckedブロックに計算を含める必要があります。その後、あなたは得る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^324の倍数)に簡単に分割し、条件を満たす関数を見つけました。

しかし、ゼロに関する問題があります。0 => x => 0ゼロはそれ自体に否定されるので、サイクルは含まれている必要があります。そして、サイクルの状態はすでに0 => x続いているため0 => x => 0 => xです。これは長さ2のサイクルにすぎず、x2回の適用後にそれ自体に変わり、ではありません-x。幸いにも、問題を解決する1つのケースがあります。場合X等号がゼロ我々は、ゼロを含む長さ1の周期を求めるとゼロの固定点であることを、我々はこの問題の結論を解きますf

できた?ほとんど。我々は持っている2^32ゼロを残して固定小数点で、数字を2^32 - 1数字を、私たちは4つの数字のサイクルにその数を分割する必要があります。2^32 - 14の倍数ではない悪い-長さ4のどのサイクルにもない3つの数値が残ります。

からまでの3ビットの符号付きイテガーの小さいセットを使用して、ソリューションの残りの部分を説明-4+3ます。ゼロで終わりました。1つの完全なサイクルがあり+1 => -2 => -1 => +2 => +1ます。次に、から始まるサイクルを作成し+3ます。

    +3 => -4 => -3 => +4 => +3

発生する問題は、+43ビット整数として表現できないことです。当社は、取得します+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つの固定点である0Int.MinValueそれ自身と任意の二つの整数にマップされなければならないxし、-xその2つの機能のアプリケーションによって相互にマップされなければなりません。

にマッピングxする-x、またはその逆を行うには、4つのサイクルを形成し、そのサイクルの反対側のコーナーに配置する必要があります。結果として0Int.MinValue反対側のコーナーにもいる必要があります。これは正しくマップされますx-xが、2つの固定ポイントを交換0し、Int.MinValue2つの機能のアプリケーションの後と2つの失敗の入力を私たちに残して。したがって、すべての値に対して機能する関数を作成することはできませんが、1つを除くすべての値に対して機能する関数があり、これが実現可能な最高の方法です。


基準を満たしていません:abs(abs(n))!= -n
Dan Olson

確かに、彼が言ったように、すべての負の数についてはそうです。それは問題の一部でした。一般的なものを思いつくことができない場合は、可能な限り広い範囲で機能するものを考え出します。
2009

この回答は、少なくともMarj SynowiecとRowland Shawの回答と同じくらい優れています。異なる範囲の数値に対してのみ機能します
1800情報

19
おい、あなたは「更新」を取り除いて、1つのまとまりのある正しい答えを書くだけでもよい。下の3/4(「別の答えに触発された」)は素晴らしいです。
Andres Jaan Tack

1
負の数のabs-solutionが本当に好きです。シンプルでわかりやすい。
–ThorbjørnRavn Andersen、2011

97

複素数を使用すると、数値を否定するタスクを2つのステップに効果的に分割できます。

  • nにiを乗算すると、n * iが得られます。これは、反時計回りに90度回転したnです。
  • もう一度iを掛けると、-nが得られます

優れた点は、特別な処理コードを必要としないことです。iを掛けるだけで十分です。

ただし、複素数を使用することはできません。したがって、データ範囲の一部を使用して、なんとかして独自の仮想軸を作成する必要があります。初期値とまったく同じ数の虚数(中間)値が必要なので、データ範囲は半分しか残りません。

符号付き8ビットデータを想定して、次の図でこれを視覚化してみました。これを32ビット整数にスケーリングする必要があります。初期nの許容範囲は-64〜+63です。正のnに対して関数が行うことは次のとおりです。

  • nが0..63(初期範囲)にある場合、関数呼び出しは64を追加し、nを64..127(中間範囲)の範囲にマッピングします。
  • nが64..127(中間範囲)にある場合、関数は64からnを減算し、nを範囲0 ..- 63にマッピングします

負のnの場合、関数は中間範囲-65 ..- 128を使用します。

代替テキスト


4
@geschema、素晴らしいグラフィックを作成するためにどのツールを使用しましたか?
jwfearn 2009

10
申し訳ありませんが、質問では、複素数は明確に示されていません。
Rui Craveiro

6
@Liran:私はOmniGraffle(Macのみ)を使用しました
geschema 2009

39
+1これが最良の答えだと思います。複素数は使用できないとの質問に全員が言及したので、人々は十分に読んだとは思わない。私はすべてを読み、あなたが尋ねた質問に非複雑なソリューションのステージを設定するために、ソリューションを複素数で説明しました。とてもよくできました。
jrista 2009

1
@jristaすべてのソリューションは、2番目の次元を使用します。これは、「複素数」が実際にそうであるすべてです(ほとんどは奇数と偶数を使用し、上記はfloatvsを使用しますint)。多くの回答が説明する「4要素リング」は、4つの状態を必要とします。これは、2つの状態を持つ2つの次元として表すことができます。この回答の問題は、追加の処理スペースが必要で(-64..63の場合は「機能する」だけですが、-128..127のスペースが必要です)、書かれた式を明示的に述べていないことです。
カークBroadhurst

65

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));
    }

絵の


これが反対投票された理由がわかりません。どの入力に対して失敗しますか?
Rodrick Chapman

signum関数を使用しませんか?!?
コモナード

1
画像は本当に良いです。送信0する0-2147483648-2147483648、否定演算子のためのこれらの二つの数が固定されているので、点x => -x。残りの数値については、上の画像の矢印に従ってください。SurDinの答えとそのコメントから明らかなように、この場合には、二つの数があるでしょう2147483647し、-2147483647とのスワップに委ねていない他のペアで。
Jeppe Stig Nielsen

それはスマイリーのように見えます-しわがたくさん
Anshul

48

質問は、入力タイプと関数の戻り値は何について何も言うことはありません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は真になります。

明らかに、このアプローチは、さらに広い範囲の数値で機能するように拡張できます...


2
卑劣。文字数制限。
ジョーフィリップス

2
ええ、私は同様のアプローチに取り組んでいました。あなたは私にそれを打ち負かした。+1 :)
jalf 2009

1
非常に賢い!これは、複素数を使用するのに非常に近い(そして事実上同じ)です。これは明白で理想的な解決策ですが、明示的に禁止されています。許容範囲外の作業。
カークブロードハースト

48

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;
}

後者は、「a」(単数)関数の要件を意味するものではありません。:)
ドリュー

解答の後半を削除してください。これが正解です。
jmucchiello 2009

@Drewだからこそ、ルールを破る可能性があると述べた
cobbal 2009

2
JavaScriptでは、関数はオブジェクトなので、状態を保持できます。
Nosredna

1
IMO:関数f(n){return n.passed?-n.val:{val:n、pass:1}}の方が読みやすく、短いです。
SamGoody 2013年

46

プラットフォームによっては、一部の言語で関数の状態を維持できます。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)。ただし、これまでに提案されている他の機能よりもはるかに優れています。


1
MAX_INTは常に奇妙なので、これはうまくいくと思います。MIN_INTと-1では機能しません。
Airsource Ltd

9
副作用がある場合、関数ではありません。
番号

12
それは数学では正しいかもしれませんが、プログラミングには無関係です。したがって問題は、彼らが数学的なソリューションを探しているのか、プログラミングソリューションを探しているのかということです。しかし、それはプログラミングの仕事のためだということを考えると
Ryan Lundy

+1出力を否定してFIFOを実装する "static int x"をCで投稿しようとしました。しかし、これで十分です。
phkahler、2010

2
@nos:はい、そうです、それは参照的に透過的ではありません。
クラークゲーベル、

26

JavaScriptの例外を悪用します。

function f(n) {
    try {
        return n();
    }
    catch(e) { 
        return function() { return -n; };
    }
}

f(f(0)) => 0

f(f(1)) => -1


以前はこのように例外が使用されていたのではないかと思います... :)
NoBugs 2013

+1箱から出して考える。涼しい!しかし、プロダクションコードでは、安全のためにtypeofを使用します。

21

すべての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

21

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;
}

良いアイデア。別の方法として、おそらく構造体を失い、代わりに1つの関数がポインターを返し、他の関数が逆参照して否定する可能性があります。
Imbue 2009

20

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(アンダーフロー)です。


19

グローバルを使用していますが、そうですか?

bool done = false
f(int n)
{
  int out = n;
  if(!done)
  {  
      out = n * -1;
      done = true;
   }
   return out;
}

3
これが質問者の意図であるかどうかはわかりませんが、「箱から出して考える」ことの+1です。
Liran Orevi 2009年

5
条件付きで「done = true」と言う代わりに、常に「done =!done」と言う必要があります。これにより、関数を複数回使用できます。
Chris Lutz、

@ Chris、doneをtrueに設定することはif(!done)ブロックの内部にあるため、done =!doneと同等ですが、!doneを計算する必要はありません(または十分に賢い場合はコンパイラーで最適化する必要はありません)。 。
nsayer 2010

1
私の最初の考えは、グローバル変数を使用してこれを解決することでもありましたが、この特定の質問をだますようなものでした。しかし、問題の仕様を考えると、グローバル変数ソリューションが最良のソリューションであると私は主張します。グローバルを使用すると、何が起こっているのかを非常に簡単に理解できます。でも、done!= doneの方が良いことに同意します。それをif句の外に移動するだけです。
Alderath、2007

3
技術的には、状態を維持するものはすべて関数ではなく状態マシンです。定義、関数は常に同じ入力に対して同じ出力を提供します。
Ted Hopp、2011

19

この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

しかし、それはそれをintに保ちません。あなたは本質的にグローバル変数データをint "n"自体に格納しています...それがintでない場合を除いて、そうすることができませんでした。たとえばn、文字列の場合、548を "First_Time_548"にして、次に関数を実行するときに... if(prefix == First_Time_ ")で" First_Time_ "を"-"に置き換える
Albert Renshaw

@AlbertRenshawあなたがそれらのアイデアをどこで手に入れるかはわかりません。(1)ここに含まれるグローバル変数は絶対にありません。(2)関数にintを指定すると、intが返されます。関数を奇数回呼び出すと、intへの参照が返されます。(3)おそらく最も基本的には、これはPerlです。すべての実用的な目的で、intとstringは完全に交換可能です。数字のように見える文字列は、ほとんどのコンテキストで数字として完全に機能し、数字は要求されたときに喜んで文字列化します。
FMc 2014

申し訳ありませんが、私は多くのPerlを知らない、あなたがグローバル配列笑使用していたように見えた
アルバート・レンショウ

18

f(x)が同じ型である必要があるとは誰も言っていません。

def f(x):
    if type(x) == list:
        return -x[0]
    return [x]


f(2) => [2]
f(f(2)) => -2

16

私は実際には問題自体に解決策を提供しようとはしていませんが、この問題が引き起こされたのは(ジョブ?)インタビューの一部であるとの質問があるので、いくつかコメントがあります:

  • 私は最初に「なぜそのような関数が必要なのでしょうか?これが含まれているより大きな問題は何ですか?」と尋ねます。実際に提起された問題をその場で解決しようとする代わりに。これは、私がどのように考え、このような問題に取り組むかを示しています。誰が知っていますか?それがそもそもインタビューで質問される実際の理由かもしれません。答えが「気にしないで、必要だと思い込んで、この関数をどのように設計するかを教えてください」の場合。それから私はそうし続けます。
  • 次に、使用するC#テストケースコードを記述します(明らかなのは、ループfrom int.MinValueからint.MaxValuenその範囲の呼び出しごとにf(f(n))、結果のチェックはです-n)。その後、テスト駆動開発を使用してそのような関数に到達することを伝えます。
  • インタビュアーが提起された問題を解決するように私に求め続けた場合のみ、私は実際にインタビュー中に疑似コードを落書きして、ある種の答えを得ようとします。しかし、面接担当者が会社の様子を教えてくれれば、私はすぐに就職するつもりはないと思います...

ああ、この回答は、インタビューがC#プログラミング関連の職位であったと想定しています。もちろん、面接が数学に関連する立場であった場合、ばかげた答えになるでしょう。;-)


7
彼らが32 intを要求したのは幸運です。64ビットの場合、テストを実行した後もインタビューは続行されません;-)
alex2k8

確かに、私が実際にそのテストを書き、面接中にそれを実行するようになるまでなら。;-)私の要点:面接ではそのポイントにまったく到達しないようにします。私の考えでは、プログラミングは「彼がコード行をどのように書くのか」というよりも「考え方」に近いものです。
peSHIr 2009

7
実際の面接ではこのアドバイスに従わないでください。インタビュアーは、あなたが実際に質問に答えることを期待しています。質問の関連性を問うことで何も買うことはありませんが、面接官を困らせるかもしれません。簡単なテストを設計しても、回答に近づくステップはありません。また、インタビューでそれを実行することもできません。追加情報(32ビット)を取得する場合は、それがどのように役立つかを考えてみてください。
Stefan Haustein 2013年

詳細について質問したときにイライラするインタビュアー(おそらく、プロセスでの彼の質問の関連性について質問しているかもしれません)は、必ずしも一緒に/一緒に働きたいインタビュアーではありません。だからインタビューではそういう質問をしていきます。彼らが気に入らない場合は、面接を終了して、両方の時間を無駄にしないようにするでしょう。「私は注文に従うだけだった」という考え方が嫌いです。あなたは..?
peSHIr 2013

16

上位2ビットを変更します。

00.... => 01.... => 10.....

01.... => 10.... => 11.....

10.... => 11.... => 00.....

11.... => 00.... => 01.....

ご覧のとおり、これは単なる追加であり、持ち運び用のビットは省かれています。

どうすれば答えがわかりますか?私の最初の考えは、対称性の必要性だけでした。私が始めたところに戻るには4ターン。最初は2ビットのグレイコードだと思った。次に、実際には標準バイナリで十分だと思いました。


このアプローチの問題は、2の補数の負の数(最近のすべてのCPUが使用するもの)では機能しないことです。そのため、同じ答えを削除しました。
Tamas Czinege 2009年

質問では、32ビットの符号付き整数を指定しました。このソリューションは、32ビット符号付き整数の2の補数表現または1の補数表現では機能しません。これは、(浮動小数点数以外の)最近のコンピューターでは非常に一般的ではない、符号と大きさの表現でのみ機能します。
Jeffrey L Whitledge、2009年

1
@DrJokepu-うわー、6か月後-ジンクス!
Jeffrey L Whitledge、2009年

数値を関数内で符号と大きさの表現に変換し、変換を実行してから、それを返す前にネイティブの整数表現に変換する必要があるだけではありませんか?
ビル・ミシェル

基本的には、虚数ビットを導入して複素数を実装したのが好きです:)
jabirali

16

これは、この問題を解決するために複素数を使用できないという要件または主張に触発されたソリューションです。

-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ビット整数が必要になります。[-231-2, 231-2]f(x)


13

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

これは1073741824であるf(0)でも機能しません。f(1073741824)=0。f(f(1073741824))= 1073741824
Dinah

一般に、任意のビットサイズの2の補数整数型の場合、関数少なくとも2つの入力値に対して機能する必要がないことを証明できます。
怠け者

12

基本的に、この関数は、利用可能な範囲をサイズ4のサイクルに分割する必要があります。nのサイクルの反対側に-nを付けます。ただし、0はサイズ1のサイクルの一部である必要があります。0->x->0->x != -x。0だけであるため、4つの要素を持つ適切なサイクルではなく、範囲内に他の3つの値(サイズは4の倍数)が存在する必要があります。

私はあることをこれらの余分な奇妙な値を選んだMIN_INTMAX_INTMIN_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)

12

無国籍である必要があるとは誰も言っていない。

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の場合にはうまく機能しませんが、より広い型を返すことが許可されていない限り、それについてできることはほとんどありません。


アドレスを確認するために「アップグレード」できます(はい、ポインタとしてref \を使用して取得する必要があります)-Cの場合、たとえば、次のようになります。int f(int&n){static int * addr =&n; if(addr ==&n){return -n; } nを返します。}
IUnknownPointer 2010年

11

31番目のビットを虚数(i)ビットとして使用することは、合計範囲の半分をサポートするアプローチになると想像できます。


これはより複雑になりますが、現在のベストアンサーより効果的ではありません
1800情報

1
@ 1800情報:一方、ドメイン[-2 ^ 30 + 1、2 ^ 30-1]は隣接しており、数学的な観点から見るとより魅力的です。
Jochen Walter、

10

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
}

10

問題は「32ビットの符号付き整数」と述べていますが、それらが2の補数1の補数かを指定していません。

1の補数を使用する場合、すべての2 ^ 32値は長さ4のサイクルで発生します-ゼロの特別なケースは必要なく、条件も必要ありません。

C:

int32_t f(int32_t x)
{
  return (((x & 0xFFFFU) << 16) | ((x & 0xFFFF0000U) >> 16)) ^ 0xFFFFU;
}

これは

  1. 上位と下位の16ビットブロックの交換
  2. ブロックの1つを反転

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)

1
異なるアーキテクチャ間のバイトオーダーはどうですか?
スティーブン

1
すべての演算は32ビットです。私は個々のバイトを操作しないので、バイト順はそれに影響しません。
finnw 2009

これはかなり近いですね。入力は2の補数であると想定できます。したがって、符号ビット表現に変換します。最後のビットに応じて、最初のビットと最後のビット、または最後のビットだけを反転します。基本的に、偶数のみを否定し、偶数/奇数を常に循環させます。したがって、2回の呼び出しの後、奇数から奇数、偶数に戻ります。最後に、2の補数に変換します。このためのコードを以下のどこかに掲載しました。
Stefan Haustein 2013年

9

:D

boolean inner = true;

int f(int input) {
   if(inner) {
      inner = false;
      return input;
   } else {
      inner = true;
      return -input;
   }
}

5
また、グローバル変数がインタビューからあなたを追い出さない場合、なぜグローバル変数が悪いのかについての議論も得られるかもしれません!
palswim 2010


7

数学者としてのこの興味深い問題についての私の見解を共有したいと思います。私は最も効率的な解決策を持っていると思います。

私が正しく覚えている場合は、最初のビットを反転するだけで、符号付き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の平方根による乗算を思い出させるはずです。

長いプレリュードなしで疑似コードだけを提示した場合、それは帽子をかぶったウサギのように思えるので、私はどのようにして解決策を得たかを説明したいと思いました。


6
はい、それは興味深い問題です。あなたはあなたの数学を知っています。しかし、これはコンピュータサイエンスの問題です。だからあなたはコンピュータを研究する必要があります。符号の大きさの表現は許容されますが、約60年前に古くなりました。2の補数が最も人気があります。
Windowsプログラマ

5
2回適用した場合の関数の2ビットに対する処理は次のとおりです。(a、b)->(-b、a)->(-a、-b)。しかし、私たちは(-a、-b)ではなく(-a、b)に到達しようとしています。
ブチオキサ

@ buti-oxa、あなたは正しい。2ビット操作は次のようになります:00-> 01-> 10-> 11->00。しかし、私のプログラマーは、現在は人気のない符号の大きさの表現を想定しているため、Windowsプログラマーは言ったので、私のアルゴリズムはほとんど役に立たないと思います。
Yoo

それで、彼は一回ではなく二回だけステップを行うことができないのでしょうか?
Nosredna

4
buti-oxaは完全に正しいです。この関数は、2回の呼び出し後に最初のビットを反転することすらせず、最初の2ビットを反転します。すべてのビットを反転することは、2の補数が行うことにより近いですが、正確ではありません。
redtuna
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.