論文一覧に戻る 📚 用語集トップ 🗺 概念マップ
📚 用語解説
📚 用語解説
順列
Permutation
数学基礎

🔖 キーワード索引

順列に関連する 12 個のキーワード。 各チップから関連ページへジャンプできる。

#順列 #置換 #階乗 #nPk #itertools #permutation_test #組合せ論 #計算量爆発 → 組合せ → 階乗 → 確率 → 並べ替え検定

💡 30秒で分かる結論

🍰 まずはやさしく

順列は、ものの並び方のことです。

選び方と順番の両方を数えるために使います。

スマホのパスワードを決める時に役立ちます。

ここでは、順列の基本的な考え方を読みます。

n個から r個を順序付けて選ぶ場合の数

📍 あなたが今見ているもの

🍰 まずはやさしく

順列は、並べ方のルールの基礎です。

データの分析や暗号の強さを調べる時に使います。

都道府県をランキング順に並べる例で考えます。

この章では、統計学での使われ方を読みます。

高校数学の確率・場合の数の基礎。 暗号強度の評価、 ノンパラメトリック検定(permutation test)など、 機械学習にも顔を出します。

📍 文脈ボックス: あなたが今見ているもの

あなたは今、 「順列」のページにいる。 これは 「組合せ論」系列の最も基本的な概念で、 $n$ 個の異なる対象から $k$ 個を選んで並べ方を考慮して並べる方法の数。 統計学では「並べ替え検定(permutation test)」の基礎、 機械学習では特徴量重要度(permutation importance)に登場する。 SSDSE-B-2026 の 47 都道府県から 5 個並べる方法数は約 1.84 億通り。

前提: 階乗 / 並列: 組合せ / 発展: 並べ替え検定

🎨 直感で掴む

🍰 まずはやさしく

順列は、順番を大切にする選び方です。

並び方が変わると別物として数えるために使います。

部活のメンバーを1列に並べる時に役立ちます。

ここでは、具体的な数え方の流れを読みます。

4人 A, B, C, D から 2人選んで一列に並べる:

  • 1人目 → 4通り
  • 2人目 → 残り3通り
  • 合計 → 4×3 = 12通り(${}_4P_2 = 12$)

順序ありなので「AB」と「BA」を別カウント。 もし区別しないなら組合せ ${}_4C_2 = 6$。

順列が登場する SSDSE-B-2026 文脈での 3 場面

組合せ ${}_nC_r$ との違い

問題順序を区別?公式47 県から 5 県の例
5 県を表彰式の壇上に並べるする (1 位/2 位/...)${}_{47}P_5$184,072,680 通り
5 県を視察先として選ぶしない${}_{47}C_5$1,533,939 通り (P÷5!)

覚え方: 「順序を意識する → P (Permutation)」「順序を意識しない → C (Combination)」。 P は C の $r!$ 倍 (5 県の並び順は 5!=120 通りあるため)。

🎮 触って理解する

スライダーで n(並べる元の個数)と k(取り出して並べる個数)を動かすと、 順列 P(n,k)=n!/(n-k)! の値・実際の並べ方・組合せ C(n,k) との違いがリアルタイムに変わります。 図をスワイプ(左右にドラッグ)するか下のボタンで、 実際の並べ方を 1 つずつ切り替えられます。 「順序を区別する」と数がどれだけ増えるかを体感してください。

順列 P(n,k) = n! / (n-k)!
12
4 × 3 = 12
組合せ C(n,k) = n! / ((n-k)! k!)
6
順序を区別しない選び方
増える倍率 = k!
2
P = C × k! → 12 = 6 × 2
1 / 12 通り目

📊 順序を区別すると何倍に増える?

🧭 もっと深く理解する

💡 直感
順列は「並べる順序を区別する」数え方。 1 番目の椅子に座る人を n 通り、 2 番目を残り (n-1) 通り…と、 埋めるたびに選択肢が 1 つずつ減る掛け算。 だから P(n,k) は n から始めて k 個ぶんの数を掛けた積になる。
⚠️ よくある落とし穴
組合せと混同するのが最大の罠。 「AB」と「BA」を別々に数えるのが順列、 同じとみなすのが組合せ。 問題文に「順番」「1 位/2 位」「並べる」があれば P、 「選ぶだけ」「グループ」なら C。 迷ったら「順序が結果に意味を持つか?」を自問する。
🚀 発展
重複順列 $n^k$(同じ要素を繰り返せる。 パスワード等)、 円順列 $(n-1)!$(回転を同一視)、 二項係数 $\binom{n}{k}={}_nC_k$(順列を $k!$ で割ったもの)へ拡張できる。 順序を「どこまで区別するか」を変えると別の数え方になる。 組合せ確率並べ替え検定へと繋がる。

📐 定義/数式

🍰 まずはやさしく

順列は、数式で表せる並べ方の数です。

計算を簡単にして答えを出すために使います。

買い物で選ぶ商品の順番を数える時に役立ちます。

ここでは、順列の公式と計算方法を読みます。

順列Permutation):n個から r個を順序付けて選ぶ場合の数

【順列の公式】
$$ {}_n P_r = \frac{n!}{(n-r)!} = n \times (n-1) \times \cdots \times (n-r+1) $$
n個から r個を順序付きで取り出す。 r=n のとき ${}_nP_n = n!$(全並べ)。

📐 重要公式まとめ

公式名 数式 適用場面
基本順列$P(n,k) = \frac{n!}{(n-k)!}$$n$ 個から $k$ 個を選んで並べる
全順列$P(n,n) = n!$全部並べる
重複順列$n^k$$n$ 種類から $k$ 個重複可
同じものを含む順列$\frac{n!}{n_1! n_2! \cdots n_r!}$重複要素含む全並べ替え
円順列$(n-1)!$円形の配置
数珠順列$\frac{(n-1)!}{2}$裏返しも同じとみなす円
組合せ$C(n,k) = \frac{n!}{(n-k)!k!}$順序を区別しない選び方
順列⇔組合せ$P(n,k) = C(n,k) \cdot k!$変換
Stirling 近似$n! \approx \sqrt{2\pi n}(n/e)^n$大きな $n!$ の近似
ガンマ関数$\Gamma(n) = (n-1)!$階乗の連続拡張

この 10 公式を覚えれば、 順列に関する問題の 95% は解ける。 残り 5% は群論や生成関数を要する高度なもの。

🎲 5 秒クイズ(最終確認)

  1. Q: P(10,3) は? A: 720
  2. Q: 5! は? A: 120
  3. Q: 47 県を全部並べる方法は何桁? A: 60 桁(47! ≒ 2.59×10^59)
  4. Q: 順列と組合せの関係式は? A: P(n,k) = C(n,k) × k!
  5. Q: 4 文字 "AABB" の並べ方は? A: 6(= 4!/2!2!)
  6. Q: Python で順列数を計算する関数は? A: math.perm(n, k)
  7. Q: 並べ替え検定の Python 実装は? A: scipy.stats.permutation_test
  8. Q: permutation importance を提案したのは? A: Breiman (2001)

全問正解なら、 順列の知識は実務レベルに達している。 次は組合せ・確率・並べ替え検定の関連ページへ。

📐 公式図鑑: 順列まわりの 12 公式

順列・組合せ・階乗の周辺には沢山の公式があり、 場面ごとに使い分ける。 ここで一覧化しておく。

名称意味・使い所
階乗$n! = n \cdot (n-1) \cdots 1$$n$ 個全てを並べる
順列$_nP_r = \frac{n!}{(n-r)!}$$n$ から $r$ を順序付き選択
組合せ$_nC_r = \frac{n!}{r!(n-r)!}$$n$ から $r$ を順序なし選択
重複順列$n^r$パスワードのような同要素再使用
重複組合せ$_{n+r-1}C_r$果物の購入セット
円順列$(n-1)!$円卓の座席配置
数珠順列$\frac{(n-1)!}{2}$ネックレスの飾り順
多項係数$\frac{n!}{k_1! k_2! \cdots k_m!}$同種要素の並び替え、 CV 分割
パスカルの公式$_nC_r = _{n-1}C_{r-1} + _{n-1}C_r$漸化式で組合せを構築
二項定理$(a+b)^n = \sum_{r=0}^{n} {_nC_r} a^r b^{n-r}$確率分布 (二項分布) の基礎
スターリング近似$n! \approx \sqrt{2\pi n}\,(n/e)^n$大きな $n$ で階乗を近似計算
交代和$\sum_{r=0}^{n}(-1)^r {_nC_r}=0$ $(n \geq 1)$包除原理の基礎、 完全順列 (montmort 数)

→ 公式を丸暗記するのではなく、 「何を区別し、 何を等価視するか」のロジックで導出できるようにしておく。 SSDSE-B-2026 の現場では、 多項係数とスターリング近似は計算量の見積もりで頻出。

🖼 図で見る順列 — SSDSE-B 都道府県データを順列で並べ替えるとどう見えるか

順列の概念は数式だけでは捉えにくい。 ここでは SSDSE-B-2026 の都道府県データを使って、 順列・順位・並び替えの効果を視覚化した 3 枚の図を示す。 すべて実データから生成されたものであり、 合成データは一切含まない。

総人口と出生数の散布図 (順位相関の元データ)
図 1: SSDSE-B-2026 の都道府県総人口 A1101 (横軸) と出生数 A4101 (縦軸) の散布図。 47 点の順位を比較すると Spearman ρ ≈ 0.98 — 「人口順位と出生数順位という 2 つの順列はほぼ一致」を意味する。 東京・神奈川・大阪が右上に大きく外れ、 順列の上位が固定的なことが視覚的に分かる。
都道府県人口のヒストグラム
図 2: 47 都道府県の総人口 A1101 のヒストグラム。 右に強く歪んだ分布で、 平均値より中央値が情報を持つ。 こうした非対称分布では t 検定が不安定になるので、 順列検定 (permutation test) が代替手段として用いられる。 順列は「分布の仮定を取り払う」道具。
地域別人口の箱ひげ図
図 3: 47 県を地域 (北海道・東北・関東・中部・近畿・中国・四国・九州沖縄) に分けた箱ひげ図。 地域間で人口分布が大きく異なる。 順列検定では「47 県のラベルをランダムに並べ替えて、 観測された地域差以上の差が偶然生じる確率」を計算する。 帰無分布の構築原理を視覚的に補助する図。

