バカラ必勝法の可能性

バカラの統計を分析して遊んでいますが、やっと身になる結果が発見されたかもしれません。バカラには必勝法がある可能性があります。

前回の結果として 0~4 の小さいカードが多いと player に有利で、5~9 の大きいカードが多いと banker に有利という結果を得ました。特定の一枚のカードではなくて、デッキに含まれていると好ましいカードに全体的に偏りがある場合の控除率の変化を調べてみました。

上のグラフはデッキに含まれる 5~9 のカードの割合と控除率の関係を示しています。偏りがない場合には rate = 5 / 13 = 0.38 です。驚くべきことに偏りが大きくなると控除率が 0 を下回る、つまり賭けの期待値が 1 を超える場合があることがわかります。5~9 のカードが 約 60 % を超えると banker に賭けたときの控除率が 0 を下回り、 約 18 % 以下になると player に賭けたときの控除率が 0 を下回ります。これはつまり、バカラでカウンティングが有効なことを示しています。ただ、控除率が負になる場面はかなり稀そうです。

一方で、実益がありそうな知見としては、player, banker の控除率が逆転するのは頻繁にありそうだという点が挙げられます。わずかな偏りで逆転が起きています。バカラは banker に賭けるのがわずかに有利だというニワカ知識だけで banker にだけ賭け続けてると、実際には不利な賭けをしている可能性があります。

さらに、偏りというのは一般に終盤のほうが大きくなります。つまり、デッキのカードが減ってくると最適な賭け方の控除率が減って遊びやすくなってきます。したがって、序盤は少なく賭けて、カードの偏りを見極めながら、終盤に大きく勝負に出るのが、効率的な遊び方だと言えるでしょう。

バカラでカウンティングは有効か

前回 バカラの勝率の計算 - 超ウィザード級ハッカーのたのしみ

バカラにハマったので、バカラを分析して遊んでいます。今回はカードの偏りがバカラの勝敗に与える影響について調べてみましょう。なぜこのようなことをするかというと、カウンティングが有効かどうかを調べるためです。ブラックジャックはカードの偏りの状態によっては期待値が 1 を超えことがあると知られています。カウンティングと呼ばれるテクニックで、デッキの中のカードの偏りの状態を推定するのが、有名なブラックジャックの必勝法です。バカラでも同様の必勝法が有効なのでしょうか?調べてみましょう。

偏りがないときの控除率

house edge
player 0.012350813
banker 0.010579058
banker (no commission) 0.014581045

偏りがないときの控除率は上記のようになります。控除率とは胴元の取り分のことです。ハウスエッジともいいます。1000 円賭けたら 11~15 円程度がカジノに持っていかれます。これを大きいと見るかどうかは価値観の問題ですが、バカラはハウスエッジが 1 程度に対してリターンが -100 か 100 と分散が大きく、ハウスエッジが気にならないぐらいお金が激しく上下します。このギャンブル性の高さが魅力なのでしょう。

no commission と書いているのは、Banker (以下 B とする) が勝ったときの手数料がないノーコミッションルールの場合です。普通のルールの場合は B に賭けるほうがやや有利ですが、ノーコミッションルールの場合は Player (以下 P とする) に賭けるほうが有利です。ノーコミッションルールのほうが遊ぶ側の手間は少ないのですが、控除率が低いので遊ぶなら普通ルールのほうが良いでしょうか。しかし、ルールが複雑な分、抜け道の必勝法があるかもしれませんので、ノーコミッションルールも含めて調べていきましょう。

1つの種類のカードの偏り

まずは1種類のカードの偏りが控除率に与える影響を調べます。今後カウンティングに使うことを目指しますので、デッキから特定のカードを抜いて、ゲームを継続したときの控除率の期待値がどうなるのかを調べます。

8 デッキの 416 枚のカードから X 点のカードを N 枚抜いたときの控除率を考えます。N が大きくなるにつれて、どのように控除率が増減するのかを調べます。0 から 9 まで 10 種類ありますが、一気に結果を貼ってしまいましょう。

