※当サイトの一部記事には広告を含みます。
うさねこ気まぐれPG開発室

麻雀の配牌は運が悪いだけ?三麻・四麻のシャンテン数と連続確率を検証

「麻雀で配牌が悪い気がする」「三人麻雀と四人麻雀では何シャンテンが普通?」「1~2シャンテンの良い配牌が何局も続く確率は?」――そんな疑問を、全配牌の組合せに基づく厳密計算で調べました。
三麻108枚・四麻136枚の違い、子13枚・親14枚の違いを分け、シャンテン分布・1局の確率・2~20局の連続確率・計算式を掲載します。

配牌が悪い!

ねこちゃん
ねこちゃん

うさちゃ~ん、麻雀で毎回配牌が悪いんだけど、なんのこれ?!

うさちゃん
うさちゃん

配牌の悪さって、確かに気になるよね~😅
しかも、続く時はずっと悪いような気もするしね。

ねこちゃん
ねこちゃん

「確率問題」に強いうさちゃん、三麻と四麻で、配牌時点で何シャンテンが多いか調べてみて? とりあえず、わかったら起こして……ぼくはもうふて寝する。

うさちゃん
うさちゃん

じゃあ、実際の牌の組合せを全部数えて、確率を見てようか~✨

結論|配牌は3~4シャンテンが多い!

子の初期配牌13枚では、三麻は3シャンテンが最多(39.673731%)、四麻は4シャンテンが最多(39.875815%)です。平均シャンテン数は三麻3.394018、四麻3.579679でした。

三人麻雀108枚と四人麻雀136枚の子13枚配牌シャンテン数分布。数値は独立全数計算の監査結果に基づく
三人麻雀108枚と四人麻雀136枚の子13枚配牌シャンテン数分布。数値は独立全数計算の監査結果に基づく
比較項目三人麻雀(13枚)四人麻雀(13枚)
最も多い配牌3シャンテン4シャンテン
最多の割合39.673731%39.875815%
平均シャンテン数3.3940183.579679
1~2シャンテン14.449974%9.974766%
1~3シャンテン54.123705%46.177847%

「3~4シャンテンがよく来る」のは、理論上ごく自然な分布です。ただし、本人の配牌が本当に偏っているかは、実際の対局ログを集めないと判断できません。

広告
Razer Gigantus V2 – L ゲーミングマウスパッド 布タイプ 中型サイズ 45 cm x 40 cm マイクロウェーブクロス 【日本正規代理店保証品】 RZ02-03330300-R3M1
Razer Gigantus V2 – L ゲーミングマウスパッド 布タイプ 中型サイズ 45 cm x 40 cm マイクロウェーブクロス 【日本正規代理店保証品】 RZ02-03330300-R3M1
【高速かつ正確な操作を実現した設計】 テクスチャ加工を施した布製の表面を採用した、ソフトなゲーミングマウスパッドは、滑らかなスワイプ操作とピクセルレベルの照準精度で快適なゲーム体験を実現。
【4 種類のサイズバリエーション】 4 種類のサイズバリエーションによりさまざまな環境にフィットします。デスク全体をカバーする 3XL サイズは低DPI を使用するユーザーや、大きなマウスパッド上でマウスを操作したいユーザーに最適です。
【テクスチャ加工された布製の表面】 細かな織り目はすべてのマウスセンサーでピクセルレベルの照準精度と追尾性能を発揮させ、抵抗の少ない滑らかな操作感で、思い通りのマウス操作を可能します。
【底面の滑り止めラバーと厚みのある高密度ラバーフォーム】 3mm(M & L)と 4mm(XXL & 3XL)の厚さのパッドは、底面のグリップ力のあるラバー素材により、平坦でない場所や、白熱した状況でも安定したマウスの動きを実現する強力な基盤になるほか、ゲームプレイ時の手首の疲労を軽減してくれます。
【製品サイズ】 450 mm(横) x 400 mm(縦) x 3mm(厚さ)
最新の価格・在庫状況はAmazonでご確認ください。

計算の前提

計算条件三人麻雀四人麻雀
使用牌27種類・108枚34種類・136枚
萬子一萬・九萬のみ一萬~九萬
筒子・索子各1~9各1~9
字牌東南西北白發中東南西北白發中
赤牌通常の5と同じ牌種として集計通常の5と同じ牌種として集計
北抜き北を抜く前の配牌該当なし