→ 図 1 は順位相関の妥当性、 図 2 は順列検定の必要性、 図 3 は順列検定の動作原理、 をそれぞれ示している。 順列は「並べ方の総数」を数えるだけでなく、 「分布の形を仮定なしに扱う」「順位を比較する」道具でもあることが、 1 枚ずつの図で確認できる。

📝 まとめ — 順列の 5 つの顔

これらを横断する共通の主題は「順序が情報を持つか / 持たないか」を見極めること。 SSDSE-B-2026 のような実データで順列概念を実感することで、 高校数学の $_nP_r$ が「実務で何の道具になるか」が明確になる。

さらに踏み込めば、 順列は単独で意味を持つだけでなく、 確率論 (二項分布、 ポアソン分布、 超幾何分布のいずれもが組合せ・順列を母数として持つ)、 情報理論 (エントロピーは確率分布上の組合せ的指標)、 圏論 (対称群が群論・表現論の母体)、 計算量理論 (NP 困難な最適化問題の多くが順列空間上の探索) など、 数学の根幹に深く接続している。 順列の理解度は、 そのまま「離散数学を実問題と結ぶ翻訳力」の指標になる。 SSDSE-B-2026 を題材にした本稿の演習で、 47 都道府県の順列空間と現実の社会データを行き来する感覚を養い、 統計・データ解析コンペの実戦で「順序を扱う」あらゆる場面に対応できる土台を築いてほしい。 順列は地味な概念だが、 データサイエンスの根を支える基礎中の基礎であり、 ここを丁寧に押さえることが上達への最短経路となる。 公務員試験・統計検定・データ解析実務のいずれにおいても、 順列を「式」「実装」「意味」の 3 層で同時に語れる人材は強い。 SSDSE-B-2026 を題材に手を動かし続けることで、 その境地に確実に近づける。

🔬 記号・用語の読み解き

記号意味
${}_nP_r$n個からr個の順列数
$n!$n の階乗(n × (n-1) × … × 1)
${}_nC_r$組合せ数(順序区別なし)
$\binom{n}{r}$二項係数(${}_nC_r$ と同じ)

🔬 詳細な解説(深掘り)

概念の本質

順列(Permutation)は、 単に用語の定義を覚えるだけでは本当には理解できません。 なぜこの概念が生まれたのかどんな問題を解決するために導入されたのか類似の手法とどう違うのか — これらを意識することで、 初めて「使える知識」になります。

数式や Python コードはあくまで 道具。 道具の使い方を覚える前に、 その道具で何をしたいか(目的) を明確にすることが、 データサイエンス学習の鉄則です。

他の概念との関係

この用語は、 単独で存在するわけではなく、 多くの関連概念とネットワークを形成しています。 上の「関連用語」セクションに挙げたリンク先を1つずつ辿ると、 全体像が見えてきます。 特に:

実務で気をつけるポイント

理論を学ぶことと、 実務で使えることは別物です。 公的統計(SSDSE、 e-Stat 等)の実データで実装・実験することで、 教科書だけでは見えない罠 に気付けます。 たとえば:

これらは 順列 に限った話ではなく、 データサイエンス全般に共通する作法です。 「落とし穴」セクションの内容と合わせて、 自分なりのチェックリストを作るとよいでしょう。

📊 評価・検証の視点

順列 を使った分析の 正しさを担保する ためには、 以下の観点で検証するのが定番です。

確認する点順列 で何を見るか
順列と組合せの混同「並べる」か「選ぶだけ」かを問題文で確認。
階乗の爆発20! を整数で扱う → 大きいが Python なら OK。 浮動小数では誤差。
0! の扱い$0! = 1$(空の積)。 ${}_nP_0=1$。
重複順列の見落とし同じものを何度も使える場合は別公式($n^r$)。
再現性同じデータ・同じコードで同じ結果が出るか。このページの ▶ 実行ボタンで確かめられます

💼 業界別の使われ方

順列 は分野横断で活躍する概念です。 業界別に見ると以下のような使われ方があります。

🏥 医療・ヘルスケア
疾病予測、 診断支援、 治療効果の評価、 公衆衛生指標の分析(高齢化率、 罹患率、 医療費等)
🏛️ 行政・公共政策
EBPM(エビデンスに基づく政策立案)、 地域経済分析、 RESAS/e-Stat の活用、 政策効果測定
🏪 マーケティング・小売
顧客分析、 需要予測、 価格弾力性、 RFM分析、 A/Bテスト、 LTV予測
🏭 製造・品質管理
品質管理、 故障予知、 異常検知、 生産最適化、 サプライチェーン分析
💰 金融・保険
信用スコア、 リスク評価、 不正検知、 アルゴリズムトレーディング、 保険料設定
🎓 教育・研究
教育効果の測定、 学習分析、 研究データ解析、 統計教育、 データサイエンス人材育成

📈 公的統計データ(SSDSE)での具体例

順列 を実際のデータで学ぶときは、 SSDSE(教育用標準データセット、 総務省統計局)が便利です。

これらは 統計センターの SSDSE ページ から CSV で直接ダウンロードできます。 上の Python コード例で data/raw/SSDSE-B-2026.csv としているのが、 まさにこれです。

実データで動かすことで、 教科書の例題では見えない 実務的な気づき(欠損のパターン、 単位の混在、 都道府県名の表記揺れ等)が得られます。

🔧 よくあるトラブルと対処

🐍 Python コードが動かない
→ Python 3.10+ と必要ライブラリ(pandas、 numpy、 scikit-learn 等)がインストール済みか確認。 pip install pandas numpy scikit-learn matplotlib で揃います。
📁 CSVファイルが読み込めない
→ ファイルパスを確認。 文字コードが utf-8 ではなく shift_jiscp932 の場合がある(古い日本の公的統計に多い)。 encoding='cp932' を試してください。
📐 数式が表示されない
→ ページが KaTeX を読み込んでいるはずです。 ブラウザのキャッシュをクリアするか、 開発者ツールで JavaScript エラーを確認。
🔢 数値計算結果が教科書と違う
→ 不偏推定(n-1)と標本推定(n)の違い、 浮動小数点誤差、 ライブラリのデフォルト引数の違いなどが原因。 ドキュメントを確認。
📊 グラフが描画されない
→ Jupyter Notebook なら %matplotlib inline、 スクリプト実行なら plt.show() を忘れずに。 日本語フォントは matplotlib 用に別途設定(japanize-matplotlib 等)が必要。

🔬 数式を言葉で読み解く

順列の数式は次の 2 つを必ず押さえる:

$$P(n, k) = {}_nP_k = \frac{n!}{(n-k)!} = n \cdot (n-1) \cdot (n-2) \cdots (n-k+1)$$

$$P(n, n) = n! = n \cdot (n-1) \cdot (n-2) \cdots 2 \cdot 1$$

直感的読み方:「最初の位置は $n$ 通り、 次は $n-1$ 通り、 ..., $k$ 番目は $n-k+1$ 通り、 これを掛け合わせる」。 並び順を区別するので、 同じメンバーの異なる並びは別カウント。

組合せ $C(n,k) = P(n,k) / k!$ は順列を「並び順を無視」して数えたもの。 順列 = 組合せ × 並び方の数。

🔬 高度な関連: 群論と置換

順列の数学的な深層には「群論」がある。 $n$ 個の対象の全順列は対称群 $S_n$ をなし、 群論の中心的対象。 SSDSE-B-2026 の 47 都道府県の置換は $S_{47}$ の元として記述できる。

置換の表記法

置換の応用

「並び順を数える」だけの順列が、 群論を経由して暗号・代数・物理・ML に展開する。 「基礎数学が応用に直結する」典型例。

📈 計算量と現実のスケール

n n! 列挙時間(1 つ 1 ns) 現実の解釈
51200.12 マイクロ秒瞬時
103,628,8003.6 ミリ秒体感ゼロ
151.3 × 10^1222 分待てる
202.4 × 10^1877 年一生終わらない
251.6 × 10^255 億年地質学的時間
302.7 × 10^328 × 10^15 年宇宙年齢×10^6
47 (SSDSE)2.59 × 10^59物理的に不可能

$n=20$ で「人生では終わらない」、 $n=30$ で「宇宙年齢の 100 万倍」。 順列の世界では「賢く絞り込む」アルゴリズムが必須。 これが分枝限定法・動的計画法・近似アルゴリズムが発達した理由。

🤖 機械学習での順列応用詳説

1. Permutation Importance

学習済みモデルに対し、 ある特徴量の値をシャッフルして予測精度がどれだけ落ちるかを測る。 落ち幅が大きい特徴量ほど重要。 順列の概念をモデル解釈に応用したのが Breiman (2001) の発明。

2. Cross-Validation

データを $k$ 個のフォールドに分割する際、 ランダム順列でシャッフルしてから分割するのが標準。 順序による偏りを除く目的。 sklearn の KFold(shuffle=True) がこの実装。

3. SHAP の Shapley 値

Shapley 値は特徴量の貢献度を「全順列における追加効果の平均」として定義する。 SHAP ライブラリはこれを高速近似計算。 順列が ML 解釈の理論的根拠を支える。

4. データ拡張(Augmentation)

時系列データの「ランダム置換」や、 タブラーデータの「カラム順序の入れ替え」は順列ベースのデータ拡張。 過学習防止に有効。

5. NLP のシャッフル系手法

BERT 系の Pre-training で、 文の順序を入れ替えて「順序が正しいか」を判定する Next Sentence Prediction や、 単語の順序をシャッフルする augmentation。 順列が NLP の表現学習に活用される。

ML 手法 順列の役割 代表ライブラリ
Random Forest特徴量重要度の計算sklearn.inspection.permutation_importance
Gradient Boosting同上XGBoost / LightGBM
SHAPShapley 値計算shap
Cross-validationフォールド分割のシャッフルsklearn.model_selection
Mini-batch SGDエポック毎のシャッフルPyTorch DataLoader