0 カード (10, J, Q, K)

0 点のカード 10, J, Q, K は合計 8 x 4 x 4 = 128 枚あります。これらカードを抜いていくと P の控除率が上がり、B の控除率が下がります。一定の偏りがあったとき、ノーコミッションルールでの P と B のハウスエッジが逆転します。これはデッキの状態によっては最適な賭け方が変わることを示唆しています。

0 のカードがとても少ないと、極値を取った後に増加減少の向きが変わります。しかし、そこまで偏ることは珍しいので実用上は単調増加、単調減少として差し支えないでしょう。

1, 2, 3, 4 カード

1 から4のカードは概ね同じなのでまとめて紹介します。0 と同様に、1 から 4 のカードも引かれて、デッキから少なくなると B の控除率が下がり、P の控除率が上がります。

点数が多いカードのほうが、偏りの影響が大きくなるようです。値が大きい方が場に与える影響も大きいからと説明できるでしょう。

5 カード

0 - 4と比べて 5 は傾向が大きく変わります。5 のカード以降はこれまでとは逆に多くひくと、P に有利になっていきます。

6 カード

6 のカードはノーコミッションルールの下では特殊です。6 のカードが少ないと、B が 6 で勝つ場合が減り、B の控除率が減ります。しかし、6 が少ないと P の勝率も増えてしまうよう、6 が少ないからと B に賭けても P に賭ける以上の効果は期待できません。

7, 8, 9 カード

7, 8, 9 のカードも 5 と同様にデッキ内の数が少ないとき P に有利になります。

コミッションありとなしの場合の B の控除率を比べると、7, 8, 9 のカードが少なくなるにつれて、控除率の差が開いていきます。7, 8, 9 のカードが少ないと、7, 8, 9 の有利な点数で勝つ場合が少なくなるからでしょう。ノーコミッションルールの B が、特別有利になる条件は見つけれませんでしたが、逆にもっと不利になってしまう条件はあるようです。

カードごとの控除率の変化

カードの種類ごとの比較がしやすいように、控除率の変化をカードごとに並べました。1枚カードを抜いたときの控除率の変化を示しています。

B か P かどちらかが勝つゲームなので、基本的に B に有利なカードと P に有利なカードは分かれていて、全体的に良くなったり悪くなったりするカードはありません。0~4 の小さいカードを引くと B に有利になり、5~9 の大きいカードを引くと P が有利になります。ただし、ノーコミッションルールのときは 6 だけは引くと全体的に控除率が下がります。

またカードごとに場に与える影響に差があります。値が大きいほうが場に与える影響は大きいが、大きすぎると合計値が10を超えてしまうので、逆効果にもなるようで、中央が膨らんでいます。

1枚のカードの偏りと控除率への影響を調べて、有利なカード、不利なカードが明らかになりました。では 0~4 のカード、5~9 のカードが全体的に偏っている場合に控除率はどうなるのかが気になります。次回はそれを調べてみます。バカラでカウンティングは有効の答えは次回に回します。

バカラの勝率の計算

カジノでバカラを遊んでみて、これはハマる人が多いのも頷けるという感想を持った。ほとんど単純な丁半博打なのですが、徐々に状態が確定していく演出があって、当たったときのフワッと気が舞い上がる感覚がクセになります。次にやったときにもっと楽しめるように、少しバカラの統計を調べて遊んでみようと思う。

今日は1回のゲームでの確率を調べてみます。モンテカルロ法でやるのも芸がないので、頑張って手計算で調べていきましょう。ただバカラのルールは複雑だから、紙と鉛筆だと間違ってしまうので、Python でコードを書きながらやっていきます。

勝率の計算

まずはプレーヤー、バンカーそれぞれの勝率を求めます。プレーヤーが勝つ場合を場合分けして、それぞれの確率を求めて、足し合わせるというトップダウンのアプローチでも計算できますが、今回はいろいろな条件下での確率を計算してみたいので、ボトムアップなアプローチでやってみたいと思います。結局はいずれでも同じことですが。