今回の計算で対象とする三麻は二萬~八萬の7種類×4枚を使用しないため、四麻とは配牌分布が変わります。一方、北抜きやチー禁止は、北抜き前の手牌13枚・14枚を形として評価する限り、今回の分布を変えません。赤ドラやドラは点数に影響しても、シャンテン数そのものには影響しません。

子は配牌13枚、親は最初の捨て牌を切る前の14枚で別計算です。北抜き後・途中のツモや打牌後・副露後などには、そのまま当てはめられません。

子・確率

通常形(4面子1雀頭)・七対子・国士無双の最小シャンテン数で分類しています。

シャンテン数三麻13枚四麻13枚
0(テンパイ)0.020631%0.008117%
11.181059%0.621400%
213.268915%9.353366%
339.673731%36.203080%
436.815187%39.875815%
58.841940%13.102012%
60.198537%0.836209%
平均3.3940183.579679

三麻は3シャンテン、四麻は4シャンテンの割合が最も高くなります。三麻では二萬~八萬がないため、牌構成の違いが通常形のまとまりやすさにも反映されます。

親・確率

親は第一打前に14枚持っているため、子の13枚とは分布が異なります。
−1は最初から和了形ができている状態で、親の場合は役満「天和(テンホー)」に相当します。
0はテンパイを表します。

シャンテン数三麻14枚四麻14枚
−1(和了形)0.000946%0.000303%
0(テンパイ)0.164024%0.069789%
14.055332%2.332879%
225.110785%19.497762%
343.860027%43.935787%
424.023920%28.514400%
52.775657%5.493765%
60.009308%0.155316%
平均2.9588593.155938

この14枚の表は、連荘した親が毎局最初に受け取る14枚など、各局の第一打前の配牌をモデル化したものです。自分で最適な1枚を捨てた後の13枚、局の途中でツモった14枚を一様な初期14枚と同じ分布として扱うことはできません。

1局で◯シャンテンになる確率

「指定範囲に入る」とは、その範囲に含まれるシャンテン数のどれかになること。
1~2シャンテンにはテンパイ(0)を含みません。

シャンテン範囲三麻13枚四麻13枚三麻14枚四麻14枚
1~214.449974%9.974766%29.166117%21.830640%
1~354.123705%46.177847%73.026144%65.766428%
2~352.942646%45.556447%68.970812%63.433549%

三麻13枚で1~2シャンテンに入る割合は約14.45%、四麻13枚は約9.97%。一方、1~3シャンテンまで広げると三麻は約54.12%、四麻は約46.18%です。

広告
VALVE Steam Deck OLED 1TB SSD + 16GB RAM (International Version) - 7.4" inch, 90Hz, 1280 x 800px, SteamOS 3.0, Handheld Gaming Console
VALVE Steam Deck OLED 1TB SSD + 16GB RAM (International Version) - 7.4" inch, 90Hz, 1280 x 800px, SteamOS 3.0, Handheld Gaming Console
最新の価格・在庫状況はAmazonでご確認ください。

何局も連続する確率(子)

以下は子13枚配牌の連続確率で「指定した連続n局が、すべて同じシャンテン範囲に入る確率」です。
局ごとに牌が一様にシャッフルされ、同じ配牌モデル・同じ成功確率pを用い、各局が独立であるとして計算します。例えば、3シャンテン→2シャンテン→3シャンテンなら「2~3シャンテンが3局連続」に含まれます。

三人麻雀

13枚配牌が連続する確率

連続局数1~2シャンテン1~3シャンテン2~3シャンテン
2局2.088018%29.293755%28.029238%
3局0.301718%15.854865%14.839420%
4局0.043598%8.581241%7.856382%
5局0.006300%4.644485%4.159376%
10局0.0000003968905%0.215712%0.173004%
20局0.000000000000001575221%0.000465%0.000299%

四人麻雀

13枚配牌が連続する確率

連続局数1~2シャンテン1~3シャンテン2~3シャンテン
2局0.994960%21.323935%20.753898%
3局0.099245%9.846934%9.454739%
4局0.009899%4.547102%4.307243%
5局0.000987%2.099754%1.962227%
10局0.000000009750508%0.044090%0.038503%
20局0.0000000000000000009507242%0.000019%0.000015%

5局すべてが該当する確率

指定した5局すべてが1~2、1~3、2~3シャンテンの範囲に入る確率。子13枚・各局独立の理論値
指定した5局すべてが1~2、1~3、2~3シャンテンの範囲に入る確率。子13枚・各局独立の理論値