📊 並べ替え検定の理論

並べ替え検定(permutation test)は順列の最も実用的な統計応用。 帰無仮説「2 群に差はない」のもとで、 ラベルを並べ替えても統計量の分布が変わらないことを利用する。

アルゴリズム

  1. 観測データから統計量(例:平均差)$T_{\rm obs}$ を計算
  2. 全ラベル順列について同じ統計量 $T_i$ を計算(または十分多くサンプリング)
  3. $\Pr(|T_i| \geq |T_{\rm obs}|)$ を p 値として算出
  4. p < 0.05 なら帰無仮説を棄却

利点と限界

観点 並べ替え検定 t 検定(パラメトリック)
分布仮定不要正規分布を仮定
小サンプル○ 適している△ 仮定が崩れる
計算コスト高い(順列爆発)低い(解析解)
汎用性任意の統計量に対応平均差のみ
解釈直感的(並べ替えて比較)分布論を要する

SSDSE-B-2026 のような 47 件規模なら、 並べ替え検定が直感的で説明しやすい。 大規模データでは t 検定の方が高速。

🔬 数式を言葉で読み解く — 順列検定 (permutation test) の力

順列 $n!$ の応用先で最も実用的なのが「順列検定」。 これは「2 群の平均差が偶然か否か」を、 仮定なし (分布の正規性を仮定しない) に判定する手法。 SSDSE-B-2026 の都道府県データで、 「東日本 vs 西日本の人口総数に差はあるか?」を順列検定で評価する。

このコードでやること: SSDSE-B-2026 の都道府県人口 (A1101) を東日本 (北海道〜静岡) と西日本 (愛知〜沖縄) に分け、 平均差を観測値とする。 そして 47 都道府県のラベルを 5,000 回ランダムに並べ替え (=順列の一部を抽出)、 「観測値以上の差が偶然生じる確率」を p 値として求める。

📥 入力データ (SSDSE-B-2026 の人口列を東/西で分割):

東日本 (22 都道県): 北海道, 青森, 岩手, ..., 静岡 — 平均 320.4 万人 西日本 (25 府県): 愛知, 三重, 滋賀, ..., 沖縄 — 平均 217.8 万人 観測された平均差 = 102.6 万人
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import pandas as pd
import numpy as np
from scipy import stats

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2022].copy()

east = df.iloc[:22]['A1101'].values   # 北海道〜静岡
west = df.iloc[22:]['A1101'].values   # 愛知〜沖縄

observed_diff = np.abs(east.mean() - west.mean())

# 順列検定: 5,000 回ラベルをシャッフル
np.random.seed(0)
combined = np.concatenate([east, west])
n_east = len(east)
n_perm = 5000
count = 0
for _ in range(n_perm):
    np.random.shuffle(combined)
    diff = np.abs(combined[:n_east].mean() - combined[n_east:].mean())
    if diff >= observed_diff:
        count += 1
p_value = count / n_perm
print(f'観測平均差 = {observed_diff / 1e4:.1f} 万人')
print(f'p 値 (順列検定) = {p_value:.4f}')

📤 実行すると次の出力が得られる:

観測平均差 = 102.6 万人 p 値 (順列検定) = 0.2212

💬 p 値 0.22 は「観測差は偶然のレベル」を意味する。 つまり東京・大阪のような巨大都市の偏りはあるものの、 「東/西」という分割は人口を区別する有意な軸ではない。 順列検定は分布の仮定を置かないので、 t 検定が使えない歪んだデータ (人口は極端な右裾を持つ) でも安全に使える。

🧮 実値で計算してみる

例:${}_5P_3 = 5\times4\times3 = 60$。
5人から3人を選んで一列=60通り。 全並べなら $5! = 120$。

🧮 実値で計算してみる(SSDSE-B-2026)

SSDSE-B-2026 の 47 都道府県から、 訪問する 5 県を選んで順番に並べる場合の総数を計算する。

📥 入力:

n = 47 都道府県 k = 5 県を並べる

📤 計算:

P(47, 5) = 47 × 46 × 45 × 44 × 43 = 184,072,680 通り 比較: - P(47, 1) = 47 - P(47, 2) = 47 × 46 = 2,162 - P(47, 3) = 47 × 46 × 45 = 97,290 - P(47, 4) = P(47,3) × 44 = 4,280,760 - P(47, 5) = P(47,4) × 43 = 184,072,680 - P(47, 6) = P(47,5) × 42 = 7,731,052,560 (約 77 億) - P(47, 47) = 47! ≒ 2.59 × 10^59 (天文学的)

💬 「47 県から 5 つ並べる」と聞くと小さい数字に感じるが、 実際は 1.84 億通り。 6 つにすると 77 億通りで、 もう全列挙は計算機でも厳しい。 これが順列の「指数的に増える」性質。

参考:組合せ $C(47, 5) = P(47,5) / 5! = 184072680 / 120 = 1,533,939$。 順列の 120 分の 1 になる(5 個の並べ方が 5! = 120 通りあるため)。

🧮 nPr vs nCr — 順列と組合せの完全な違い

順列 $_nP_r = n!/(n-r)!$ と組合せ $_nC_r = n!/(r!(n-r)!)$ の関係は $_nP_r = _nC_r \times r!$ で結ばれる。 47 都道府県から 3 県を選ぶ場合の比較:

概念記号47 県から 3 県順序
順列$_{47}P_3$$47 \times 46 \times 45 = 97{,}290$区別する「金・銀・銅」の表彰順
組合せ$_{47}C_3$$97{,}290 / 6 = 16{,}215$区別しない「上位 3 県」の選出

「順序が問題になるか?」が分岐点。 県名を「並べる」なら順列、 「集める」なら組合せ。 scipy では scipy.special.perm(47, 3)scipy.special.comb(47, 3) で計算できる。

🧭 追加深掘り: 順列が現れる現実の現場と SSDSE-B-2026 での実証

順列 (permutation) は「順序を区別して並べる方法の総数」を扱う離散数学の基礎概念である。 確率・統計・機械学習の至るところに姿を現すが、 中学・高校で学んだ $_nP_r = n!/(n-r)!$ の式だけでは、 現実のデータ解析でどう使うかが見えにくい。 ここでは SSDSE-B-2026 (47 都道府県 × 約 80 変数 × 12 年度) を題材に、 順列の概念が現れる 6 つの実例 — (1) 順列検定による中央値差の評価、 (2) 特徴量重要度の permutation importance 計算、 (3) クロスバリデーション分割の順列空間、 (4) ランキング系列の Spearman/Kendall 順位相関、 (5) 順列暗号としてのワンタイムパッド類似、 (6) 巡回セールスマン問題 (TSP) で 47 都道府県を回る最短経路 — を順に体験する。 各実例で「順列の総数」「実際に試行できる回数」「現実的な計算可能性」を必ず数字で示し、 「組合せ爆発」がどこから始まるかを腹に落とす。

(1) 順列検定 (permutation test) で中央値差を評価する

このコードでやること: SSDSE-B-2026 の都道府県別「人口総数 A1101 (2022 年)」を、 関東 1 都 6 県 vs その他 40 県に分割し、 中央値の差を 10,000 回の順列で評価する。 t 検定や Welch 検定は平均と分散の仮定が必要だが、 順列検定は「ラベルを交換しても分布は変わらない」という弱い仮定のみで動く。 人口データは右に強く歪んでおり (東京・神奈川・大阪が突出)、 正規分布の仮定は破綻している。 こうした場合に順列検定は強力な代替手段になる。

📥 入力データ (SSDSE-B-2026 を関東/その他で分割した抜粋):

関東 7 県: 東京 1403.8 万人, 神奈川 923.2 万人, 埼玉 733.7 万人, 千葉 626.6 万人, 茨城 284.0 万人, 栃木 190.9 万人, 群馬 191.3 万人 → 中央値 626.6 万人 その他 40 県 (一部): 北海道 514.0 万人, 大阪 878.2 万人, 愛知 749.5 万人, 福岡 511.6 万人, 沖縄 146.8 万人, 鳥取 54.4 万人, ... → 中央値 131.0 万人 前後 観測された中央値差 ≈ 496 万人
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2022].copy()
np.random.seed(0)

kanto = ['東京都', '神奈川県', '埼玉県', '千葉県',
         '茨城県', '栃木県', '群馬県']
df['region'] = df['Prefecture'].apply(
    lambda x: 'kanto' if x in kanto else 'other')

group_a = df[df['region'] == 'kanto']['A1101'].values
group_b = df[df['region'] == 'other']['A1101'].values
observed = np.abs(np.median(group_a) - np.median(group_b))

combined = np.concatenate([group_a, group_b])
n_a = len(group_a)
n_perm = 10000
count = 0
for _ in range(n_perm):
    np.random.shuffle(combined)
    diff = np.abs(np.median(combined[:n_a]) - np.median(combined[n_a:]))
    if diff >= observed:
        count += 1
p_value = (count + 1) / (n_perm + 1)
print(f'観測中央値差 = {observed / 1e4:.1f} 万人')
print(f'p 値 (順列検定, 10,000 回) = {p_value:.5f}')

📤 実行すると次の出力が得られる:

観測中央値差 = 495.6 万人 p 値 (順列検定, 10,000 回) = 0.00170

💬 p < 0.01 で、 「関東とその他で人口中央値に差はない」という帰無仮説は強く棄却される。 47 県のラベル並び替えの全パターンは $_{47}C_7 = 62{,}891{,}499$ 通りもあるが、 そのうち 10,000 件をランダムサンプリングするだけで p 値の近似は十分得られる。 これが順列検定の効率性。

(2) Permutation Importance — 機械学習モデルの説明可能性

このコードでやること: SSDSE-B-2026 の説明変数 (人口総数、 出生数、 死亡数、 転入者数、 転出者数) から「総人口」を予測する RandomForest を学習し、 各特徴量の列を順列でシャッフルして予測誤差がどれだけ悪化するかを測る (permutation importance)。 シャッフル後に大きく誤差が増える列ほど、 モデルにとって重要な特徴量である、 という解釈。 順列は「特徴量間の依存関係を壊す」ために使われている。

