できそうでできない - 後だしジャンケンの心理学

生活はすべて次の二つから成り立っている。
したいけど、できない。できるけど、したくない。
ゲーテ -『格言と反省
ちょっと前に、たまたま見たテレビ番組の中で「できそうでできないこと」というコーナーがありました。そこで取り上げられた「できそうでできないこと」の一つが
  • 「後出しジャンケンで負ける」
というものでした。

これくらい簡単にできそうな気はします。しかし、やってみるとこれが意外にできない。勿論、しばらく冷静に考えると分かるのですが、「ジャンケン、ポン、ポン」くらいのタイミングでやろうとすると、これが意外とうまくいきません。あいこになったり、勝ってしまったり。まさに「できそうでできない」のです。

勿論、ジャンケンのルールは誰でも知っているので、冷静に考えれば負けられるはずです。それに、後だしジャンケンで勝つのは割合簡単です。なのに、なぜか負けろと言われると難しい。分かってるはずなのに。何故でしょう?

ジャンケンの論理

ジャンケンでは、「グー」と「チョキ」と「パー」の三者間に循環型の関係があります。「グー」は「チョキ」より強いけれど、「パー」より弱い。これを記号で書くと次のようになるでしょうか。
G < P, P < C, C < G ... (1)
ここで、書いた不等号「<」は、一般的な意味ではなく「A < B」とした時にAよりBが強いということを示す記号として使っています。G, C, Pはそれぞれ「グー」「チョキ」「パー」です。

ジャンケンに勝つ方程式

さて、後だしジャンケンの時、人は先ほどのような関係を思い浮かべているでしょうか。おそらくそうではないでしょう。このような関係を一々考えていては、とっさに後だしできません(^^;)。では、後だしジャンケンでとっさに勝ち手が出てくる時はどうしているのでしょう。

ある入力に対して、正しい出力を導き出す場合、入力と目的の出力の間の関係が明確であれば、入力をそのルール或は関数に当てはめることによって、目的の出力が得られます。「y = f (x)」<という関係です。
しかし、この関係式fはいつも既知とは限りません。例えば、音声認識や手書き文字認識のようなパターン認識問題から、野球のバッティングからバスケットボールのパスのようなスポーツの問題等。このような明確な関係式が存在しないものに関しては、人は今までのパターンからルールを導き出します。ニューラルネットワークは、神経回路網の模してこのような系をモデル化したものです。

元のジャンケンの話に戻します。後だしジャンケンの時、人は(1)のような関係で出す手を考えるのではなく、おそらく入力に対して勝つ手を思い浮かべる関数のようなものを使っているのです。つまり、
f(G) = P, f(P) = C, f(C) = G ... (2)
となる関数fです。後だしジャンケンで勝とうとするときは、おそらくこの関数のような思考過程をたどっているのではないでしょうか。3者の関係から推論を積み重ねるわけではないので、答えはすぐに出せます。人はジャンケンの経験から、このような関係式を獲得しているのでしょう。

負けるための方程式

4873112710では、後出しジャンケンでなぜ負けれないのかという点に戻ります。おそらく「後だしジャンケンで負けて下さい」と言われた時、大抵の人は、負け手を算出するための関数を持っていません。しかし、勝ち手を算出する関数はある。そこで、勝ち手を算出する式から、逆変換をしようと試みます。しかし、逆変換は難しい。そのため、とっさに手を出そうとすると、計算途中の結果である勝ち手がでてしまうのではないでしょうか。そのため、ランダムに手を出したときの負け確率(1/3)以上にすら負けられないのではと。
よって、勝つための方程式を一旦忘れて、負けるための方程式(グーならチョキを出そうといったもの)を頭に置いて考えれば、簡単に負けられるはずです。

と、分析のような話を書いてみましたが、特に何の裏づけもない単なる私の推測です(^^;)。でも、まぁ「当たらずとも遠からず」くらいかな、と。

ちなみに、私が見た番組では他にも「できそうでできない」ことを取り上げていました。せっかくのなので紹介しておきます。
  • スキップの途中で一旦止まって(この時両足とも地面につける)、その姿勢からスキップを再開。
  • 左手の指を、右の耳の穴に命中させる
これもなかなか難しいです(^^;)。
他にもこの手のネタをご存知の方があれば、コメントやTBでぜひ教えてください。

【関連記事】
プログラミング言語と思考
過学習と局所解