三麻13枚で1~2シャンテンが5局連続する確率は約0.006300%。四麻13枚なら約0.000987%です。逆に「毎回、何となく悪い」という感覚を評価するには、何シャンテン以上を悪いと呼ぶのかを先に決める必要があります。

何局も連続する確率(親)

こちらも厳密計算結果ですが、連続する全局で14枚の親配牌が与えられるとあらかじめ仮定した理論値です。「実際に親を5局連続で引く確率」や「連荘する確率」を含めた結果ではありません。

三人麻雀

親14枚配牌の連続確率

連続局数1~2シャンテン1~3シャンテン2~3シャンテン
2局8.506624%53.328177%47.569730%
3局2.481052%38.943511%32.809229%
4局0.723626%28.438945%22.628792%
5局0.211054%20.767865%15.607262%
10局0.000445%4.313042%2.435866%
20局0.000000001984140%0.186023%0.059334%

四人麻雀

親14枚配牌の連続確率

連続局数1~2シャンテン1~3シャンテン2~3シャンテン
2局4.765769%43.252230%40.238152%
3局1.040398%28.445447%25.524488%
4局0.227126%18.707554%16.191089%
5局0.049583%12.303290%10.270582%
10局0.000025%1.513709%1.054849%
20局0.000000000006044071%0.022913%0.011127%

計算式と数え上げの根拠

計算モデル・式・独立検証の内容を記載します。

① 配牌の全組合せ数

使用牌総数を T、配牌枚数を h とすると、全配牌の組合せ数は C(T,h) です。牌を配る順序は区別しません。

条件全配牌の組合せ数
三麻13枚C(108,13) = 20,592,957,740,312,160
四麻13枚C(136,13) = 483,774,556,165,488,000
三麻14枚C(108,14) = 139,737,927,523,546,800
四麻14枚C(136,14) = 4,250,305,029,168,216,000

② 牌姿の重みとシャンテン分布

牌種 i を ci 枚含む牌姿の組合せ重みを、次のように定義します。

W(c) = ∏i C(4, ci)

指定のシャンテン数 s に該当する牌姿を重み付きで合計した整数を Ns とすると、

P(S = s) = Ns / C(T,h)

例えば「1~2シャンテン」なら、p = (N1 + N2) / C(T,h)。牌種の枚数ベクトルを同確率で扱うのではなく、物理牌4枚の選び方で重み付けする必要があります。

③役の最小シャンテン

  • 通常形:4面子1雀頭の完成形との差から計算。三麻の一萬と九萬の間に架空の順子を作らない。
  • 七対子:対子のある牌種数をP、手牌内の異なる牌種数をUとして、6 − P + max(0, 7 − U)。4枚同一牌も「異なる対子2組」としては扱わない。
  • 国士無双:異なる么九牌数をK、么九牌の対子があればQ=1、なければQ=0として、13 − K − Q。
  • 最終値:上記3種の最小値。完成形でも同一牌は4枚以内とし、仮想的な5枚目を必要とする形は認めない。

④ 指定したn局が続く確率

指定範囲に入る1局の確率を p とし、同じ配牌モデル・同じpで各局が独立であれば、

P(n局連続) = pn

例えば三麻13枚で1~2シャンテンが5局連続する確率は、次の丸める前の整数比から計算します。

p = 2,975,677,059,969,184 / 20,592,957,740,312,160
p5 ≒ 0.00629992448%

この式は、前後の局を範囲外に限定した「ちょうどn局だけの連続」の確率ではありません。親子が混在する場合は、あらかじめ親子の並びを固定して各局が独立とすれば、各局に対応する確率の積を使います。子がc局、親がd局なら、同じ対象範囲についてp13c × p14dです。実際の親の継続や連荘が起こる確率まで含めた値ではありません。

最初の1局がすでに該当したと分かっている場合、合計n局連続になる条件付き確率は pn−1 です。また100局のどこかで5連続が1回以上起こる確率は、重なる連続区間があるため単純なp5ではありません。

⑤ 100局中「どこかで連続」は別の計算

M局中に少なくとも一度n連続する確率は、末尾が何局連続しているかを状態とする動的計画法で求めます。qt(r)を「t局時点でn連続が未発生で、直近の連続成功数がr」の確率とします。初期値はq0(0)=1、q0(r)=0(1 ≦ r ≦ n−1)で、