📥 入力データ (SSDSE-B-2026 の主要 5 列):

特徴量列: A1101 (人口), A4101 (出生数), A4200 (死亡数), A5101 (転入者数), A5102 (転出者数) 目的変数: A1101 (再構成タスク、 列を抜いた残りから予測) レコード: 47 都道府県 × 12 年度 = 564 行 (NaN 除外後)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
import pandas as pd
from sklearn.ensemble import RandomForestRegressor
from sklearn.inspection import permutation_importance
from sklearn.model_selection import train_test_split

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
cols = ['A4101', 'A4200', 'A5101', 'A5102']
df = df.dropna(subset=cols + ['A1101'])
X = df[cols]
y = df['A1101']
X_tr, X_te, y_tr, y_te = train_test_split(
    X, y, test_size=0.3, random_state=0)

model = RandomForestRegressor(n_estimators=200, random_state=0)
model.fit(X_tr, y_tr)
result = permutation_importance(
    model, X_te, y_te, n_repeats=30, random_state=0)
for name, imp in zip(cols, result.importances_mean):
    print(f'{name}: 重要度 {imp:.4f}')

📤 実行すると次の出力が得られる (値は random_state 依存):

A4101: 重要度 0.0513 ← 出生数の追加情報は小さい A4200: 重要度 0.9330 ← 死亡数が最重要 (人口規模と強く連動) A5101: 重要度 0.0110 ← 転入者数の追加情報は小さい A5102: 重要度 0.0478 ← 転出者数も小さい

💬 死亡数 A4200 を順列で壊すと R² が 0.93 ポイント悪化する一方、 転入者数 A5101 を壊しても 0.01 しか悪化しない。 → 「転入者数の情報は、 他の特徴量で代替できる」「死亡数は他で代替が効かない、 独立な情報源」とモデルが教えてくれる。 この計算は順列が無いと成立しない (列を抜くと相関の連鎖が壊れる)。

(3) クロスバリデーション分割と順列空間の大きさ

47 都道府県を 5-fold CV に分割する方法は何通りあるか? これは多項係数で表され $\binom{47}{10,10,10,10,7} = \frac{47!}{10!\,10!\,10!\,10!\,7!}$ となり、 およそ $3.0 \times 10^{29}$ 通り。 全パターンを試すのは宇宙の寿命でも無理なので、 通常は 1 つの分割しか試さない。 だが「分割の偶然」で精度が振れることを防ぐため、 sklearn の RepeatedKFoldn_repeats 回だけランダム順列を取って平均する。

分割方法順列の数実務での試行回数用途
2-fold (47 を 23+24)$_{47}C_{23} \approx 1.6 \times 10^{13}$2-5 回高速プロトタイプ
5-fold$\approx 3.0 \times 10^{29}$5 回 × 5 repeats標準
Leave-One-Out47 (= $_{47}C_1$)47 回 (全部)小データ向け
Random permutation CV$47! \approx 2.6 \times 10^{59}$100-1000 回サンプリングベンチマーク

→ 順列の総数は天文学的だが、 実務では数十回のサンプリングで CV 精度の分散は安定する。 これは「中心極限定理 + 順列のランダム性」の合わせ技。

(4) Spearman/Kendall — 順列を比較する相関係数

このコードでやること: SSDSE-B-2026 の総人口 A1101 による都道府県順位と、 出生数 A4101 による順位を比較する。 Spearman 相関係数は「2 つの順列がどれだけ近いか」を測る指標で、 Kendall の τ は「順列のペアごとの転倒数 (inversion)」をカウントする。 順列は単なる「並べ方」だが、 2 つの順列を比べることで「ランキングの一致度」が定量化できる。

📥 入力データ (SSDSE-B-2026 2022 年度、 人口順位 vs 出生数順位):

都道府県 人口順位 出生数順位 東京 1 1 神奈川 2 3 大阪 3 2 愛知 4 4 埼玉 5 5 千葉 6 6 ... 鳥取 47 46
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import pandas as pd
from scipy import stats

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df = df[df['SSDSE-B-2026'] == 2022].copy()

# 人口順位と出生数順位を比較
pop_rank = df['A1101'].rank(ascending=False)
birth_rank = df['A4101'].rank(ascending=False)
rho, p_s = stats.spearmanr(pop_rank, birth_rank)
tau, p_k = stats.kendalltau(pop_rank, birth_rank)
print(f'Spearman rho = {rho:.4f}  p = {p_s:.2e}')
print(f'Kendall  tau = {tau:.4f}  p = {p_k:.2e}')

📤 実行すると次の出力が得られる:

Spearman rho = 0.9763 p = 1.53e-31 Kendall tau = 0.8885 p = 1.32e-18

💬 ρ ≈ 0.98 は「人口順位と出生数順位はほぼ同じ順列」を意味する。 47! ≈ $2.6 \times 10^{59}$ 通りの順列空間で、 これだけ似ているのは偶然ではない (p < $10^{-30}$)。 出生数の多寡は人口規模でほぼ決まる、 という自然な結論が、 順列の比較から自動的に出てくる。 順列の差分を「転倒対 (inversion pair) の個数」として数えるのが Kendall の τ で、 ρ より頑健 (外れ値に強い)。

(5) 順列暗号 (One-Time Pad とトランスポジション暗号)

古典暗号「シーザー暗号」は文字シフトだが、 トランスポジション暗号 (転置式暗号) は順列そのもの。 たとえば 47 文字の平文を「47! 通りのどれかの順列で並び替えた」と思えば、 鍵が無ければ 47! ≈ $2.6 \times 10^{59}$ の中から 1 つを当てる総当たり計算が必要。 これは現代のスーパーコンピュータでも宇宙の寿命を超える時間がかかる。 順列の組合せ爆発が暗号の安全性を支えている。 一方で、 順列の構造には「偶置換 / 奇置換」「巡回構造」「対称群 $S_n$」といった群論的性質があり、 量子計算アルゴリズム (例: Shor のアルゴリズム) の研究対象になる。

(6) TSP: 巡回セールスマン問題を順列全探索と最近傍法で解く

このコードでやること: 架空の 8 都市の座標をコード内にハードコードし、 全都市を 1 回ずつ訪問する経路の総距離を計算する。 都市数 n の巡回路は始点・向きの対称性を除いて $(n-1)!/2$ 通り。 8 都市なら 2,520 通りなので順列全探索で厳密最適が求まるが、 47 都道府県では $47!/(2 \times 47) \approx 2.7 \times 10^{57}$ 通りとなり全探索は不可能。 ここでは (A) 最近傍法 (nearest neighbor) と (B) 順列全探索 ($(n-1)! = 5{,}040$ 通りを列挙) を比較する。 順列 = 訪問順序、 という最も直接的な対応関係。

📥 入力データ (架空の都市配置・実在の県庁所在地ではなくコード内にハードコードした仮想座標):

都市 x y A 2.0 1.0 B 5.0 2.0 C 8.0 1.5 D 9.0 5.0 E 7.0 8.0 F 4.0 7.0 G 1.0 4.0 H 6.0 5.5 8 都市 × (x, y) — すべて架空の座標
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import numpy as np
from itertools import permutations

# 架空の都市配置(8 都市)- 実在の県庁所在地ではなくハードコードした仮想座標
names = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H']
xs = np.array([2.0, 5.0, 8.0, 9.0, 7.0, 4.0, 1.0, 6.0])  # 架空の x 座標
ys = np.array([1.0, 2.0, 1.5, 5.0, 8.0, 7.0, 4.0, 5.5])  # 架空の y 座標

def total_dist(order):
    d = 0.0
    for i in range(len(order) - 1):
        a, b = order[i], order[i + 1]
        d += np.hypot(xs[a] - xs[b], ys[a] - ys[b])
    return d

# (A) 最近傍法(ヒューリスティクス)
n = len(xs)
visited = [0]
remaining = set(range(1, n))
while remaining:
    last = visited[-1]
    nxt = min(remaining,
              key=lambda j: np.hypot(xs[last] - xs[j], ys[last] - ys[j]))
    visited.append(nxt)
    remaining.remove(nxt)
print(f'最近傍法の総距離   = {total_dist(visited):.2f}')
print('最近傍法の訪問順   =', ' → '.join(names[i] for i in visited))

# (B) 順列全探索(始点固定、(n-1)! = 5040 通りを列挙して厳密最適)
best_order, best = None, float('inf')
for perm in permutations(range(1, n)):
    order = [0] + list(perm)
    c = total_dist(order)
    if c < best:
        best, best_order = c, order
print(f'全探索の最短総距離 = {best:.2f}')
print('全探索の最短訪問順 =', ' → '.join(names[i] for i in best_order))

📤 実行すると次の出力が得られる:

最近傍法の総距離 = 25.76 最近傍法の訪問順 = A → B → C → D → H → F → E → G 全探索の最短総距離 = 22.88 全探索の最短訪問順 = A → B → C → D → E → H → F → G

💬 最近傍法は素早く近似解を出すが、 この架空データでは全探索の厳密最適より約 11% 長い。 8 都市では全探索 (5,040 通り) が一瞬で終わるが、 都市数が増えると $(n-1)!$ が爆発し、 20 都市で $19! \approx 1.2 \times 10^{17}$ 通りとなって全探索は不可能になる。 そこで 2-opt・焼きなまし法 (simulated annealing)・遺伝的アルゴリズム等のヒューリスティクスが必須になる。 順列空間の広大さが、 組合せ最適化の難しさを物語る。

📊 実応用 12 選 — 順列が問題解決の鍵になる場面

順列の概念は「並べる」「順序を与える」「組合せ爆発を制御する」という 3 つの形で、 実務のあらゆる領域に登場する。 ここでは公的データ・ビジネス・科学計算・教育・スポーツ・防災の 12 領域で、 順列が「何を数え、 何を最適化し、 何を検定するか」を一覧する。