まず、プレーヤーとバンカーにカードが全て配られて手札が確定した状態のすべての場合のそれぞれの確率を計算してしまいましょう。紙と鉛筆だとかなりつらいですが、計算機上でやっているのでこういうことができます。

手札の状態というか場の状態を表すデータ構造 Hand を定義します。

Hand = namedtuple('Hand', ["player", "banker"])

player フィールドがプレーヤーのカード、banker フィールドがバンカーのカードで、それぞれに配られたカードのタプルが入ります。例えば プレーヤーが A, J, 5 でバンカーが 2, 3, 4 のカードを順に引いたらなら、Hand(player=(1,0,5), banker=(2,3,4)) となります。3枚目を引かない場合は タプルの3つ目は None です。10, J, Q, K を区別する必要はないのでこれらのカードはすべて0で表しましょう。

この Hand 型の値 をキーとして、Hand が示す手札の状態になる確率を p_hand連想配列に入れていきます。

p_hand = dict()

すべてのカードの場合を for ループを回してそれぞれの確率を計算してきます。

# すべての最初2枚の場合について
for p1, p2, b1, b2 in product(list(range(10)), repeat=4):

    # 最初2枚の点数
    p = (p1 + p2) % 10
    b = (b1 + b2) % 10

    # プレーヤー、バンカーが両方とも2枚のとき。
    if p >= 8 or b >= 8 or (6 <= p <= 7 and 6 <= b <= 7):
        hand = Hand(player=(p1,p2,None), banker=(b1,b2,None))
        p_hand[hand] = p_cards(p1,p2,b1,b2)
        continue

    # プレーヤーが3枚目を引く。
    if p <= 5:
        for p3 in range(10):
            # テーブルに従って、バンカーが3枚目を引くかを判断する。
            if HIT_TABLE[b][p3]:
                for b3 in range(10):
                    hand = Hand(player=(p1,p2,p3), banker=(b1,b2,b3))
                    p_hand[hand] = p_cards(p1,p2,p3,b1,b2,b3)
                continue

            # バンカーは3枚目を引かない。
            hand = Hand(player=(p1,p2,p3), banker=(b1,b2,None))
            p_hand[hand] = p_hand[hand] = p_cards(p1,p2,p3,b1,b2)
        continue

    # バンカーのみ3枚目を引く
    for b3 in range(10):
        hand = Hand(player=(p1,p2,None), banker=(b1,b2,b3))
        p_hand[hand] = p_cards(p1,p2,b1,b2,b3)

さて、p_cards は引数のカードを引く確率となります。for ループで場の状態を全て数え上げて、それぞれの状態になる確率を求めていっています。p_cards は以下で計算しました。

def p_cards(*cards):
    """引数に与えられたカードを引く確率"""
    # 8 デッキと仮定する。
    N = 8
    # 0 から 9 のカードの枚数。
    deck = [4 * 13 * N] + [13 * N for i in range(1, 10)]

    p = 1.0
    for c in cards:
        # ほしいカードがなくなった場合は確率0
        if deck[c] <= 0:
            return 0.0
        p *= deck[c] / sum(deck)
        deck[c] -= 1
    return p

8デッキとして、カードの山の中のほしいカードの割当をかけあわせています。カードが尽きた場合は減らさずに0を返します。

2ゲーム目以降はカードが減っているので、以前のゲームの結果を反映すると確率は変わってきます。いわゆるカウンティングは呼ばれる必勝法はこの原理を利用します。しかし、終わったゲームの知識を反映しない立場に立つなら、何回目のゲームでも欲しいカードを引く確率は同じです。そのため、deck を毎回同じ値で初期化しても結果は変わりません。

HIT_TABLE は以下です。ディーラはこれを暗記して間違えないようにしなければならないので、大変ですね。人がやる仕事ではないが、人がやるから面白い。娯楽とはそういうものです。

