f(f(x)) ==> -x な、実数を返す f を定義せよ…クイズ


http://oss.timedia.co.jp/index.fcgi/kahua-web/show/ossz/oneline/2006-04-17

| ff |
ff := [:xx | xx * 1 i].

^ #(0.2 -0.2 1.2 -1.2) collect: [:rr | (ff value: (ff value: rr)) real]
=> #(-0.2 0.2 -1.2 1.2)


実数を返す関数だからこれじゃダメか…。てへっ。 ウソです。わざとです。ごめんなさい。しかも、ちゃっかり実数部を返しているし。w


でも、一回、虚数に飛ばしてやるというのはヒントになります。f を適用するたびに、虚数に飛ばす代わりになにか“フラグ”になりそうなものを上げ下げして、それに正負の反転を連動させればいいわけです。実数で正負以外でフラグに使えそうなのは整数部の偶奇。|x| < 1.0 でも使えるようにするためには、偶数のときに +1 して奇数に、奇数のときは -1 して偶数にしてやればよい…と。これで、二度 f を適用すると元に値に戻るので、あとは二度の適用のどちらかで正負が逆転するようにしてやればOK。


条件とその際の算出式を、すなおに列挙すると、こんなかんじになりましょうか。

| ff |
ff := [:real |
   real isZero ifTrue: [real] ifFalse: [

      {real > 0. real truncated even} caseOf: {
         [{true. true}] -> [(real + 1) negated].
         [{true. false}] -> [real - 1].
         [{false. true}] -> [(real - 1) negated].
         [{false. false}] -> [real + 1]}]].

^ #(0.2 -0.2 1.2 -1.2) collect: [:rr | ff value: (ff value: rr)]
=> #(-0.2 0.2 -1.2 1.2)


条件分岐を最小限にして、むりやりひとつの式で結果を算出させると、こうなります。

| ff |
ff := [:real |
   real isZero ifTrue: [real] ifFalse: [

      | delta |
      delta := real truncated even ifTrue: [1] ifFalse: [-1].
      (real abs + delta) * real sign * delta negated]].

^ #(0.2 -0.2 1.2 -1.2) collect: [:rr | ff value: (ff value: rr)]
=> #(-0.2 0.2 -1.2 1.2)


delta も計算で出すようにして、0 sign = 0 を活用すれば、条件分岐を(表面上は)排除できます。

| ff |
ff := [:real |

      | delta |
      delta := (real truncated \\ 2 - 0.5) sign negated.
      (real abs + delta) * real sign * delta negated].

^ #(0.2 -0.2 0 1.2 -1.2) collect: [:rr | ff value: (ff value: rr)]
=> #(-0.2 0.2 0 -1.2 1.2)


もっとも誤差があるために 0.0 とかを与えるとダメなので、結局はゼロの判断のための条件分岐は省かず残しておかないといけないのですが…。


id:sumim:20060419:p1 に続く。