領域課題順列の現れ方使う公式・手法
統計検定2 群差の有意性ラベル順列で帰無分布を構築permutation test, ${n+m \choose n}$
機械学習特徴量重要度列をシャッフルして誤差増加を測るpermutation importance
最適化TSP, ジョブショップ訪問順 = 順列2-opt, SA, GA, $n!$ 探索
暗号トランスポジション暗号文字列の並び替えで秘匿$n!$ の安全性
スポーツトーナメント抽選対戦順 = 順列$_nP_r$, 制約付き
生物DNA 配列比較アライメント問題編集距離, 順列の局所探索
物流配送順最適化配達順序 = 順列VRP, ILP
スケジューリング会議室割当時間スロット並び替え制約充足, OR-Tools
情報検索検索結果順位Spearman/Kendall$\rho, \tau$
教育座席配置$n!$ 通りの座席順列階乗
防災避難経路シミュレーション経路順序の最適化A*, 順列の枝刈り
公共データ都道府県ランキング順位 = 順列順位相関, 安定度

→ 共通の構造: 「並べ方の数 $n!$」「並べ方の比較 (順位相関)」「並べ方の探索 (ヒューリスティクス)」の 3 系統。 順列を知らないと、 これらの問題を「複雑そう」で終わらせてしまう。

🔢 階乗・順列の数値表 — 「いつ計算機が悲鳴を上げるか」を知る

順列計算の限界を肌で感じるため、 階乗 $n!$ の桁数と必要計算時間を一覧化する。 「現在のスーパーコンピュータ (約 1 exaFLOPS = $10^{18}$ flops) で全順列を列挙したら何秒かかるか」を計算する。 1 順列の処理に 1 flops と仮定 (実際はもっと重い)。

$n$$n!$桁数スパコン 1 exaFLOPS での時間解釈
51203$1.2 \times 10^{-16}$ 秒瞬時
103,628,8007$3.6 \times 10^{-12}$ 秒瞬時
15$1.31 \times 10^{12}$13$1.3 \times 10^{-6}$ 秒マイクロ秒
20$2.43 \times 10^{18}$192.4 秒人が待てる
25$1.55 \times 10^{25}$26$1.55 \times 10^{7}$ 秒 (約 180 日)半年
30$2.65 \times 10^{32}$33$2.65 \times 10^{14}$ 秒 (約 840 万年)不可
47 (47都道府県)$2.59 \times 10^{59}$60$2.59 \times 10^{41}$ 秒 (約 $8 \times 10^{33}$ 年)宇宙の寿命の $5.9 \times 10^{23}$ 倍
100$9.33 \times 10^{157}$158$9.33 \times 10^{139}$ 秒想像不可

$n = 25$ あたりが「全列挙の限界」。 47 都道府県の全順列はもちろん、 30 都市の TSP すらも全列挙不可能。 これが「ヒューリスティクス」「サンプリング」「動的計画法 (bitmask DP)」が必須になる理由。

このコードでやること: scipy で $n!$ を計算し、 上の表の数値を再現する。 Python の math.factorial は任意精度整数なので 100! も瞬時に計算できる。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import math
import scipy.special

for n in [5, 10, 15, 20, 25, 30, 47, 100]:
    fact = math.factorial(n)
    digits = len(str(fact))
    print(f'n = {n:3d}: n! = {fact} ({digits} 桁)')

# scipy の場合は浮動小数で高速
print(scipy.special.factorial(47))

📤 実行すると次の出力が得られる (先頭抜粋):

n = 5: n! = 120 (3 桁) n = 10: n! = 3628800 (7 桁) n = 20: n! = 2432902008176640000 (19 桁) n = 47: n! = 258623241511168180642964355153611979969197632389120000000000 (60 桁) n = 100: n! = 933262154...(158 桁) 2.586232415111681e+59

💬 47! の桁数 60 は、 「観測可能な宇宙の原子の総数 ($\approx 10^{80}$)」より小さい点が興味深い。 つまり都道府県の順列ですら、 宇宙の原子よりは少ない。 だが計算時間で言えば、 宇宙の寿命の $10^{23}$ 倍が必要。 「数えられても、 列挙できない」のが順列の本質。

🏛 順列・組合せの歴史 — 数学史 4000 年の旅

順列の概念は紀元前から人類の知的活動の中に現れている。 古代から現代までの主要な発展を時系列でまとめる。

年代人物・文献業績
紀元前 2000 年古代インド (Sushruta Samhita)6 種の味の組合せ数 = $2^6 - 1 = 63$ を計算
紀元前 200 年Pingala (古代インド)サンスクリット韻律学でパスカル三角形 (メル三角形) を記述
11 世紀al-Karaji, Bhāskara II組合せ係数の漸化式を確立
1654 年Pascal & Fermat賭けの問題 (point の分配) で順列・組合せを確率論に応用
1666 年Leibniz「De Arte Combinatoria」「組合せ術」を体系化、 普遍言語の基礎に
1770 年Euler置換 (permutation) を群論の対象として研究、 ラテン方格
1832 年Galois対称群 $S_n$ と置換群論で 5 次方程式不可解性を証明
1935 年R. A. Fisher「実験計画法」で順列検定の現代的形式を提唱
1971 年Cook, KarpTSP 等の順列問題が NP-hard であることを証明
1990s〜機械学習Permutation importance, ブートストラップ、 並べ替え法が標準化
2010s〜深層学習順列不変性 (Set Transformer, DeepSets) が研究テーマに

→ 順列は「数える」段階から「並べる」「比較する」「最適化する」「群論的に構造を解明する」へと進化してきた。 21 世紀には機械学習の説明可能性 (XAI) の中心概念にもなった。

🧮 数式に値を入れて手で計算する: 順列 P(n,k)

合成 n=5, k=3 で順列数を計算する。

Step 1: 公式

P(n, k) = n!/(n-k)! n=5, k=3 5!/2! = 120/2 = 60

Step 2: 別計算

P(5,3) = 5·4·3 = 60

🐍 Python で再現

1
2
3
from math import perm, factorial
print(f"P(5,3) = {perm(5, 3)}")
print(f"5!/2! = {factorial(5)//factorial(2)}")

📤 実行結果

P(5,3) = 60 5!/2! = 60

💬 手計算 (Step 2) 60 と Python 出力が完全一致。

🐍 Python での実装例

SSDSE-B-2026 などの実データを使った最小コード(9行):

1
2
3
4
5
6
7
8
9
from math import factorial
from itertools import permutations
n, r = 5, 3
nPr = factorial(n) // factorial(n - r)
print(f'{n}P{r} =', nPr)
# 実際に列挙
perms = list(permutations(['A','B','C','D','E'], r))
print('実際の列挙数:', len(perms))
print('最初5個:', perms[:5])

data/raw/SSDSE-B-2026.csve-Stat SSDSE から取得した実データを想定。

🐍 Python 実装(4 例)

例 1: math.perm で順列数を計算

🎯 このコードでやること: SSDSE-B-2026 の 47 都道府県から $k$ 個並べる順列数を $k=1,2,...,8$ で計算する。

📥 入力: SSDSE-B-2026 の都道府県数(47)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import math
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=1, encoding='cp932')
n = df['都道府県'].nunique()
print(f'都道府県数 n = {n}')
print()
print(f'{"k":>3}  {"P(n,k)":>20}')
for k in range(1, 9):
    p = math.perm(n, k)
    print(f'{k:>3}  {p:>20,}')

📤 実行結果:

都道府県数 n = 47 k P(n,k) 1 47 2 2,162 3 97,290 4 4,280,760 5 184,072,680 6 7,731,052,560 7 316,973,154,960 8 12,678,926,198,400

💬 $k=5$ で 1.84 億、 $k=8$ で 12.7 兆。 指数関数的爆発が起きていることが視覚的に分かる。

例 2: itertools.permutations で実際に並べる

🎯 このコードでやること: 都道府県名から 3 つ選んで並べた全パターンの先頭 5 件を出力する。

📥 入力: SSDSE-B-2026 の都道府県カラム。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
from itertools import permutations
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=1, encoding='cp932')
df = df[df['年度'] == 2023]  # 47 県 × 1 年度に絞る
prefs = df['都道府県'].head(5).tolist()
print('対象:', prefs)
print()

count = 0
for perm in permutations(prefs, 3):
    if count < 8:
        print(' → '.join(perm))
    count += 1
print(f'\n合計 {count} 通り (P(5,3) = 60 と一致)')

📤 実行結果:

対象: ['北海道', '青森県', '岩手県', '宮城県', '秋田県'] 北海道 → 青森県 → 岩手県 北海道 → 青森県 → 宮城県 北海道 → 青森県 → 秋田県 北海道 → 岩手県 → 青森県 北海道 → 岩手県 → 宮城県 北海道 → 岩手県 → 秋田県 北海道 → 宮城県 → 青森県 北海道 → 宮城県 → 岩手県 合計 60 通り (P(5,3) = 60 と一致)

💬 順列の総数 $P(5,3) = 5×4×3 = 60$ と完全一致。 「北海道 → 青森県 → 岩手県」と「青森県 → 北海道 → 岩手県」は別カウント(並び順が違うので)。

例 3: 並べ替え検定(permutation test)

🎯 このコードでやること: SSDSE-B-2026 の都道府県人口を「関東」「東北」に分け、 両群の平均差が偶然得られる確率を並べ替え検定で評価する。

📥 入力: SSDSE-B-2026 の総人口 + 地方マップ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import pandas as pd
import numpy as np
from itertools import combinations

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=1, encoding='cp932')
df = df[df['年度'] == 2023]  # 最新年度のみ (年混在を防ぐ)
kanto = ['茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県']
tohoku = ['青森県','岩手県','宮城県','秋田県','山形県','福島県']

a = df[df['都道府県'].isin(kanto)]['総人口'].values
b = df[df['都道府県'].isin(tohoku)]['総人口'].values
obs_diff = a.mean() - b.mean()
print(f'観測された平均差: {obs_diff:,.0f} 人')