qt+1(0) = (1−p) × Σr=0n−1 qt(r)
qt+1(r) = p × qt(r−1) (1 ≦ r ≦ n−1)

と遷移させ、「少なくとも1回発生」= 1 − Σr=0n−1 qM(r) を計算します。今回の連続確率表はこのM局モデルではなく、あらかじめ指定したn局に対するpnの値です。

全数計算の独立検証

牌姿を重み付きで全数集計し、異なるアルゴリズムによる独立全数計算でも結果を検証しています。乱数シミュレーションによる推定値ではありません。

監査項目結果
三麻・四麻×13枚・14枚計32区分の整数組合せ数が完全一致
既存結果との照合304項目、不一致0件
局所距離の検算4,485,050項目、不一致0件
ランダム牌姿による補助照合100,000牌姿、不一致0件
総組合せ数4ケースともC(T,h)と一致

独立検証が確認したのは、使用牌・シャッフル・シャンテン定義を固定した数学モデルでの理論値です。特定のゲームの配牌プログラムや、実際のプレイヤーの対局履歴を解析した検証ではありません。

広告
【Nintendo Switch 2 専用】スーパーマリオブラザーズ ワンダー Nintendo Switch 2 Edition + みんなでリンリンパーク アップグレードパス|オンラインコード版
【Nintendo Switch 2 専用】スーパーマリオブラザーズ ワンダー Nintendo Switch 2 Edition + みんなでリンリンパーク アップグレードパス|オンラインコード版
Nintendo Switch 2 本体が必要です。
Nintendo Switchソフト『スーパーマリオブラザーズ ワンダー』を『スーパーマリオブラザーズ ワンダー Nintendo Switch 2 Edition + みんなでリンリンパーク』にアップグレードするためのパスです。
リンリンパークには家族や友達と遊べるアトラクションが盛りだくさん!
「おすそわけ広場」では、集めたコインの数を競ったり、リズムにあわせてみんなでジャンプをしたり。
「ルームマッチ広場」では、乗り物に乗ってレースをしたり、逃げて隠れての鬼ごっこをしたり。
最新の価格・在庫状況はAmazonでご確認ください。

シャンテン数の厳密計算プログラム

本記事で使用した独立全数計算プログラム(C++)のソースコードを公開します。三麻108枚・四麻136枚について、子13枚・親14枚(第一打前)の全配牌を、通常形・七対子・国士無双の最小シャンテン数で集計しています。

ソースコードは下の項目を開いて確認してください。

C++ソースコード全文を表示する(クリックで開く)
C++17/GCC・Clang対応。コード欄は横スクロールできます。
// 麻雀の初期配牌:シャンテン数別の組合せ数を厳密に全数計算
// 対象:三麻108枚/四麻136枚 × 子13枚/親14枚(第一打前)
// 方法:合法な完成形を列挙し、距離変換と牌種別の組合せ重みで集計
// 作成:AIによる作成支援 

#include <algorithm>
#include <array>
#include <cassert>
#include <chrono>
#include <cstdint>
#include <fstream>
#include <functional>
#include <iostream>
#include <limits>
#include <map>
#include <random>
#include <string>
#include <unordered_map>
#include <vector>

using U = __uint128_t;  // 巨大な組合せ数を扱う128ビット整数
using D = std::array<unsigned char, 10>;
constexpr int INF = 99;

// 128ビット整数を10進数文字列に変換
std::string dec(U x) {
    if (!x) return "0";

    std::string s;
    while (x) {
        s += char('0' + x % 10);
        x /= 10;
    }
    std::reverse(s.begin(), s.end());
    return s;
}

// 組合せ C(n, k)
U choose(int n, int k) {
    if (k < 0 || k > n) return 0;

    U r = 1;
    for (int j = 1; j <= k; j++) {
        r = r * (n - j + 1) / j;
    }
    return r;
}

// 牌グループの状態をまとめるためのシグネチャ
struct Sig {
    D d{};
    int kinds = 0, pairs = 0, term = 0, tp = 0;

    bool operator==(Sig const& b) const {
        return d == b.d && kinds == b.kinds && pairs == b.pairs &&
               term == b.term && tp == b.tp;
    }
};

struct Hash {
    size_t operator()(Sig const& s) const {
        size_t r = 0;
        for (auto v : s.d) r = r * 131 + v;
        for (int v : {s.kinds, s.pairs, s.term, s.tp}) r = r * 131 + v;
        return r;
    }
};

struct Entry {
    Sig sig;
    uint64_t w;
};