【関連書籍】
Mind Hacks―実験で知る脳と心のシステム Tom Stafford
脳のなかの幽霊、ふたたび 見えてきた心のしくみ V・S・ラマチャンドラン
心のパターン―言語の認知科学入門 レイ・ジャッケンドフ
認知パターン―オブジェクト技術のための問題解決フレームワーク ロバート・コニツァー 他
学習とニューラルネットワーク 熊沢逸夫
もっと脳を鍛える大人のDSトレーニング (NINTENDO DS用ソフト)

ビットシフトの落とし穴 - 算術シフトと論理シフト

「不定だ・・・。犀川創平君。君の方程式の解は、今や不定だ」
C言語には、ビットシフト演算子というものがあります。左シフト演算子(<<)と右シフト演算子(>>)です。同じビット演算でも、ビット単位の論理和(|)や論理積(&)、NOT(~)等はの方は、フラグ型の変数の処理で使われる事が多い気がしますが、ビットシフトの方は使用されるケースはあまりないかもしれません。

さて、このビットシフト演算子で時々問題になるのが、符号ビットが立っている時の右シフト演算です。見逃されがちなポイントは、
  • 型によって挙動(算術シフトか論理シフトか)がかわることがある
  • C言語の規格として、算術シフトか論理シフトかは不定
  • Nbitの算術シフトと2のN乗での除算は等価ではない
といったところにあります。
算術シフト(shift arithmetic)と論理シフト(shift logical:又は0充填シフト)という言葉をご存知ない方のためにちょっと説明を書いておくと、シフトによって空いたビット部分を符号ビットを同じもので詰めるのが算術シフト、0で詰めるのが論理シフトです。そのため、それぞれ、符号充填シフト、0充填シフトと呼ばれることもあります。詳しくは、Wikipedia(ビット演算)等を参照してください。

型によって挙動がかわることがある

具体的な例をあげて説明します。次のコードを実行するとどうなるでしょうか。
#include <stdio.h>
int
main()
{
  long sval = 0xffffffff;
  unsigned long uval = 0xffffffff;

  sval >>= 8;
  uval >>= 8;

  printf("%#010x %#010x\n", (unsigned int)sval, (unsigned int)uval);

  return 0;
}

「0x00ffffff 0x00ffffff」という結果を予想される方もいるでしょう。しかし、実際gccでこのソースをコンパイルして実行すると、結果は「0xffffffff 0x00ffffff」となります。同じビットパターンを8bit右シフトしただけなのですが、型が符号付整数(signed)か符号なし整数(unsigned)かによって結果が異なっています。実は、gccではsignedなら算術シフト、unsignedなら論理シフトとなる実装になっているのです。実際、上記コードをコンパイルしたものをobjdumpで逆アセンブルした結果も乗せておきます。

  sval >>= 4;
 80483a6:       8d 45 f8                lea    0xfffffff8(%ebp),%eax
 80483a9:       c1 38 04                sarl   $0x4,(%eax)
  uval >>= 4;
 80483ac:       8d 45 fc                lea    0xfffffffc(%ebp),%eax
 80483af:       c1 28 04                shrl   $0x4,(%eax)

signedの場合は算術右シフト(sarl)命令、unsignedの場合は、論理右シフト命令(shrl)になっているのが確認できます。

このような実装のため、何気なく論理シフトを期待して右シフト処理を行っていると、たまたま変数の型がsignedの場合、思わぬバグの原因になることがあります。

C言語の規格として、算術シフトか論理シフトかは不定

先程の例は、gccでコンパイルした時の話でした。なぜ、わざわざコンパイラを明記したかというと、先ほどの挙動(signedなら算術シフト、unsignedなら論理シフト)は、gccの仕様であって、C言語の仕様ではないのです。残念なことに、C言語の規格としては、右シフト時に算術シフトになるか論理シフトになるかは規定していません。つまり、処理系依存の動作になっています。よって、gccでコンパイルしている限りは、先程の挙動は保障されますが、他の処理系(例えば、全て論理シフトになっている処理系)との互換性は保障されません。大抵の処理系ではgccと同じような実装になっているため、C言語の解説サイトでも、C言語の仕様として書かれているのを見かけることがあります。しかし、保障はないので移植性が必要な場合は注意する必要があります。ちなみに、VC++も、gccと同じ実装です。