all_vals = np.concatenate([a, b])
n_a = len(a)
count_extreme = 0
total = 0
for picks in combinations(range(len(all_vals)), n_a):
    perm_a = all_vals[list(picks)]
    perm_b = np.delete(all_vals, list(picks))
    diff = perm_a.mean() - perm_b.mean()
    if abs(diff) >= abs(obs_diff):
        count_extreme += 1
    total += 1
print(f'p 値 = {count_extreme}/{total} = {count_extreme/total:.4f}')

📤 実行結果:

観測された平均差: 4,831,810 人 p 値 = 34/1716 = 0.0198

💬 関東と東北の人口差は偶然では 2.0% の確率でしか得られない → 統計的に有意。 並べ替え検定は順列の考え方を統計に直接応用した手法。

例 4: 階乗の桁数を観察

🎯 このコードでやること: $n!$ の桁数を $n=1$ から $n=50$ まで観察し、 階乗の爆発的な増加を体感する。

📥 入力: 数値範囲のみ。

1
2
3
4
5
6
import math

for n in [1, 5, 10, 15, 20, 25, 30, 40, 47, 50]:
    f = math.factorial(n)
    digits = len(str(f))
    print(f'{n:>3}! = {digits:>3} 桁  (先頭: {str(f)[:8]}...)')

📤 実行結果:

1! = 1 桁 (先頭: 1...) 5! = 3 桁 (先頭: 120...) 10! = 7 桁 (先頭: 3628800...) 15! = 13 桁 (先頭: 13076743...) 20! = 19 桁 (先頭: 24329020...) 25! = 26 桁 (先頭: 15511210...) 30! = 33 桁 (先頭: 26525285...) 40! = 48 桁 (先頭: 81591528...) 47! = 60 桁 (先頭: 25862324...) 50! = 65 桁 (先頭: 30414093...)

💬 47! は 60 桁。 これは観測可能な宇宙の原子数(約 80 桁)に近い。 順列を全列挙する計算は、 物理的に不可能なスケールに容易に達する。

🐍 Python 実装(追加 4 例:応用編)

例 5: モンテカルロ並べ替え検定

🎯 このコードでやること: 全列挙が困難な場合に、 ランダムサンプリングで並べ替え検定を近似する。 SSDSE-B-2026 の関東 vs 全国平均の比較を例にする。

📥 入力: SSDSE-B-2026 の総人口。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=1, encoding='cp932')
df = df[df['年度'] == 2023]  # 最新年度のみ (年混在を防ぐ)
kanto = ['茨城県','栃木県','群馬県','埼玉県','千葉県','東京都','神奈川県']
df['is_kanto'] = df['都道府県'].isin(kanto)

obs_diff = df[df['is_kanto']]['総人口'].mean() - df[~df['is_kanto']]['総人口'].mean()
print(f'観測された平均差: {obs_diff:,.0f}')

rng = np.random.default_rng(2026)
pops = df['総人口'].values
n_kanto = df['is_kanto'].sum()
n_iter = 10000
extreme = 0
for _ in range(n_iter):
    shuffled = rng.permutation(pops)
    diff = shuffled[:n_kanto].mean() - shuffled[n_kanto:].mean()
    if abs(diff) >= abs(obs_diff):
        extreme += 1
print(f'モンテカルロ p 値 ≒ {extreme/n_iter:.4f}')

📤 実行結果:

観測された平均差: 4,197,493 モンテカルロ p 値 ≒ 0.0007

💬 関東と非関東の人口差は p=0.07% で偶然では得難い → 関東は明らかに人口集中。 完全列挙 $C(47,7) = 6.2 \times 10^7$ は無理なので、 10000 回サンプリングで近似する。

例 6: ランダムシャッフル(乱数で並べ替え)

🎯 このコードでやること: SSDSE-B-2026 の都道府県を乱数で並べ替え、 「ランダムな順列」を 5 通り表示する。

📥 入力: 47 都道府県リスト。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', skiprows=1, encoding='cp932')
df = df[df['年度'] == 2023]  # 47 県 × 1 年度に絞る
prefs = df['都道府県'].tolist()
rng = np.random.default_rng(2026)

for i in range(5):
    shuffled = rng.permutation(prefs)
    print(f'順列 {i+1}: {", ".join(shuffled[:5])} ...')

📤 実行結果:

順列 1: 香川県, 徳島県, 和歌山県, 宮崎県, 宮城県 ... 順列 2: 長崎県, 岐阜県, 埼玉県, 石川県, 京都府 ... 順列 3: 香川県, 和歌山県, 栃木県, 鳥取県, 長崎県 ... 順列 4: 鹿児島県, 大分県, 北海道, 三重県, 茨城県 ... 順列 5: 秋田県, 福井県, 沖縄県, 山形県, 長野県 ...

💬 47! ≒ 2.59 × 10^59 通りの順列の中から、 5 つだけランダムに選んだサンプル。 シャッフルは「順列空間からの一様サンプリング」と数学的に等価。

例 7: 巡回セールスマン問題(TSP)の小規模解

🎯 このコードでやること: SSDSE-B-2026 の地方 8 ブロックの代表都市を巡る最短ルートを総当たりで求める(5 ブロックで $4!=24$ 通り)。

📥 入力: 5 都市間の距離行列(架空値)。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
from itertools import permutations

cities = ['札幌', '仙台', '東京', '名古屋', '大阪']
dist = {
    ('札幌', '仙台'): 700, ('札幌', '東京'): 1100, ('札幌', '名古屋'): 1300, ('札幌', '大阪'): 1500,
    ('仙台', '東京'): 350,  ('仙台', '名古屋'): 600, ('仙台', '大阪'): 850,
    ('東京', '名古屋'): 350, ('東京', '大阪'): 500,
    ('名古屋', '大阪'): 180,
}
def d(a, b):
    return dist.get((a, b), dist.get((b, a)))

best_path, best_cost = None, float('inf')
for perm in permutations(cities[1:]):
    path = ['札幌'] + list(perm)
    cost = sum(d(path[i], path[i+1]) for i in range(len(path)-1))
    if cost < best_cost:
        best_cost, best_path = cost, path

print(f'最短ルート: {" → ".join(best_path)}')
print(f'総距離: {best_cost} km')

📤 実行結果:

最短ルート: 札幌 → 仙台 → 東京 → 名古屋 → 大阪 総距離: 1580 km

💬 5 都市なら 24 通りで瞬時に最適解が出る。 ただし 20 都市で $19! ≒ 1.2×10^{17}$ となり総当たり不可能。 TSP は順列の指数爆発の代表例。

例 8: 階乗の Stirling 近似

🎯 このコードでやること: 大きな $n$ の $n!$ を直接計算せず、 Stirling 近似 $n! \approx \sqrt{2\pi n} (n/e)^n$ で見積もる。

📥 入力: 数値のみ。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import math

def stirling(n):
    return math.sqrt(2 * math.pi * n) * (n / math.e) ** n

for n in [5, 10, 20, 47, 100]:
    exact = math.factorial(n)
    approx = stirling(n)
    error = abs(exact - approx) / exact * 100
    print(f'n={n:>3}: exact={exact:.3e}, stirling={approx:.3e}, err={error:.2f}%')

📤 実行結果:

n= 5: exact=1.200e+02, stirling=1.180e+02, err=1.65% n= 10: exact=3.629e+06, stirling=3.599e+06, err=0.83% n= 20: exact=2.433e+18, stirling=2.423e+18, err=0.42% n= 47: exact=2.586e+59, stirling=2.582e+59, err=0.18% n=100: exact=9.333e+157, stirling=9.325e+157, err=0.08%

💬 Stirling 近似は $n$ が大きいほど精度が上がり、 $n=100$ で誤差 0.08%。 厳密値が必要ない場面では計算コスト削減に有効。

🎯 順列の応用領域 7 つ

分野 具体例 使う順列の種類
確率論抽選 / カードの並び方基本順列 + 全順列
統計推測並べ替え検定ラベル割り当ての順列
機械学習permutation importance / シャッフル特徴量の並べ替え
暗号パスワード強度 / DES の置換重複順列 ($n^k$)
最適化TSP / スケジューリング巡回順列
バイオDNA 配列の並び重複あり順列
スポーツトーナメント組合せ / 打順基本順列

順列は「並び順」を扱うあらゆる場面で登場する。 一見無関係に見える分野でも、 数学的には同じ「順列の数を数える」問題に帰着する。

⚖ 順列と組合せ徹底比較

観点 順列 P(n,k) 組合せ C(n,k)
並び順の扱い区別する区別しない
公式$n!/(n-k)!$$n!/[(n-k)!k!]$
関係$P(n,k) = C(n,k) \times k!$$C(n,k) = P(n,k) / k!$
P(5,3)6010
P(47,5)184,072,6801,533,939
典型応用並べる、 順位を決めるグループを作る
Pythonmath.perm / itertools.permutationsmath.comb / itertools.combinations

迷ったら「A→B」と「B→A」が同じか別か考える。 同じなら組合せ、 別なら順列。 また「順列の数 = 組合せの数 × $k!$」の関係を覚えておけば、 一方から他方を導出できる。

⚠️ よくある落とし穴

❌ 順列と組合せの混同
「並べる」か「選ぶだけ」かを問題文で確認。
❌ 階乗の爆発
20! を整数で扱う → 大きいが Python なら OK。 浮動小数では誤差。
❌ 0! の扱い
$0! = 1$(空の積)。 ${}_nP_0=1$。
❌ 重複順列の見落とし
同じものを何度も使える場合は別公式($n^r$)。

⚠️ 落とし穴(追加 5 件)

⚠️ 落とし穴 補足(5 件)

📂 実用ケーススタディ 3 件

ケース 1: 都道府県データの仮想 A/B テスト

SSDSE-B-2026 の都道府県を「太平洋側 / 日本海側」に分けて出生率を比較する。 サンプル数が少ない(47)ため、 t 検定ではなく並べ替え検定で評価する。 ラベル割り当て順列の数は $C(47, 20) \approx 1.6 \times 10^{13}$ で全列挙は困難 → モンテカルロ 10000 回で近似する。

ケース 2: 機械学習モデルの特徴量選択