// ─────────────────────────────────────────────
// 1. 牌種別の合法な完成形と距離テーブルを作る
//    数牌9種/字牌7種/三麻萬子2種を別々に前計算
// ─────────────────────────────────────────────
struct Table {
    int n, size;
    bool runs, allterm;
    std::vector<int> pow5;
    std::array<std::vector<unsigned char>, 10> dist;
    std::array<std::vector<Entry>, 15> groups;

    Table(int n_, bool runs_, bool term_)
        : n(n_), size(1), runs(runs_), allterm(term_) {

        // 各牌の所持枚数0〜4を5進数で表す
        for (int i = 0; i < n; i++) {
            pow5.push_back(size);
            size *= 5;
        }
        for (auto& a : dist) a.assign(size, INF);

        // 刻子と、許可されている牌種では順子を列挙
        std::vector<std::vector<int>> melds;
        for (int i = 0; i < n; i++) {
            std::vector<int> a(n);
            a[i] = 3;
            melds.push_back(a);
        }
        if (runs) {
            for (int i = 0; i + 2 < n; i++) {
                std::vector<int> a(n);
                a[i] = a[i + 1] = a[i + 2] = 1;
                melds.push_back(a);
            }
        }

        // 4面子+雀頭までの合法な目標形を列挙する
        std::vector<int> a(n);
        std::function<void(int, int)> rec = [&](int start, int m) {
            int code = 0;
            for (int i = 0; i < n; i++) code += a[i] * pow5[i];

            dist[m][code] = 0;
            for (int i = 0; i < n; i++) {
                if (a[i] <= 2) dist[5 + m][code + 2 * pow5[i]] = 0;
            }
            if (m == 4) return;

            for (int j = start; j < (int)melds.size(); j++) {
                bool ok = true;
                for (int i = 0; i < n; i++) {
                    if (a[i] + melds[j][i] > 4) ok = false;
                }
                if (!ok) continue;

                for (int i = 0; i < n; i++) a[i] += melds[j][i];
                rec(j, m + 1);
                for (int i = 0; i < n; i++) a[i] -= melds[j][i];
            }
        };
        rec(0, 0);

        // 足りない牌の枚数を、1牌種ずつ距離変換で求める
        for (int z = 0; z < 10; z++) {
            size_t targets = std::count(dist[z].begin(), dist[z].end(), 0);
            std::cerr << "TARGETS n=" << n << " h=" << z / 5
                      << " m=" << z % 5 << " count=" << targets << "\n";

            // min_y f(y) + Σ max(y_i - c_i, 0) を座標ごとに計算
            for (int step : pow5) {
                for (int base = 0; base < size; base += 5 * step) {
                    for (int off = 0; off < step; off++) {
                        int p = base + off;
                        for (int c = 1; c < 5; c++) {
                            dist[z][p + c * step] = std::min(
                                dist[z][p + c * step],
                                dist[z][p + (c - 1) * step]
                            );
                        }
                        for (int c = 3; c >= 0; c--) {
                            dist[z][p + c * step] = std::min<int>(
                                dist[z][p + c * step],
                                dist[z][p + (c + 1) * step] + 1
                            );
                        }
                    }
                }
            }
        }

        // 物理牌4枚からの選び方 C(4, c) で重み付けする
        std::array<std::unordered_map<Sig, uint64_t, Hash>, 15> agg;
        constexpr uint64_t w[5] = {1, 4, 6, 4, 1};
        size_t vectors = 0;

        for (int code = 0; code < size; code++) {
            Sig s;
            int v = code, total = 0;
            uint64_t weight = 1;

            for (int i = 0; i < n; i++) {
                int c = v % 5;
                v /= 5;
                total += c;
                weight *= w[c];
                s.kinds += c > 0;
                s.pairs += c >= 2;
                if (allterm || i == 0 || i == n - 1) {
                    s.term += c > 0;
                    s.tp += c >= 2;
                }
            }
            if (total > 14) continue;

            for (int z = 0; z < 10; z++) s.d[z] = dist[z][code];
            agg[total][s] += weight;
            vectors++;
        }

        size_t signatures = 0;
        for (int k = 0; k <= 14; k++) {
            U sum = 0;
            for (auto const& [s, wg] : agg[k]) {
                groups[k].push_back({s, wg});
                sum += wg;
            }
            assert(sum == choose(4 * n, k));
            signatures += groups[k].size();
        }
        std::cerr << "LOCAL n=" << n << " valid_vectors=" << vectors
                  << " signatures=" << signatures << "\n";
    }
};