# 縦が Banker の2枚目までの点数、横がPlayer の3枚目のカード
HIT_TABLE = [
    [1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,1,1],
    [1,1,1,1,1,1,1,1,0,1],
    [0,0,1,1,1,1,1,1,0,0],
    [0,0,0,0,1,1,1,1,0,0],
    [0,0,0,0,0,0,1,1,0,0],
    [0,0,0,0,0,0,0,0,0,0],
]

これで1ゲームが終わったときのすべての状態とその確率を挙げることができました。全部で 339400 通りの状態がありました。

プレーヤ、バンカーそれぞれの勝率、タイとなる確率は、状態群から欲しい場合をフィルターして確率を足し合わせれば計算できます。

def point(hand):
    return sum(card for card in hand if card) % 10

player_win = sum(p for h, p in p_hand.items() if point(h.player) > point(h.banker))
banker_win = sum(p for h, p in p_hand.items() if point(h.player) < point(h.banker))
tie = sum(p for h, p in p_hand.items() if point(h.player) == point(h.banker))

結果以下のような値を得ました。

Winner Probability
Player 0.446177979
Banker 0.458480647
Tie 0.095341374

それっぽい値を得ることができました。サイトによって値が違うのはデッキの数など置いている仮定が違ったりしているのだと思われます。

次に色々な場合の確率を求めていきましょう。

カードを引く枚数

プレーヤの毎数 バンカーの毎数 確率
2 2 0.378750705
3 2 0.185579254
2 3 0.117666050
3 3 0.318003992

4割弱のゲームは最初の2枚で終わります。またバンカーだけ引く場合は少ないようです。感覚にあってますね。

最初2枚が分かったあとの勝率

バカラの醍醐味は、3枚目があとから配られたり、カードを絞ってめくったりして、状態が徐々に確定していくところですよね。最初2枚が確定している状態で、その後の勝率を見ていきましょう。

バンカー、プレーヤーのいずれかの最初2枚が明らかな状態での、もう片方の勝率は以下のとおりとなります。ただし、引き分けの場合は除いています。

バンカーの最初2枚の点数 プレーヤーの勝率 プレーヤーの最初2枚の点数 バンカーの勝率
0 0.699536232 0 0.701654826
1 0.680185869 1 0.695725285
2 0.662796859 2 0.685655013
3 0.645613308 3 0.669359519
4 0.613826427 4 0.642328585
5 0.556476320 5 0.600026889
6 0.497409035 6 0.498438328
7 0.332291914 7 0.332291914
8 0.104645082 8 0.104645082
9 0.000000000 9 0.000000000

基本的に点数が9 に違いほど有利です。相手が9だとすでに負けが確定していて、 8でもほぼ負けです。しかし、相手が 0 であっても、油断はできないことがわかります。絞るときの念じ方が重要だと言えるでしょう。

3枚目以降の逆転の可能性

双方の最初の2枚が確定して、その後に逆転する可能性を調べてみます。

最初2枚のプレーヤーとバンカーの点数差とプレーヤーの勝率の関係は以下のとおりです。2枚目で勝敗が確定している場合と引き分けの場合は除外しています。

点数差 プレーヤーの勝率
-7 0.166794800
-6 0.222154613
-5 0.256331981
-4 0.286572484
-3 0.307923668
-2 0.331206776
-1 0.387202181
0 0.496529573
1 0.595966939
2 0.640109980
3 0.662224521
4 0.689246802
5 0.720494534
6 0.782535047
7 0.833205200

基本的に点数差が大きい方が有利なようです。しかし、3枚目以降のカードで逆転の可能性も残されています。適度に逆転の可能性があって、ゲームバランスがよいと感じました。ただカードが引かれるのを眺めているだけなのですが、このあたりのハラハラドキドキがバカラの魅力だと思います。

答えのない問題に挑む心構え

「答えのない問題」と呼ばれる種類の問題があります。この種類の問題に取り組む仕事は、過重労働のイメージが強い。その理由が問題の構造にあることに気づいたので記録しておく。