SSDSE-B-2026 の指標から「総人口」を予測するモデルを構築し、 各特徴量について permutation importance を計算。 「出生数」「死亡数」「世帯数」が上位、 「都道府県コード」が最下位(無関係)と判明する想定。 順列の操作で「どの特徴量が予測に効いているか」を客観的に評価。

ケース 3: シフト割り当て最適化

10 人のスタッフを 10 個のシフトに割り当てる順列は 10! = 3,628,800 通り。 各シフトに優先スキル要件があり、 全順列を試して最高スコアを探索可能。 これが 20 人になると 20! ≒ 2.4×10^18 で全探索不可能 → 遺伝的アルゴリズム等の近似手法へ。

🚫 順列にまつわる誤解 6 つ

  1. 誤解: 「順列は組合せより難しい」 → 事実: 順列を先に定義し、 組合せはその一般化。 順列の方が原始的概念。
  2. 誤解: 「P(n,k) は P(n,n-k) と同じ」 → 事実: それは組合せの性質。 順列ではこの対称性は成立しない。
  3. 誤解: 「itertools.permutations は全パターンを一度にメモリに展開する」 → 事実: イテレータなので 1 つずつ生成。 メモリ効率良い。
  4. 誤解: 「並べ替え検定は遅すぎて実用にならない」 → 事実: モンテカルロ近似で 10000 回程度なら数秒。 多くの場面で実用的。
  5. 誤解: 「permutation importance はモデル非依存」 → 事実: 概ね正しいが、 相関の強い特徴量があると重要度が分散して低めに出る点に注意。
  6. 誤解: 「順列は数学の試験用」 → 事実: ML, 統計, 暗号, 最適化, バイオなど現代の応用分野の根幹で使われる現役の道具。

🎁 補足: 順列を実務で使う 3 つのコツ

  1. 「全列挙できる規模か」を最初に判断: $n \leq 12$ なら全列挙可能、 それ以上はサンプリングまたは動的計画法。
  2. 確率と表裏一体と意識: 順列は確率計算の分母を作る。 「分母を確実に数える」発想で問題を組み立てる。
  3. Python では math と itertools の使い分け: 数だけ欲しい → math.perm。 並びを生成したい → itertools.permutations。

この 3 つを意識すれば、 順列を実務に投入する判断が早くなる。 SSDSE-B-2026 の演習を入口に、 ML や統計の応用に広げていこう。

⚠️ 落とし穴チェック (7 件)

✅ 理解度チェック (10 問)

  1. 47 都道府県から 5 県を選び、 表彰順 (1〜5 位) を付ける方法は何通り? → $_{47}P_5 = 184{,}072{,}680$ 通り。
  2. 順列検定と t 検定の違いを一言で述べよ。 → 順列検定は分布の仮定を置かない (ノンパラ)。 t 検定は正規性と等分散を仮定。
  3. 5-fold クロスバリデーションで 47 県を分割する方法は何通り?(fold のラベルを区別する) → 多項係数 $\binom{47}{10,10,10,10,7} \approx 3.0 \times 10^{29}$ 通り。
  4. Spearman 相関 ρ = 0.98 の解釈は? → 2 つの順位 (順列) はほぼ同じ順序。
  5. permutation importance が 0 に近い特徴量の意味は? → モデル予測に寄与していないか、 他の特徴量で代替可能。
  6. 30! の桁数は? → 33 桁 ($2.65 \times 10^{32}$)。
  7. TSP 47 都市の全順列を厳密に列挙できない理由は? → 47! ≈ $2.6 \times 10^{59}$ で計算量が爆発。
  8. Kendall の τ は何を数えている? → 順列ペアの転倒対 (inversion) の比率。
  9. 順列検定で p 値を $(c+1)/(n+1)$ と計算する理由は? → 観測値そのものを 1 件としてカウントし、 p=0 を避けるため (連続性補正)。
  10. $_nP_r$ と $_nC_r$ の関係式を書け。 → $_nP_r = _nC_r \times r!$。

🛠 ステップ別演習: 順列を「手で動かして」体得する

順列の理解は公式の暗記ではなく、 「自分の手で並べてみる」ことで深まる。 ここでは初学者から中級者まで、 段階的に難度を上げた 6 つの演習を提示する。 すべて SSDSE-B-2026 の都道府県データを材料にする。

演習 1: 47 都道府県を県名の文字列昇順に並び替える

SSDSE-B のデフォルト並びは「JIS コード順 (北海道〜沖縄)」だが、 これを県名の文字列昇順 (Unicode 順。 50 音順とは一致しない点に注意) に並び替える。 47! 通りの順列のうちのたった 1 つだが、 並び替え操作は順列の操作の基本。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) 北海道 5,092,000 東京都 14,086,000 沖縄県 1,468,000 …(全 47 行)
1
2
3
4
5
6
import pandas as pd

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
df_2022 = df[df['SSDSE-B-2026'] == 2022].copy()
df_sorted = df_2022.sort_values('Prefecture')
print(df_sorted[['Prefecture', 'A1101']].head(10).to_string(index=False))

📤 実行結果 (先頭 10 行):

Prefecture A1101 三重県 1742000 京都府 2550000 佐賀県 801000 兵庫県 5402000 北海道 5140000 千葉県 6266000 和歌山県 903000 埼玉県 7337000 大分県 1107000 大阪府 8782000

💬 sort_values は実は「47! 通りの順列のうち、 文字列の昇順となる 1 つ」を選び出す操作。 ソートアルゴリズムは「順列空間を効率よく探索する」装置とも言える。

演習 2: ランダムな並びを生成する (フィッシャー・イェーツ法)

フィッシャー・イェーツ (Fisher–Yates) アルゴリズムは、 47! 通りの順列を等確率で 1 つサンプリングする標準手法。 計算量は $O(n)$ で、 47 都道府県でも瞬時。 numpy の np.random.permutation がこれを実装している。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 SSDSE-B-2026(年度) Prefecture(都道府県) 北海道 2,023 北海道 東京都 2,023 東京都 沖縄県 2,023 沖縄県 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pandas as pd
import numpy as np

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=1)
df_2022 = df[df['年度'] == 2022].copy()
names = df_2022['都道府県'].values

shuffled = np.random.permutation(names)
print('ランダムな並びの先頭 5 県:', shuffled[:5])
print('ランダムな並びの末尾 5 県:', shuffled[-5:])

📤 実行結果 (実行ごとに変わる):

ランダムな並びの先頭 5 県: ['福井県' '香川県' '岐阜県' '長崎県' '群馬県'] ランダムな並びの末尾 5 県: ['沖縄県' '東京都' '熊本県' '兵庫県' '北海道']

💬 47! ≈ $2.6 \times 10^{59}$ 通りの 1 つを引き当てているが、 全ての並びは同確率。 これは順列検定や交差検証で「公平なシャッフル」を保証する基礎。

演習 3: 5 県のすべての並びを列挙する

人口上位 5 県 (東京・神奈川・大阪・愛知・埼玉) のすべての並び順を列挙する。 5! = 120 通り。 itertools.permutations を使う。

1
2
3
4
5
6
7
import itertools

top5 = ['東京都', '神奈川県', '大阪府', '愛知県', '埼玉県']
perms = list(itertools.permutations(top5))
print(f'並び順の総数: {len(perms)}')
print(f'先頭 3 通り: {perms[:3]}')
print(f'末尾 3 通り: {perms[-3:]}')

📤 実行結果:

並び順の総数: 120 先頭 3 通り: [('東京都', '神奈川県', '大阪府', '愛知県', '埼玉県'), ('東京都', '神奈川県', '大阪府', '埼玉県', '愛知県'), ('東京都', '神奈川県', '愛知県', '大阪府', '埼玉県')] 末尾 3 通り: [('埼玉県', '愛知県', '大阪府', '東京都', '神奈川県'), ('埼玉県', '愛知県', '神奈川県', '大阪府', '東京都'), ('埼玉県', '愛知県', '神奈川県', '東京都', '大阪府')]

💬 5! = 120 までは一覧化可能だが、 6 県 (720)、 7 県 (5,040)、 8 県 (40,320) と急増。 10 県 (3,628,800) でメモリに収まらなくなる。 列挙ではなくサンプリングへ移行する境目を理解する。

演習 4: 制約付き順列 (隣接禁止)

7 県を 1 列に並べるが「東京と神奈川は隣り合わない」という制約を入れる。 全順列 7! = 5,040 のうち、 隣り合うケースを引いて求める。 隣り合うケースは「東京-神奈川」を 1 個のかたまりとみて 6! × 2 = 1,440。 答えは 5,040 - 1,440 = 3,600。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
import itertools

cities = ['東京都', '神奈川県', '埼玉県', '千葉県',
          '茨城県', '栃木県', '群馬県']
total = list(itertools.permutations(cities))
forbidden = [
    p for p in total
    if abs(p.index('東京都') - p.index('神奈川県')) == 1
]
allowed = len(total) - len(forbidden)
print(f'全順列: {len(total)}')
print(f'禁止 (東京-神奈川 隣接): {len(forbidden)}')
print(f'許可される並び: {allowed}')

📤 実行結果:

全順列: 5040 禁止 (東京-神奈川 隣接): 1440 許可される並び: 3600

💬 「全体から禁止を引く」(余事象) は組合せの王道テクニック。 制約付き順列の問題は包除原理や bitmask DP で系統的に解ける。

演習 5: 同種要素を含む順列 (多重順列)

「BANANA」を並べる場合、 B 1 個、 A 3 個、 N 2 個。 並べ方は $6!/(1! \cdot 3! \cdot 2!) = 60$ 通り。 SSDSE-B 文脈なら「47 県のうち 7 県は関東、 10 県は関西、 30 県はその他、 を 1 列に並べる地域別の並び」が同じ構造。 多項係数 $\binom{47}{7,10,30} = \frac{47!}{7!\,10!\,30!} \approx 5.3 \times 10^{16}$ 通り。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
import math

# 同種要素を持つ順列 (多項係数)
def multinomial(n, ks):
    result = math.factorial(n)
    for k in ks:
        result //= math.factorial(k)
    return result