// ─────────────────────────────────────────────
// 2. 牌グループどうしの最小距離を合成する
//    数牌・字牌の対称性による倍率は使用しない
// ─────────────────────────────────────────────
D combine(D const& a, D const& b) {
    D r;
    r.fill(INF);

    for (int ha = 0; ha < 2; ha++) {
        for (int hb = 0; ha + hb < 2; hb++) {
            for (int ma = 0; ma < 5; ma++) {
                for (int mb = 0; ma + mb < 5; mb++) {
                    r[5 * (ha + hb) + ma + mb] = std::min<int>(
                        r[5 * (ha + hb) + ma + mb],
                        a[5 * ha + ma] + b[5 * hb + mb]
                    );
                }
            }
        }
    }
    return r;
}

// ─────────────────────────────────────────────
// 3. 三麻・四麻の13枚/14枚を全数集計
//    distance = シャンテン数 + 1
// ─────────────────────────────────────────────
void count(std::string mode, int h, std::array<Table const*, 4> ts) {
    auto start = std::chrono::steady_clock::now();
    std::array<U, 8> counts{};
    std::array<std::array<U, 15>, 3> shapes{};
    uint64_t products = 0;

    for (int n0 = 0; n0 <= h; n0++) {
        for (int n1 = 0; n0 + n1 <= h; n1++) {
            for (int n2 = 0; n0 + n1 + n2 <= h; n2++) {
                int n3 = h - n0 - n1 - n2;

                for (auto const& a : ts[0]->groups[n0]) {
                    for (auto const& b : ts[1]->groups[n1]) {
                        auto ab = combine(a.sig.d, b.sig.d);

                        for (auto const& c : ts[2]->groups[n2]) {
                            auto abc = combine(ab, c.sig.d);

                            for (auto const& d : ts[3]->groups[n3]) {
                                int normal = INF;
                                for (int head = 0; head <= 1; head++) {
                                    for (int m = 0; m <= 4; m++) {
                                        normal = std::min(
                                            normal,
                                            abc[head * 5 + m] +
                                            d.sig.d[(1 - head) * 5 + 4 - m]
                                        );
                                    }
                                }

                                int kinds = a.sig.kinds + b.sig.kinds +
                                            c.sig.kinds + d.sig.kinds;
                                int pairs = a.sig.pairs + b.sig.pairs +
                                            c.sig.pairs + d.sig.pairs;
                                int term = a.sig.term + b.sig.term +
                                           c.sig.term + d.sig.term;
                                int tp = a.sig.tp + b.sig.tp +
                                         c.sig.tp + d.sig.tp;

                                // 通常形・七対子・国士無双の最小距離
                                int distance = std::min({
                                    normal,
                                    7 - pairs + std::max(0, 7 - kinds),
                                    14 - term - (tp > 0)
                                });
                                assert(distance >= 0 && distance < 8);

                                U weight = U(a.w) * b.w * c.w * d.w;
                                counts[distance] += weight;
                                shapes[0][normal] += weight;
                                shapes[1][7 - pairs + std::max(0, 7 - kinds)] += weight;
                                shapes[2][14 - term - (tp > 0)] += weight;
                                products++;
                            }
                        }
                    }
                }
            }
        }
    }

    // 標準出力:mode, tiles, shanten, count
    U total = 0;
    for (int i = 0; i < 8; i++) {
        total += counts[i];
        std::cout << mode << "," << h << "," << i - 1 << ","
                  << dec(counts[i]) << "\n";
    }
    assert(total == choose(mode == "three" ? 108 : 136, h));

    // 補助出力:役種別の距離分布をshapes.csvに保存
    std::ofstream sf("shapes.csv", std::ios::app);
    for (int kind = 0; kind < 3; kind++) {
        for (int d = 0; d < 15; d++) {
            if (shapes[kind][d]) {
                sf << mode << "," << h << "," << kind << "," << d - 1
                   << "," << dec(shapes[kind][d]) << "\n";
            }
        }
    }
    std::cerr << "COUNT " << mode << " " << h << " total=" << dec(total)
              << " ordered_products=" << products << " seconds="
              << std::chrono::duration<double>(
                     std::chrono::steady_clock::now() - start).count()
              << "\n";
}