まず答えのない問題について説明します。答えのない問題とは、明確な解決策や結論が存在しなかったり、主観性の違いにより一意の答えが存在しない問題のことです。明らかな答えはなくて、人によって言うことが変わるような問題です。例えば広告のクリエイティブだったり、ビジネス上の戦略立案や意思決定に関する問題、投資判断の問題は、答えがない問題です。どんな広告を出したらいいかに唯一の正解はないし、企業の戦略も唯一の正解があるわけではありませんね。

『左利きのエレン』で描かれている広告会社のクリエイティブの人たちとか、コンサルタントの人たちの SNS とか眺めていると、彼らの仕事はハードワークの印象が強い。やりがいが強く夢中になれる仕事だから、ハードワークなのかとも思えるが、それだけではなさそうだ。取り組む問題の種類が、ハードワークを産んでいる気がする。

対比として答えのある問題を考えてみましょう。それは、予め答えが用意されている問題ではなくて、解けたときに解が正解かどうか分かる問題のことをいいます。例えば、PC の使い方が分からなかったから、Google で調べて解決したなどはよくあると思います。簡単な問題ですが、確かに明らかに問題を解決している。そして問題が解決されたので、仕事を終えて家に帰ることができる。もっと難しい例として、作っているプログラムが何かしらの理由で動かないのを挙げましょうか。問題を切り分けていって、原因を突き止めます。そして、解決すれば、家に帰れます。

こういう問題を解決するのは私は好きです。解けたときに快感は代え難いものがあります。もちろん全く手がかりが見つからずに、それで帰れないとなるとしんどいですが、ほとんどの場合解があることはわかってるので、気が楽です。また、専門でずっとやっていることであれば、出会う問題は過去に見たことのある問題の類型が多いので、解決の目処がたたずに途方にくれるなんてことも少ないです。専門家が一定の時間かけて解決できない問題なら、もうそれは解けない問題だと見切ってしまうこともできます。

数学の未解決問題のような解ける見込みがあるかも分からない問題に挑むのは精神的にすり減りますが、普通の仕事だとそんなものには取り組む機会もないので、置いておきましょう。デバッグが下手なジュニアがバグが解決せずに苦しそうにしているのは、その問題が解ける見込みがあると見えてないからで、そのしんどさは未解決問題に挑むのと似ているかもしれないが、それも今回は置いておきましょう。

一方で、答えのない問題は、そもそも問題が解決されたという状態がない。どっちがエラいかとかはないが、異なるルールで評価される問題なのは間違いないです。戦略立案を例にすれば、未来のことなんて分かず、何が正解かなんて知りようがありませんので、それが正しいかどうかって誰も言えないです。明確な答えがないので、どんなものでも答えになりえます。どんな愚策であっても戦略は戦略です。無数にある案を、多角的に検討して最良の案を選ぶのが求められていることです。

正解かどうかが明らかに分からないため、問題への解答の妥当性は解答にいたるプロセスで判断されます。正解かどうかはその時点では誰にも分からないのだから、正しさについては誰も責任をとれない。こんだけやったんだから結果が誤っていても仕方ないよねっていう努力の跡が要求される。それは積み上げられたファクトだったりするかもしれないし、長年鍛えた思考技術かもしれない。

論理的には考えるのでしょうけど、十分に考え抜いたのかどうかは論理ではない。そのため、答えのない問題は、精神論と親和性が高い。答えのない問題は、明確に解けたという状態がないから、どこまでもコネコネやろうと思えばできる。時間と体力が許す限りコネコネと練った案が求められる。高い手数料を取っているアクティブファンドの運用成績が、サルに運用させるよりも劣っていたとしても、誰もサルには運用を任せない。マッキンゼーが予測する未来が合っているなんてだれも思ってないですが、私が予測する未来なんて誰も聞いてくれない。優秀な人が苦しそうに死にそうにやっているのが求められる。