使用している処理系の右シフト動作がgccやVC++の動作に依存している場合、次のようなstatic_assertマクロでチェックを入れておくことができます。
#define STATIC_ASSERT(expr) { \
    char __STATIC_ASSERTION[(expr) ? 1 : -1]; \
    (void)__STATIC_ASSERTION; \
  }

/* 符号付き整数の右シフトが算術シフトかどうか */
#define SHIFT_LEFT_SINGNED_USES_SAL \
  (((signed int)0xffffffff >> 1) == 0xffffffff)

/* 符号無し整数の右シフトが論理シフトかどうか */
#define SHIFT_LEFT_UNSIGNED_USES_SHL \
  (((unsigned int)0xffffffff >> 1) == 0x7fffffff)

ソースのどこかで、
STATIC_ASSERT(SHIFT_LEFT_SINGNED_USES_SAL)
のように書いておけば、処理系の動作が期待値と異なる場合にコンパイルエラーになります。
勿論最初から依存しないように書けばいいのですが、条件分岐等を無駄に入れたくない場合には、処理系依存を承知の上でコードを書いて、上記のようなチェックコードを埋めておくというのもひとつの手です。

ちなみに、JavaではCにおける>>演算子に加えて>>>演算子が追加されているので、問題は緩和されていますね。

Nbitの算術シフトと2のN乗での除算は等価ではない

では、gccとVC++は、型によって算術シフトと論理シフトを使い分けているのでしょうか。何もそんなややこしい仕様にしなくてもいいのにと思われる方もいるでしょう。
signedが算術シフトになっている理由とは、その名が示しているように、右シフト演算を整数演算(除算相当)になるようにするためです。具体的には、負数が2の補数で現れている場合、負数に右論理シフトを行うと、値の符号が変化してしまいますが、算術シフトにするとつじつまがあいます。右シフトが2の累乗での除算(例. x >> 1 == x / 2)、左シフトが2の累乗倍(例. x << 1 == x * 2)というルールが負数にも「ほぼ」当てはまるようになります。
ここで、「ほぼ」と書いたのがポイントで、実際は除算の方は「x >> 1 == x / 2」とはいかないことがあります。
C99以前のC言語の場合、負数の整数除算(/演算)は、切捨てになるか切り上げになるかが処理系依存でした。C99では、正負にかかわらず切り捨て(負数であれば、0方向への切り上げ)という規定が入りました。これによって、整数除算に関する移植性の問題はなくなりました。
2の補数で表現された負数の算術右シフトは、切り下げ(-1.5なら-2)です。よって、C99に従った整数除算と、算術右シフトは、切り上げ/切捨てが異なってしまいます。具体的は、「-3 / 2 == -1」「-3 >> 1 == -2」となります。C99以前のCの場合、整数除算の方が処理系依存ですので、一致することもあるかもしれないし、一致しないこともあるかもしれません。(私自身も「ハッカーのたのしみ―本物のプログラマはいかにして問題を解くか」を読んでいて、初めて認識しました :-P)

何かと落とし穴の多い右シフト。使うときは十分注意しましょう。

(追記)
最初この記事を書いた時には、C99以前のC言語では負数の整数除算の扱いが不定ということを知りませんでした(konumaさんに指摘いただきました)。記事を書く前に、gccで動作&アセンブラ確認もしたのですが、それでは処理系依存か否かという点はわかるはずもないですね。今回の教訓です。一度、こういった処理系依存の話についてもまとめてみたいと思います。

4434046683 【関連記事】
コンパイル時の静的チェック (STATIC_ASSERT)
charの落とし穴 - 暗黙の型変換と符号拡張

【関連リンク】
ビット演算 - Wikipedia
シフト演算子 (Microsoft)
シフト演算子 (二流プログラマの三流な日常)
プログラミング言語Cの新機能 (C99に関する詳しい解説)

【関連書籍】
ハッカーのたのしみ―本物のプログラマはいかにして問題を解くか Jr,ヘンリー.S.ウォーレン
Cプログラミングの落とし穴 A.コーニグ
GCC GNU C Compiler―Manual & Reference 遠藤 俊徳
プログラミング言語C ANSI規格準拠 B.W.カーニハン D.M.リッチー

博士の愛した数式 - 数学の美しさ