// ─────────────────────────────────────────────
// 4. 実行:三麻/四麻 × 13枚/14枚の4ケース
// ─────────────────────────────────────────────
int main(int argc, char** argv) {
    auto start = std::chrono::steady_clock::now();
    std::ofstream("shapes.csv")
        << "mode,tiles,shape_0normal_1seven_2orphans,shanten,count\n";

    Table suit(9, true, false);
    Table honor(7, false, true);
    Table man(2, false, true);

    // 引数に出力先を指定すると、局所距離テーブルをバイナリ保存
    if (argc > 1) {
        std::string path = argv[1];
        for (auto t : {&suit, &honor, &man}) {
            std::ofstream f(
                path + "/table" + std::to_string(t->n) + ".bin",
                std::ios::binary
            );
            for (auto const& a : t->dist) {
                f.write((char const*)a.data(), a.size());
            }
        }
    }

    std::cout << "mode,tiles,shanten,count\n";
    for (int h : {13, 14}) {
        count("three", h, {&man, &suit, &suit, &honor});
        count("four", h, {&suit, &suit, &suit, &honor});
    }

    std::cerr << "ALL seconds="
              << std::chrono::duration<double>(
                     std::chrono::steady_clock::now() - start).count()
              << "\n";
}

実行方法:GCCなどのC++17対応コンパイラでビルドし、実行結果の標準出力をCSVへ保存できます。別途、役種別の計算結果も shapes.csv に出力されます。組合せ数から確率を求めるには、各区分の組合せ数を全配牌数 C(T,h) で割ります。連続確率の表は、1局の確率をもとに、本文記載の pn で計算したものです。

このプログラムは乱数シミュレーションではなく、指定した数学的モデルの全数計算です。実際のゲームの配牌システムや乱数生成処理を検査するものではありません。

プログラム実行結果とCSVの見方

このC++プログラムを実行すると、三人麻雀・四人麻雀の13枚・14枚について、シャンテン数ごとの配牌組合せ数がCSV形式で標準出力に表示されます。

出力されるCSVは、次の4項目で構成されています。

項目内容
modethree=三人麻雀、four=四人麻雀
tiles配牌枚数(13枚または14枚)
shantenシャンテン数(−1は和了形、0はテンパイ)
count該当する配牌の組合せ数(整数)

出力例:三人麻雀・13枚配牌

以下は、プログラムを実際に実行して得られた結果の一部です。

mode,tiles,shanten,count
three,13,-1,0
three,13,0,4248510347840
three,13,1,243214955934944
three,13,2,2732462104034240
three,13,3,8169994696659840

例えば、three,13,3,8169994696659840 は、三人麻雀の13枚配牌で3シャンテンになる組合せが8,169,994,696,659,840通りあることを表しています。

この組合せ数を、三人麻雀13枚の全配牌数20,592,957,740,312,160通りで割ると、記事に掲載している 約39.673731% が求められます。

なお、プログラムは補助集計として shapes.csv も作成します。こちらは通常形・七対子・国士無双を個別に評価したデータであり、記事の最終的なシャンテン数分布とは区別しています。

実行時の出力について

プログラムを実行すると、計算結果がCSV形式で標準出力に表示されます。TARGETS や COUNT などの途中経過は標準エラー出力に表示されるため、> out.csv で保存したCSVには含まれません。

また、実行したフォルダには、通常形・七対子・国士無双を個別に集計した shapes.csv が作成されます。こちらは5列構成で、役種は「0=通常形、1=七対子、2=国士無双」を表します。

※本プログラムでは、同じ牌を最大4枚までとし、5枚目の同一牌がなければ和了できない形はテンパイに含めません。 そのため、牌の形だけで判定する一部のシャンテン計算ツールとは、結果が異なる場合があります。

よくある質問(FAQ)

Q
三麻の北抜きは確率に影響しない?
A

北を抜く前の初期配牌には、通常の字牌として北を含むため、今回の確率に追加の補正は不要です。北を抜いて補充した後の手牌は別の確率分布になります。

Q
「1~2シャンテン」と「2シャンテン以内」は同じ?
A

同じではありません。「1~2」にはテンパイ(0)を含みません。「2シャンテン以内」には0、14枚の和了形なら−1も含めます。

Q
同じ悪い配牌が続いたら、配牌が操作されている証拠?
A

いいえ。理論上起こり得る連続を見ただけでは、配牌の偏りや操作の有無は判断できません。実際に検討するには、事前に決めた判定条件・十分なサンプル数・対局ログなどが必要です。今回の理論分布だけでは、特定のゲームの乱数実装の公正さも不公正さも証明できません。