専門家が知識と経験をもとにサクサクっと問題を解決するやり方とは、だいぶ違う。問題が解けたから帰りますってわけにはいかない。答えのない問題は全く異なるルールで行われているゲームです。割に合わないのでようやらないと思いますが、取り組む必要が出てきたら、問題を解くのとはちょっと違うものだと理解して挑むのが実用上は重要ですかね。知らずにやると疲弊すると思います。

あなたの感想が聞きたい

それってあなたの感想ですよね?

このフレーズは、ひろゆきの言葉として有名です。このフレーズだけ聞くと、感想だからだめだというようにも聞こえますが、感想を述べること大切だと私は考えています。

もちろん、ひろゆきが感想を持つべきではないと言っているわけではありません。彼の言いたかったことは、意見と事実の混同を避け、それぞれをきちんと識別しましょうということでした。意見と事実の区別は議論における基本的なスキルで、事実の裏付けがない意見は説得力を欠きます。

しかし、自分の意見を持つことは重要です。意見を持つことは、ポジションを持つとも言います。上司に対して「どうしましょう?」という相談を持ちかけたとき、「君はどうしたいの?」と聞き返されることは皆さんも経験があるのではないでしょうか。それは、自分の意見を表明してほしいというリクエストです。

その一方で、意見を持つことは、しんどいと感じることもあるでしょう。なぜなら自分の意見を持つと、自分自身の責任を伴うからです。意見が自由で、何を言ってもいいという状況では、その表現は楽しいものになります。しかし、発言が影響力を持ち責任が伴う場合には、意見を述べることは勇気を必要とします。それでも、責任をもってポジションを持つことが求められます。

責任を拒む態度は、一般的にはあまり評価されません。「どうしましょう?」とだけ聞いて、全てを上司に任せてしまうような姿勢は叱責の対象になります。また、単に事実を並べただけでは、「それで?」という疑問が返ってくることでしょう。

役職が高い人々は、不確実性の中でも迅速な意思決定を行うため、印象だけを頼りに判断することも多いです。そういった人々に対して「それってあなたの感想ですよね」と問いかけるのはあまり意味をなしません。「そうだ。これが私の意見だ。何か問題でも?」という反応が返ってくることでしょう。彼らは自身の責任をもって意見を述べており、その感想自体が重要です。偉い人の意見でものが決まって、組織が動いていきます。結局のところ、意見こそが行動を生む力なのです。

しかし、これは役職の高い人だけでなく、あらゆる人に当てはまります。責任範囲外の事項について無闇に私見を述べるべきではありませんが、自分の責任範囲において意見を持ち、それに基づいて行動することは、責任の一部といえます。そして行動とは、自分の意見が正しいかどうかを検証することです。何かをすべきだという意見を持ったなら、やってみて、実践を通じて確認するべきです。

「意見」という言葉が重たく感じるのであれば、「仮説」と言い換えてみましょう。手元にあるデータや過去の経験に基づき、最も確からしいことを仮説として提示してくだださい。仮説なので、結果的に誤っていることが明らかになっても大丈夫です。あなたの感想でいいのです。それがあなたの意見、感想であることが重要なのです。自分の感想を持って、行動を起こしましょう。

ChatGPT と主体性

ChatGPTが私の人間に対する価値観を変えている。人より賢いAIが実際に存在するとわかると、どのように人間があるべきか悩む。ChatGPTを使えば、大量のテキストをほとんど手間なく作成できるため、私がわざわざ文章を書く価値はないと思うが、逆にChatGPTに刺激されて、ものを書きたくなるのが不思議だ。

さて、ChatGPTは、主体性がないがとても優秀なアシスタントのようなものだ。言われたことには、必ずすぐに何か返答する。こちらの作業指示が悪くて間違う場合もあるが、フィードバックを与えて修正を促せる。逆に聞いていないことは答えないので、ChatGPTのアウトプットがこちらの想像を超えることはない。