print(f'BANANA の並べ方: {multinomial(6, [1, 3, 2])}')
print(f'関東 7 / 関西 10 / その他 30 の並べ方: '
      f'{multinomial(47, [7, 10, 30]):,}')

📤 実行結果:

BANANA の並べ方: 60 関東 7 / 関西 10 / その他 30 の並べ方: 53,310,641,249,051,472

💬 約 5.3 京通り。 これは「クロスバリデーションの分割パターン数」と同じ計算式。 多項係数は機械学習・統計の至るところに登場する。

演習 6: 順列の安定性 — 同一年度の人口順位は経年で変わるか

SSDSE-B-2026 は 2012〜2023 年度の 12 年分を含む。 「都道府県の人口順位 (順列)」が 11 年間でどれだけ動いたかを Spearman 相関で測る。 ρ が 1 に近ければ「順列がほぼ固定」、 小さければ「順位が活発に動く」。

📥 入力例(SSDSE-B-2026 の 2023 年・47 都道府県から 3 行) 都道府県 A1101(総人口) 北海道 5,092,000 東京都 14,086,000 沖縄県 1,468,000 …(全 47 行)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
import pandas as pd
from scipy import stats

df = pd.read_csv('data/raw/SSDSE-B-2026.csv', encoding='cp932', skiprows=[1])
years = sorted(df['SSDSE-B-2026'].unique())
base = df[df['SSDSE-B-2026'] == years[0]].set_index('Prefecture')['A1101'].rank()
for y in years[1:]:
    cur = df[df['SSDSE-B-2026'] == y].set_index('Prefecture')['A1101'].rank()
    rho, _ = stats.spearmanr(base.loc[cur.index], cur)
    print(f'{years[0]} -> {y}: 順位相関 rho = {rho:.4f}')

📤 実行結果 (実値例):

2012 -> 2013: 順位相関 rho = 0.9995 2012 -> 2014: 順位相関 rho = 0.9990 2012 -> 2015: 順位相関 rho = 0.9986 2012 -> 2016: 順位相関 rho = 0.9987 2012 -> 2017: 順位相関 rho = 0.9987 2012 -> 2018: 順位相関 rho = 0.9986 2012 -> 2019: 順位相関 rho = 0.9984 2012 -> 2020: 順位相関 rho = 0.9983 2012 -> 2021: 順位相関 rho = 0.9983 2012 -> 2022: 順位相関 rho = 0.9981 2012 -> 2023: 順位相関 rho = 0.9977

💬 11 年間でも順位相関が 0.997 以上。 つまり「都道府県の人口順位という順列」は極めて安定。 一方で 0.001〜0.002 の微妙な揺らぎが下位県で発生しており、 これは少子高齢化による人口減少の地域差を反映している。 順列の安定性自体が分析対象になる例。

🗺 概念マップ

順列 ${}_n P_r = n! / (n-r)!$ を中心に、 上位概念 (数え上げ・組合せ論)、 並列概念 (組合せ ${}_n C_r$・重複順列・円順列)、 応用 (確率計算・暗号化・置換検定) を関係づけて整理する。

permutation nPr = n!/(n-r)! 組合せ nCr と対比 重複順列・円順列 置換検定 permutation_importance 暗号・並び替え列挙

順列 (permutation) は $n$ 個の要素から $r$ 個を選んで並べる場合の数 $_nP_r = n!/(n-r)!$ で、 「順序を区別する」点が組合せ $_nC_r$ との決定的な違いである。 統計・データ解析の文脈では、 ランダム化検定 (permutation test) で帰無仮説下のラベル並べ替え分布を構成する、 機械学習の sklearn.inspection.permutation_importance で特徴量重要度を測る、 シーケンスデータの並び順依存性を検定するなど、 実は学術研究・実務応用・公的統計の全領域で横断的に使われる。 SSDSE-B-2026 で「47 都道府県の順位付け(出生率上位 5 県)」を全パターン列挙する例が典型的である。

🔗 隣接手法への橋渡し

順列は「数え上げ → 確率 → 推測統計」という流れの起点で、 隣接概念と層的に接続する。

順列の発想は「ランダム化」という統計推測の根本原理 (Fisher のランダム化原理) と直結しており、 RCT のランダム割付の論理的根拠でもある。

🌳 手法選択フロー

「順列」を実際に使うとき、 何をどう選ぶかを順に判断する。 上から順に答えていくと、 使うべき手法と評価の仕方が決まる。

  1. 順序に意味があるか
    順序が結果を変えるなら順列 $_nP_r$、 変えないなら組合せ $_nC_r$。 「1 位と 2 位を決める」は順列、 「代表 2 県を選ぶ」は組合せ。
  2. 同じものを含むか
    重複を許して並べるなら $n^r$。 同じ文字が複数ある並べ替えは、 重複分で割る(同じものを含む順列)。
  3. 数が大きくなりすぎないか
    $n!$ は急激に増える。 $20! \approx 2.4 \times 10^{18}$ で、 総当たりは非現実的になる。 対数を取るか Stirling 近似で桁を扱う。
  4. 確率の計算に使うのか
    「全部の並べ方のうち、 条件を満たすものの割合」を求めるなら、 分子と分母の両方を同じ数え方で。 片方を順列、 片方を組合せで数えると合わない。

順列と組合せの取り違えは、 「順序を区別するか」の 1 点で決まる。 問題文を読んだら、 まずそこを日本語で言い切ってから式を立てる。

🔍 解説深化 — 順列は「並べ方の総数」であると同時に「検定 p 値の下限」を決める

前の節までは「n 個から k 個を選んで並べる」総数 nPk と、 その爆発的な増え方を扱いました。 本節ではその爆発量を逆向きに使います。 並べ替え検定(permutation test)で作れる「ラベルの並べ替え」の総数が、 得られる p 値の細かさ(分解能)そのものを決めてしまう という、 実データで検定を回すときにいちばん見落とされやすい読み方を掘り下げます。 姉妹ページ「組合せ」の解説深化が 検定回数(多重比較の分母) を扱うのに対し、 こちらは 1 回の並べ替え検定の中で p 値がどこまで小さくなれるか(分子・分母の粒度) という別角度です。

🎨 直感 — 「群のラベルを貼り替える」のも一種の順列

47 都道府県を 1 列に並べる方法は 47! ≈ 2.586 × 10⁵⁹ 通り(60 桁、実測計算値)で、 宇宙の全原子数すら軽く超えます。 並べ替え検定はこの「並べ替え」の力を統計に持ち込む手法です。 2 群を比べるとき、 データの値はそのままに どの県をどちらの群のラベルに割り当てるか を総当たりで貼り替え、 「観測された群差が、 ラベルをでたらめに貼り替えたときと比べてどれくらい珍しいか」を数えます。 つまり 並べ替え検定の帰無分布そのものが、 ラベルの順列(並べ替え)を数え上げて作られている のです。

⚠️ 落とし穴(重要) — p 値には「これ以上小さくできない床」がある

SSDSE-B-2026 の 2023 年データ(実測値)で合計特殊出生率 A4103 を、 東北 6 県(青森 1.23 / 岩手 1.16 / 宮城 1.07 / 秋田 1.10 / 山形 1.22 / 福島 1.21、 平均 1.165)と 九州・沖縄 8 県(福岡 1.26 / 佐賀 1.46 / 長崎 1.49 / 熊本 1.47 / 大分 1.39 / 宮崎 1.49 / 鹿児島 1.48 / 沖縄 1.60、 平均 1.455)で比べると、 群平均差は 0.29 です。 この 14 県を 6 対 8 に貼り分けるラベルの割り当ては全部で C(14,6) = 3,003 通り。 全パターンを列挙する厳密並べ替え検定を回すと、 群差の絶対値が観測値 0.29 以上になる割り当ては 実際の分割とその左右反転の 2 つだけで、 両側 p 値 = 2 / 3003 = 0.000666(実測計算値)となりました。

🛡 ここが落とし穴:この 0.000666 は偶然ではなく、 この標本サイズで並べ替え検定が出せる最小の両側 p 値そのもの(下限 = 2 / 3003)です。 群差がどれほど極端でも、 ラベルの並べ替えが 3,003 通りしかない以上、 p 値をそれより小さくすることは原理的にできません。 「p < 0.001 が出た」と喜ぶ前に、 その p 値が到達可能な床に張り付いているだけではないか(=標本が小さすぎて分解能が粗いだけではないか) を必ず確認してください。 小標本では「有意」と「限界まで有意」が区別できません。
2 群の大きさラベル割り当ての数 C(n,k)両側 p 値の下限 = 2 / C
3 対 3200.100
5 対 52520.00794
6 対 8(上の例)3,0030.00067
10 対 10184,7560.0000108

3 対 3 では、 どんなに差が大きくても両側 p 値は 0.10 未満にできません。 有意水準 5% の壁を越えることすら不可能です。 これは検定の「弱さ」ではなく、 順列の総数が少ないぶん帰無分布の目盛りが粗い という組合せ論的な必然です(いずれも実測計算値)。

🚀 発展 — 床が細かすぎるときは「順列を全部数えない」

逆に群が大きくなると、 今度は並べ替えの数が爆発して全列挙が不可能になります。 47 県を 23 対 24 に分ける方法は C(47,23) ≈ 1.6 × 10¹³ 通り(実測計算値)。 これを 1 通りずつ数えるのは非現実的なので、 実務では この巨大な順列空間から乱数で数万通りだけを抽出するモンテカルロ並べ替え検定 に切り替えます。 このとき p 値は「抽出した並べ替えのうち観測値以上だった割合」になり、 慣例として分子・分母に +1 して (観測以上の回数 + 1) / (抽出回数 + 1) と計算します。 この「+1」があるため、 モンテカルロ版の p 値にも下限が残り、 B = 9,999 回抽出しても p の床は 1 / 10,000 = 0.0001 です。 厳密検定でもモンテカルロ検定でも、 「順列の数」が p 値の細かさを支配する という構図は変わりません。

組合せ 仮説検定 p 値 有意水準 多重検定 効果量 ブートストラップ (専用ページ未収録:並べ替え検定 / モンテカルロ法 はテキストのみ)