まとめ|ねこちゃんの配牌は宇宙級に悪い?

厳密な組合せ計算では、子13枚の初期配牌で三麻は3シャンテン、四麻は4シャンテンが最多でした。1~2シャンテンの配牌は三麻で約14.45%、四麻で約9.97%。指定した5局すべてが1~2シャンテンになる確率は、三麻で約0.006300%、四麻で約0.000987%です。

ねこちゃん
ねこちゃん

3~4シャンテンの配牌がこんなに多いなんて知らなかったよ。地球人の麻雀、思ったより厳しいわね。

うさちゃん
うさちゃん

つまり、ねこちゃんだけ配牌が悪いとは限らないってこと!
(ねこちゃんは、何星人なの……)

ねこちゃん
ねこちゃん

次は配牌ログを集めて、理論値と比べてみたいね。

免責事項

本記事のサンプルプログラムは、学習・参考用として掲載しているもので、動作や結果を保証するものではありません。 利用する場合は、ご自身の環境に合わせて確認しながらお使いください。万が一トラブルや損害が発生した場合でも、当サイトでは責任を負いかねます。

本記事の配牌確率は、使用牌を一様に無作為シャッフルした場合の数学的な理論値です。掲載C++プログラムはシャンテン数別の配牌組合せ数を全数計算し、連続確率については各局の配牌が独立であると仮定して算出しています。
実際の対局での勝率や和了率を保証せず、無作為な配牌でも偶然の偏りや連続が生じます。
この記事はゲームの攻略・研究を目的とした非公式の解説であり、特定のオンライン麻雀サービスや大会運営団体とは関係ありません。

広告
Logicool G ゲーミングマウス G502WL LIGHTSPEED ワイヤレス ゲーミング マウス HERO 25K センサー LIGHTSYNC RGB 11個プログラムボタン POWERPLAY 無線 充電 対応 ブラック PC windows mac G502 国内正規品 【 ファイナルファンタジー XIV 推奨モデル 】
Logicool G ゲーミングマウス G502WL LIGHTSPEED ワイヤレス ゲーミング マウス HERO 25K センサー LIGHTSYNC RGB 11個プログラムボタン POWERPLAY 無線 充電 対応 ブラック PC windows mac G502 国内正規品 【 ファイナルファンタジー XIV 推奨モデル 】
【Logicool G G502 ワイヤレス ゲーミングマウス】ゲーミングマウス販売台数世界No.1※のG502シリーズの待望のワイヤレス版。TIME雑誌に「BEST INVENTIONS 2019」(100のベスト発明品の中)唯一に選ばれたゲーミングマウスです。
【遅延のないワイヤレス】超高速LIGHTSPEED技術搭載で1msワイヤレスゲーミングマウスに進化したG502ワイヤレスマは有線ケーブルの煩わしさから解放されます。
【HERO センサーさらなる進化】G703hに搭載した次世代HERO 16Kオセンサーは25Kに進化。精度性能約4倍、電源効率は約10倍向上し、最大のトラッキング精度は16,000DPIから25,000DPIにアップグレード。(25KにアップデートするにはGHUBファームウェアの更新が必要)
【無線充電対応】POWERPLAY 対応で、プレイ中も常に充電が可能なため、バッテリーの新配は不要です。通常の充電の場合は1 回のフル充電で最長60 時間のゲームプレイ、標準USB ポートでの5 分の充電で2.5 時間のゲームプレイが可能。
【パッケージ内容】製品本体、LIGHTSPEEDワイヤレスレシーバー 、レシーバー延長アダプター 、充電/データケーブル 、アクセサリーケース 、2gウェイトx4、4gウェイトx2、クイックスタートガイド、2年間無償保証書、保証規定。
最新の価格・在庫状況はAmazonでご確認ください。

参考資料

 

※本文中に記載の会社名・製品名・サービス名・ゲームタイトル名等は、各社の商標または登録商標であり、権利は各社に帰属します。

※サンプルはテストを行っていますが、すべての環境での動作を保証するものではありません。ご利用は自己責任でお願いいたします。

※本記事の仕様・価格・対応状況等は執筆時点で確認できた情報をもとに掲載しています。最新の情報はメーカー公式サイトをご確認ください。

※当サイトでは一部の記事において、アイキャッチ画像にAI生成を使用しています。

※Amazonのアソシエイトとして、うさねこ散歩は適格販売により収入を得ています。