「だいたい、役に立たないものの方が楽しいじゃないか。音楽だって、芸術だって、何の役にも立たない。最も役に立たないということが、数学が一番人間的で純粋な学問である証拠です。人間だけが役に立たないことを考えられるんですからね」
410401303X 今回は久々に本の感想を。
その本は、小川洋子さんの「博士の愛した数式」。全国書店員が選ぶ第一回本屋大賞受賞作ということでベストセラーになった本です。どこかでこの本のレビューを読んでから、読んでみたいなと思っていたのですが、今回文庫化されていたのを見つけたので買って読んでみました。
知らない人にのために、内容紹介を引用。
記憶が80分しか持続しない天才数学者は、通いの家政婦の「私」と阪神タイガースファンの10歳の息子に、世界が驚きと喜びに満ちていることをたった1つの数式で示した…。頻出する高度な数学的事実の引用が、情緒あふれる物語のトーンを静かに引き締め整える。
記憶障害の数学者のという設定に理系心(?)をくすぐられましたが、内容はAmazonのレビュー等にもあるように「あったかさ」や「優しさ」の溢れる本です。記憶が80分しか持たない「博士」の純粋さと、主人公の息子「ルート」に対する無条件の愛情、数式や数字の美しさ、そこに阪神タイガースへの熱狂がからみあって、とても素敵な雰囲気が出ています。この本を読んで、友愛数完全数オイラーの公式を調べた人も多いと思います。

私自身も数学に美しさを感じたことは何度もあります。まず思い出すのは大学生の時、級数展開で得られたグレゴリーの公式。
π
4
1
1
1
3
1
5
1
7
+ ...
有理数の和から無理数、しかもπが出てくることに、何とも言えない神秘性を感じたものです。思わずプログラムを書いてπを計算とかしてみたり(πは多角形近似とかでもやったなぁ。。)。もっとさかのぼれば、「素数」や「虚数」を初めて知った時も、不思議な魅力を感じました。

よく虚数やsin,cosといった三角関数は、普通に生活する分には役に立たないものの代名詞として使われることがあります(勿論、理工学では大変役に立つのですが)。この本に登場する友愛数や完全数も実生活に役に立つものではありません。しかし、役に立たないものに魅力を感じるのが人間というもの。音楽や美術のように、学校教育でも数学の持つ「美しさ」を伝えるようになれば、もっと数学好きや人も増えるだろうにと思います。「手段」としての数学ではなく、「芸術」としての数学。とりあえずは、この本を中高生にも読んでもらって、数学の美しさや楽しさを感じてもらえたらいいのになと思います。
# ドラゴン桜でも東大志願者が増えるんだし(^^;)。

せっかくなので、美しいなと思った数字を一つ紹介。「ご冗談でしょう、ファインマンさん」という本に登場する、ファインマンが見つけたという数字。
1/243=0.004115226337448559・・・
無限に続く少数の列に不思議な規則性を見出すことができます。

数学の美しさはプログラミングにおける美しさに通じます。同じ証明であっても、簡潔で美しいものを求めるように、プログラミングも動けばいいというものではなく、やはりソースコードに美しさを求めるものです。プログラミングや数学は手段でもありますが、時にはそれ自身目的(楽しみといった方がいいか)であることもあります。

ちなみに私は、吉田武氏の『オイラーの贈物―人類の至宝eiπ=-1を学ぶ』という本と2ヶ月程前に買ったものの、まだ最後まで読了(という表現が正しいのかどうかわかりませんが)していません(^^;)。年末年始の休みの間、ペンと紙を片手に読んでみようかなと思います。

【関連リンク】
美しい数の世界
数学の美しさ

【関連書籍】
博士の愛した数式(文庫本) 小川 洋子
世にも美しい数学入門 藤原 正彦 小川 洋子
放浪の天才数学者エルデシュ ポール・ホフマン
フェルマーの最終定理 - ピュタゴラスに始まり、ワイルズが証明するまで サイモン・シン
ビューティフル・マインド - 天才数学者の絶望と奇跡 シルヴィア・ナサー
オイラーの贈物 - 人類の至宝eiπ=-1を学ぶ 吉田 武
虚数の情緒 - 中学生からの全方位独学法 吉田 武
ある数学者の生涯と弁明 G.H.ハーディ C.P.スノー
プログラマの数学 結城浩
夜のピクニック 恩田陸 (第二回本屋大賞受賞作)
笑わない数学者―MATHEMATICAL GOODBYE 森博嗣 (天王寺博士が魅力的)
人気エントリ
最近の記事
本のおすすめ

4274065979

4844337858

482228493X

4904807057

4873114799


最近のコメント
Links
プロフィール
  • Author:proger
  • 組み込み関係で仕事してます
ブログ内検索