ChatGPTは、私を含めて大半の人より賢いので、人に頼むよりChatGPTと会話している方が有意義だとよく思う。ChatGPTには主体性がなく、報連相ができないため、分からなかったことを「分からない」とは言ってくれない。しかし、必ずすぐに結果を返すので、こちらの指示が悪かったのか、性能の限界を超えているのか、指示を出した人は判断できる。報連相ができなくても、応答が十分に速ければ問題がないと知ったのは、大きな発見だった。

一方で、分からないことを分からないとも言わずに、時間をかけて違うものを人が作ってくると困る。ChatGPTの成果物なら、簡単に捨てられるが、人の成果物を捨てるのには抵抗がある。ChatGPTは、仕事を依頼する側の心理的安全性が高い。

さらに、同等の指示(プロンプト)を与えたときに、人のよりAIの成果物の品質が高いとなると、もはや人は要らないのではないかと考える。わかっていないと思う人にChatGPTを渡して、「まずはこれと相談しろ」と言ったりするが、だったら最初からChatGPTにプロンプトを投げたほうが速くなるのはもう遠くないだろう。

言われたことはやる。言われた以上はやらない。与えられた作業指示の正しさに関しては関知しないし、責任も持たない。いわゆる作業者と言っていいのか、このようなスタンスに立つものは、もうAIで十分だと思う。

主体性、リーダーシップ、イニシアチブ、当事者意識。これらが欠けていると、ますます仕事にならない。

AIが登場する前から、主体性の重要性は語られてきた。*1私の経験からも、主体性の有無がパフォーマンスに強く相関している。あまりに主体性がないと、仕事にならないことは前に述べた

機械ではないのだから、情報を与えずとも、手に入る情報は先に集めておいてほしいし、言わなくても気を利かせて、「これをやっていいですか」と自発的にやってほしい。問題を解決するために必要なデータが欠けていたら、データを取ってきてほしい。

むしろ、AIと競わなくてもいいので、AIを使って仕事をするべきだ。AIにない情報を取ってきて、AIに与えて、AIが出した仮説の真偽を判断するためにまた行動を起こして。行動は人間にしかできない。自分の責任でやるという意識を持って行動しないと、AIは使いこなせない。

主体性を持って行動できないと、AIに駆逐されるだろう。紡績機の発明で糸を紡ぐ人がいなくなったり、自動車の発明で馬車がなくなったり、歴史上仕事が消滅することは何度も起きている。翻訳の仕事がほぼ消滅しつつあるのを私は知っている。私の仕事も時間の問題だ。私は不安で仕方がない。

*1:例えばマッキンゼーの『採用基準』。

間違いを避ける方法

ChatGPT が出てきて、平均値なものに価値がなくなって、外れ値に価値があるか、人の役割はノイズを与えるだとか言われているのを聞くが、そんなことはないと思っている。間違い方は無数にあるが、正しいのやり方はほんの少ししかない。なにかをするときには、無数の選択肢の中から、正しいものを選び取っていかないと、ゴールにたどり着かない。ChatGPT だってプロンプトとして与えられるものは無数にあるが、求める答えが得られるプロンプトはわずかだ。正しい答えを素早く得る能力の価値は ChatGPT があっても変わらない。

もちろん人間なので、たまに道から逸れるだろう。外乱もある。でも、その場合も都度都度修正していけば、目的地にたどり着ける。 ChatGPT の画期的なところは即座にフィードバックを与えて修正ができることだ。

だが、修正がなしに一発で決める方が、何度もフィードバックを繰り返すより、速い。仕事が速い人は、自動車レーサーのライン取りが寸分違わずに正確だったり、将棋指しが最善手を間違いなく指し続けるように、正しい解を誤ることなく選び取っているように見えます。

フィードバックが効く以上に道から逸れるような場合は、系としては不安定になるので、目的地にはたどり着けない。その場合は、仕事が速い遅いとは別次元の話なので、どうしようもないというのは前回に考えたことだった。

目的地にたどり着ける人が、誤った方にいかないようにしていることはある。最も間違いのおかし方に詳しい分野なので、プログラミングを例に挙げてみます。まだ、思いついたのを並べているだけにすぎないので構造化も網羅もされていません。

間違いがあってはならぬと思う。

精神論だけど最も重要だと思う。レビューで見つけてもらおう、テストで見つけようなどと思わず、間違わないように細心の注意を払って一つ一つの作業をする。もちろんレビューやテストもするのだが、その工程でできることはやりきる。トヨタでいうところの自工程完結です。本当はマインドセットに頼らずにメカニズムにしたいが、メカニズムを効かすにもマインドが必要だ。

不安を信じる。

きっと合っていると思っているところでも、誤りはある。いわんや合っているか怪しいと感じるところには必ず間違いがある。不安は当たっている。

根拠のない持論ですが、論理的思考能力なんかより、非論理的な感受性の方が鍛えるのが難しい。言語化できない不安を察知して、言語化したり行動する能力は鍛えた方がいいと思う。

合っているかどうか確信が持てていないものはレビューに出さない。仮に出すとしても、この点には不安がありますといってほしいものです。

自己レビュー

自己レビューは視点を変えてやるのが有効です。例えば、音読してみたり、時間が許せば一晩寝かせたり、ローカルの開発環境ではなくて GitHub に上げたあとに見たり。

類似確認

間違いを少なくするとは、効率的に間違いを探すと同じです。タイポにしろ、勘違いにしろ、同じ間違いはよくあるので、必ず類似確認はする。タイポがあったら、まずは間違えた単語を検索して同じ形の間違いを探す。

パターン認識を活かす。

古いが『間違ったコードは間違って見えるようにする - The Joel on Software Translation Project』という話。A であるべきところが B になっているのを見つけるのは、見れば分かる場合が多い。パターン認識能力を鍛えて、まずは見て分かってほしい。高いレベルになったら、見れば分かるように作る。

型で守る。

一歩進んで、間違ったコードを目視で見つけられるようにするのではなくて、誤っていたらコンパイルが通らないようにする。

安全な言語を使う。

習熟した人が集まらないなどの問題を無視すれば、安全性を高める機能の付いた新しいプログラミング言語を使って解決する問題は多い。

この null チェックはいりませんとか、ここで NPE で落ちる危険性がありますなんて、指摘をしたくはない。

静的解析ツールを使う。

プログラミング言語を変えるのは大変だが、静的解析ツールの導入は簡単だ。チームとしてプロセスに組み込むのも容易だが、ローカルで勝手に実行するのは、もっと容易だ。

互換性を保つために、バージョンが新しくなっても言語仕様やライブラリの仕様は維持される、間違いを生みやすい仕様は残される。静的解析ツールで、一般的に陥りやすい間違いを排除できる。

構造化

A であるべきところが B になっているよりも、あるべきところに A がないのを見つけるのは難しい。構造化されていれば、あるべきところにあるべきものがないと、分かりやすくなる。

ドキュメントには、仕様が構造化して書かれているものだから、ドキュメントにかかれている通りに書けばいい。

間違えやすい書き方をしない。

重複しているのだが、間違いにくいコードの書き方の対として、間違をしやすいコードの書き方がある。あえて間違いを犯しやすい方に進みたくなる引力がどこかにあるのでしょう。

変数の値やオブジェクトの状態を条件に応じて変更してのは、よく見る。最初に返り値を宣言して、何か初期値を入れて、条件に応じて変数を更新していって、最後に返すみたいな。変数のスコープが大きくなっているからか、よく間違っている。

間違いやすい書き方をしても、正しく作りきれることもあるとは思いますが、間違いやすい書き方がわざわざ選ばれている場合は、作りきれていないことが多い。

そもそも書かない。

作ること自体が間違いなのもよくある話です。例えば、HTTP クライアントのライブラリを使わずに、Socket からストリームを読んでパースしてなんてしてはいけない。その選択をしてしまう人に、そんなものが正しく作れるはずがない。