
皆さんは「ランダム」という言葉を聞いて、どんなイメージを持つでしょうか?おそらく、サイコロを振る、ルーレットが回る、宝くじを引く、といった予測不能な出来事を思い浮かべるかもしれません。ゲームやギャンブルの世界では、この予測不能性が面白さを生み出す要素となっています。
実は、コンピューターのアルゴリズムの世界でも、「ランダム性」は非常に強力なツールとなり得ます。通常、アルゴリズムはどんな入力に対しても正しい答えを効率的に出すように設計されます。しかし、時には「意地悪な入力」が存在し、特定のアルゴリズムの効率を著しく低下させることがあります。例えば、数字当てゲームで1から100までの数値を順番に当てることを考えると、もし隠された数が91だった場合、91回質問する必要があり、非常に非効率です。
このような「意地悪な入力」を回避したり、あるいは問題そのものが持つ「ランダムな性質」を巧みに利用したりすることで、驚くほど効率的なアルゴリズムを構築できることがあります。
本記事では、「ランダム性」をアルゴリズム設計と解析の強力なツールとして捉え、以下の2つの主要なトピックを通して、その魅力を探求します。
乱択アルゴリズム (Randomized Algorithms): アルゴリズム自身がランダムな選択を行うことで、どんな入力に対しても(期待値として)効率的なパフォーマンスを達成する手法。
確率的解析 (Probabilistic Analysis): アルゴリズムは決定的だが、入力が特定の確率分布に従うと仮定し、その平均的なパフォーマンスを解析する手法。
まずは、皆さんも親しみやすいゲームを通して、乱択アルゴリズムのアイデアに触れてみましょう。
乱択アルゴリズム
数当てゲームとランダム性の力
1から100までの整数が書かれたカードが10枚あります。その中の1枚を選んで、書かれた数を当てれば「当たり」です。あなたは質問をすることができます。例えば「64ですか?」と質問して、当たったかどうかを教えてもらいます。
このゲーム、何回質問すれば平均的に当てられるでしょうか?
一番シンプルな戦略は、ランダムに1~100の中から1つの数を言い続ける、というものです。もし、カードに書かれた数が10, 34, 40, 60, 62, 63, 70, 80, 85, 90のいずれかだった場合、あなたはこれらの数のどれか1つを当てるまで、ランダムな数を言い続けます。
あなたが1回の質問で当たりを引く確率は、当たりが10種類あるので10/100 = 1/10です。外れる確率は90/100 = 9/10です。

質問回数を \(Q\) とすると、 \(Q=1\) 回で当たる確率は \(1 / 10\) です。 \(Q=2\) 回で当たる確率は、 1 回目に外れて 2 回目に当たる確率なので \((9 / 10) \times(1 / 10)\) です。 \(Q=k\) 回で当たる確率は \((9 / 10)^{k-1} \times(1 / 10)\) です。
期待される質問回数は、これらの確率の和となります。
$$
E[Q]=\sum_{k=1}^{\infty} k \cdot\left(\frac{9}{10}\right)^{k-1} \cdot \frac{1}{10}
$$
これは幾何分布の期待値の形です。成功確率 \(p=1 / 10\) の場合の期待値は \(1 / p\) なので、
$$
E[Q]=\frac{1}{1 / 10}=10
$$
となります。平均的には10回の質問で当たりを引けると期待できます。実際の質問回数は運によって変わりますが、確率分布を見ると、質問回数は平均値 である10回のあたりに最も集中し、それよりずっと多くなる確率は急速に減少します。

しかし、なぜ「ランダムに選ぶ」ことが重要なのでしょうか?もしランダムではない、決定的な戦略をとるとどうなるか考えてみましょう。
決定的な戦略1:1, 2, 3, ... と順番に質問する
もしカードの数が1だったら1回で当たります。しかし、もしカードの数が91だったら、91回質問する必要があります。最悪ケースでは100回(カードにない数を選び続けた場合)や、運悪く90以下の数を質問し尽くして最後に残った数が91だった場合など、非常に多くの回数がかかります。特に、もしカードに書かれた数が意図的に(例えば、あなたの戦略を知っている人が)大きな数を選んでいた場合、この戦略は非常に非効率になります。
決定的な戦略2:100, 99, 98, ... と逆順に質問する
今度はカードの数が100だったら1回で当たります。しかし、もしカードの数が10だったら、91回質問する必要があります。やはり意地悪な入力に対しては非効率です。
このように、決定的なアルゴリズムは、入力の性質(この場合はカードに書かれた数の選び方)によっては極端に効率が悪くなる(最悪ケース)可能性があります。一方、ランダムな戦略は、入力によらず(平均的に見れば)安定したパフォーマンスを示すことが期待できます。今回のゲームでは、ランダムに選ぶ戦略は、平均10回で当たりを引くことが保証されます。カードの数の選び方がどんなに意地悪でも、あなたのランダムな選択によってその意地悪さを「かき混ぜて」しまうからです。
このような「サイコロを振る(乱数を使う)」ことで、問題を効率的に解ける(あるいは効率的に解析できる)アルゴリズムを乱択アルゴリズム (Randomized Algorithm) と呼びます。
乱択アルゴリズムは、その振る舞いが入力だけでなく、乱数発生器によって生成される値によっても決定されます。乱数発生器としては、例えばRANDOM(a, b)のように、aとbを含む範囲の整数を一様な確率で返すものを利用できると仮定します。(実際には、多くのプログラミング環境では擬似乱数発生器が使われます。これは統計的にランダムに見える決定的なアルゴリズムです。)
乱択アルゴリズムの性能を評価する際には、乱数発生器が返す値の分布にわたる実行時間の期待値を評価します。これを期待実行時間 (expected running time) と呼びます。入力がランダムなアルゴリズムの平均ケースの実行時間 (average-case running time) とは区別されます。一般に、確率分布がアルゴリズムへの入力にかかる場合は平均ケースの実行時間について議論し、アルゴリズム自体がランダムな選択を行う場合は期待実行時間について議論します。
乱択アルゴリズムの例:Quick Select
次に、乱択アルゴリズムの力をより本格的な問題で見てみましょう。
問題
\(N\) 個の整数 \(a_1, a_2, \ldots, a_N\) が与えられます。この中で \(K\) 番目に小さい数は?
例えば、 \(24,58,49,77,81,36,53\) という 7 つの数があり、 \(K=4\) 番目に小さい数を求める場合を考えます。数を小さい順に並べると \(24,36,49,53,58,77,81\) となり、4番目に小さい数は 53 です。
この問題を解く最も簡単な方法は、与えられた配列をソートすることです。ソートされた配列のK番目の要素が答えになります。マージソートやクイック ソートなど、多くの効率的なソートアルゴリズムは \(O(N \log N)\) の計算量でこれを実現できます。これは決定的なアルゴリズムとしては非常に高速です。
しかし、乱数を使うことで、平均計算量(average complexity)が \(O(N)\) になるアルゴリズムが存在します。それがQuick Selectです。Quick Selectは、ソ —トのアルゴリズムであるQuick Sortと似たアイデアに基づいています。
Quick Selectのアイデア
- 与えられた配列の中から、ランダムに1つの値(ピボット)を選びます。
- 選んだピボットを基準に、配列の要素を「ピボットより小さい要素のグループ」と「ピボット以上の要素のグループ」に分割します。この分割操作 (パーティション)は \(O(N)\) の時間で可能です。
- ピボット自体の最終的な順位が分かります。例えば、ピボットより小さい要素が \(S\) 個あったとすると、ピボットは全体で \((S+1)\) 番目に小さい数です。 もし求めている \(K\) 番目の数が、ピボットの順位と一致すれば、ピボットが答えです。
- もし \(K\) 番目の数がピボットより小さいグループにあるなら、そのグループの中から \(K\) 番目に小さい数を求める問題に帰着します。
- もし \(K\) 番目の数がピボットより大きいグループにあるなら、そのグループの中から \(K\) 番目に小さい数を求める問題に帰着します。ただし、K番目の数 は、元の問題におけるピボットの順位を考慮して、グループ内での相対的な順位に変換する必要があります。
Quick Selectの動き

例 : \(53,58,24,77,81,49,90,37,19\) の中から \(K=5\) 番目に小さい数を求める。
①ランダムに1つの値を選びます。例えば37を選んだとします。

②37をピボットに分割します。「37より小さいグループ」 \(\{24,19\}\) 、「37」自身、「37より大きいグループ」 \(\{53,58,77,81,49,90\}\) 。

③グループごとに並べると、 \(\{24,19\}, 37,\{53,58,77,81,49,90\}\) となります。37より小さい要素が2個あるので、37は3番目に小さい数です。

④求めたいのは5番目に小さい数です。37は3番目なので、5番目の数は「37より大きいグループ」の中にあります。問題は「 \(\{53,58,77,81,49,90\}\) の中から、元のリストで5番目に小さい数」に帰着しました。37が3番目だったので、このグループの中では \((5-3)=2\) 番目に小さい数を探せばよいことになります。

⑤新しい問題「6個の中から2番目に小さい数」に対して、再びランダムにピボットを選び、分割...を繰り返します。
Quick Selectの計算量解析
このアルゴリズムの計算量は、どのピボットが選ばれるかに依存します。
最悪ケース
もし毎回ピボットとして配列の最小値や最大値を選んでしまうと、分割後の小さい方のグループが常にサイズ1になってしまい、再帰的に解く問題のサイズが1つしか減りません。この場合、計算量は \(N+(N-1)+(N-2)+\cdots+1=O\left(N^2\right)\) となり、ソートより悪くなってしまいます。
平均ケース(期待計算時間)
Quick Selectの強みは、ランダムにピボットを選ぶことで、意図的に最悪ケースの入力を作るのが不可能になる点です。ランダムなピボ ットは、平均的には配列の中央付近の値になる可能性が高く、これにより分割後のグループのサイズが大きく偏る可能性は低くなります。 より厳密な解析(詳細は割愛しますが)によると、ランダムなピボットを選んだ場合、各ステップで問題のサイズが平均して定数倍(例えぱ0.75倍以下)に縮小することが示されます。例えば、サイズ \(N\) の問題が、 \(0.75 N 、 0.75^2 N 、 0.75^3 N 、 \ldots\) と縮小していくと考えると、全体の計算量は \(N+0.75 N+0.75^2 N+\cdots=N\left(1+0.75+0.75^2+\ldots\right)\) という等比級数の和になります。初項1、公比 0.75 の無限等比級数の和は \(1 /(1-0.75)=1 / 0.25=4\) なので、全体の計算量は \(O(4 N)=O(N)\) となります。
Quick Selectの期待計算時間は \(O(N)\) です。これは、ソートの \(O(N \log N)\) よりも高速であり、ランダム性によって最悪ケースの入力を回避し、平均的に優れた性能を達成できる乱択アルゴリズムの好例です。
採用問題(秘書問題)
ランダム性を用いたアルゴリズムと解析の導入例として、採用問題を詳しく見てみましょう。
問題設定
新しいオフィスアシスタントを一人雇う必要があります。あなたは職業紹介所に依頼し、紹介所は毎日一人ずつ候補者を送ってきます(合計 \(n\) 人)。あなた は各候補者と面接し、その場でその候補者を「雇う」か「雇わない(拒否する)」かを決定します。
戦略

あなたは常に、それまで面接した中で最も優秀な人物を雇うという方針をとります。面接後、その候補者が現在のオフィスアシスタントよりも優秀であれば、現在のオフィスアシスタントを解雇し、新しい候補者を雇います。最初の候補者は、他の候補者よりも能力の低いダミー候補者(候補者0)と比較されます。
これを擬似コードで表したのが、以下の決定的な手続き HIRE-ASSISTANT(n) です。候補者は1からnまで番号が付けられ、その順番に面接されます。

※この擬似コードは、候補者iがそれまでに見た中で最高の候補者かどうかを常に判断できると仮定しています。これは、候補者の能力に全順序が存在することを意味します。
コストモデル
この問題では、アルゴリズムの実行時間そのものではなく、面接と採用にかかる費用に注目します。
- 面接にかかるコストは比較的低く、1人あたり \(c_i\) とします。
- 採用にかかるコストは高く、1人採用するたびに \(c_h\) とします。新しい人を雇うたびに、現在のオフィスアシスタントを解雇し、紹介所に手数料を支払う必要があるためです。
\(m\) 人を雇った場合の総コストは \(O\left(c_i n+c_h m\right)\) となります。あなたは常に \(n\) 人全員と面接するため、面接コスト \(c_i n\) は固定です。したがって、私たちは主 こ採用コスト \(c_h m\) に焦点を当てて分析します。この費用(雇われる人の数 \(m\) )は、候補者が面接に現れる順序に依存します。
最悪ケース分析
最も費用がかさむ最悪のケースは、面接した候補者全員を実際に雇う場合です。これは、候補者が厳密に能力の高い順に現れた場合に発生します(例:候補者1が最も能力が低く、候補者 \(n\) が最も能力が高い)。この場合、最初の候補者以外は全員、それまでの誰よりも優秀なので、面接するたびに新しい候補者が雇われます。これにより、 \(n\) 人全員が採用されることになり、 \(m=n\) となります。最悪ケースの総採用コストは \(O\left(c_i n+c_h n\right)=O\left(\left(c_i+c_h\right) n\right)\) 、特に採用コストだけ見れば \(O\left(c_h n\right)\) となります。
確率的解析のアプローチ
しかし、候補者が能力の高い順に来るとは限りません。実際、候補者がどのような順序で来るか、あなたは知る由もないかもしれません。そこで、典型的なケース や平均的なケースで何が起こるかを分析するために、確率的解析の手法を用います。
確率的解析では、入力が特定の確率分布に従うと仮定し、その分布全体にわたるアルゴリズムのパフォーマンス(この場合は採用コスト)の平均(期待値)を計算します。
採用問題では、候補者の能力に全順序が存在すると仮定し、各候補者を 1 から \(n\) までの一意のランクで評価できるとします。そして、これらの候補者がラン ダムな順序(一様ランダム順列)で面接に現れると仮定します。つまり、 \(n!\) 個の可能な順列のそれぞれが等しい確率で発生すると仮定します。この仮定の もとで、決定的な手続きHIRE-ASSISTANTを実行した場合に雇われる人の数の期待値を計算します。この解析結果を平均ケースの採用コストと呼びます。後のセクションで詳しく分析するように、この期待値は約 \(\ln n\) となり、平均ケースの採用コストは \(O\left(c_h \ln n\right)\) となります。
乱択アルゴリズムのアプローチ
もし候補者がランダムな順序で来るという保証が得られない場合、私たちは入力の分布に頼る確率的解析を行うことはできません。しかし、私たちはアルゴリズムをランダム化することで、入力の順序に対する制御を得ることができます。
採用問題の乱択アルゴリズムでは、紹介所から \(n\) 人分の候補者のリストを事前に受け取り、面接を行う前に、そのリストをランダムに並ベ替えます(一様ランダム順列を生成します)。そして、そのランダム化された順序で、決定的な手続きHIRE-ASSISTANTを実行します。

このように、アルゴリズム自身が乱数発生器を用いてランダムな選択(リストの順列化)を行うアルゴリズムを乱択アルゴリズムと呼びます。乱択アルゴリズムの性能を分析する際には、乱数発生器によって返される値の分布にわたる性能の期待値をとります。これを期待実行時間と呼びます。 RANDOMIZED-HIRE-ASSISTANTの期待される採用コストは、入力の順序がランダムであると仮定した場合のHIRE-ASSISTANTの平均ケース採用コストと等 しくなります。つまり、任意の入力に対して、期待される採用コストは \(O\left(c_h \ln n\right)\) となります。最悪の入力(能力の高い順に並んだリスト)が与えられても、ランダムな順列化によってその影響は打ち消され、期待されるコストは抑えられます。
つまり、分布を仮定する代わりに、無理やりある分布を押し付けるのです。
確率的解析と乱択アルゴリズムの対比
採用問題を例にとると、この2つのアプローチの違いが明確になります。
- 確率的解析(決定的なHIRE-ASSISTANT + ランダムな入力の仮定): 入力が自然にランダムである場合に、そのアルゴリズムの平均的な性能を分析します。もし入力がランダムでない(意図的に操作された)場合、分析結果は適用できません。
- 乱択アルゴリズム(RANDOMIZED-HIRE-ASSISTANT + 任意の入力): 入力がランダムであるかどうかにかかわらず、アルゴリズム自身がランダム性を導入することで、期待される性能を保証します。最悪ケースの入力によって性能が極端に悪化する可能性を、期待値として回避できます。
どちらの手法も、ランダム性が効率的なアルゴリズム設計や解析に貢献することを示しています。
配列のランダムな順列化
乱択アルゴリズムでは、しばしば入力配列をランダムに並び替える(順列化する)操作が必要になります。例えば、Quick Selectでランダムなピボットを選ぶ簡単な方法は、配列をランダムに並べ替えてから常に最初の要素をピボットに選ぶ、というものです。また、採用問題で、職業紹介所が候補者をランダムな順序で送ってれる保証がない場合に、自分で候補者のリストをランダムに並べ替えるというランダム化戦略RANDAMIZED-HIRE-ASSISTANTを考えました。この手続きRANDOMIZED-HIRE-ASSISTANTは、候補者のリストをランダムで順列化した後、決定的なHIRE-ASSISTANT手続きを実行するだけです。これにより、候補者がランダムな順序で提示された場合のパフォーマンス \(O\left(c_h \ln n\right)\)の平均ケースの採用コストと同一の期待される採用コスト \(O\left(c_h \ln n\right)\)を、あらゆる入力に対して保証できるようになります。
では、どのようにして配列を「一様ランダム順列」に並べ替えることが出来るでしょうか?「一様ランダム順列」とは、考えられる\(n!\)通りの順列のどれもが等しい確率で生成されるような順列です。
RANDOMLY-PERMUTE 手続き
以下の手続き RANDOMLY-PERMUTE は、\(n\) 個の要素を持つ配列 Aを、追加の記憶領域をほとんど使わずに(インプレースで)、一様ランダム順列に並べ替えることができます。配列の要素は 1 から \(n\) まで番号が振られているとします(プログラミングでは 0 から始まることも多いですが、ここでは 1 から始めます)。

この手続きは、i を 1 から \(n\) まで変化させながら繰り返します。i 番目の繰り返しでは、配列の A[i]という要素と、A[i] 自身を含むそれ以降の要素 A[i], A[i+1], ..., A[n] の中から、ランダムに選ばれた位置にある要素を交換します。
例えば、\(n=3\) の配列 A = [10, 20, 30] で考えてみましょう。

手続きが終了したとき、配列 A はランダムに並べ替えられています。しかし、この並べ替えが本当に「一様ランダム順列」になっていることを、数学的に保証する必要があります。
補題 :RANDOMLY-PERMUTE の正しさ
この手続きの正しさを示すために、以下の補題を証明しましょう。
補題
手続き RANDOMLY-PERMUTE は、一様なランダム順列を計算します。
この証明には、「ループ不変式」という強力な手法を用います。ループ不変式とは、ループが開始される直前、各繰り返しが開始される直前、そしてループが終了した直後に成り立つ性質のことです。この性質がループの各繰り返しで保たれる(維持される)ことを示すことで、ループ終了時の状態について結論を導き出します。
証明を進める前に、証明で使われる「k-順列」という言葉の意味を確認しましょう。
k-順列とは?
\(n\) 個の要素からなる集合があるとします。この集合から \(k\) 個の要素を選び、それらを順序をつけて並べたものをk-順列 (k-permutation) と呼びます。例えば、集合\( \{a, b, c, d\} \)(\(n=4\)) の 2-順列 (\(k=2\)) は、以下の \(4 \times 3 = 12\) 通りです。
\((a, b), (a, c), (a, d)\)
\((b, a), (b, c), (b, d)\)
\((c, a), (c, b), (c, d)\)
\((d, a), (d, b), (d, c)\)
\(n\) 個の要素から \(k\) 個を選んで順序をつける方法は、 1 番目に \(n\) 通り、 2 番目に残りの \(n-1\) 通り、 \(\ldots 、 k\) 番目に残りの \(n-k+1\) 通りありますから、 k -順列の総数は \(n \times(n-1) \times \cdots \times(n-k+1)\) となります。これは順列の記号 \(P(n, k)\) または \({ }_n P_k\) を用いて \(n!/(n-k)!\) と書かれます。
\(n\)-順列は、集合の全ての要素を順序をつけて並べたものです。これが単に「順列」と呼ばれるものです。 \(n\) 個の要素の順列は \(n!\) 通りあります。私たちが最終的に生成したい「一様ランダム順列」とは、この \(n!\) 通りの \(n\)-順列のどれもが等しい確率 \(1 / n!\) で生成される状態のことです。
証明
さあ、RANDOMLY-PERMUTE 手続きが、一様ランダム順列を計算することを示す証明を、ループ不変式を使って進めていきましょう。
ループ不変式
for ループの i 番目のイテレーション(i が 1 から \(n\) まで変化)の直前において、元の配列に含まれていた \(n\) 個の要素のそれぞれの可能な\((i-1)\)-順列について、サブ配列 \(\mathrm{A}[1: \mathrm{i}-1]\) がこの\((i-1)\)-順列を確率 \((n-i+1)!/ n!\) で含んでいる。
この不変式の意味するところを、言葉で説明します。 ループが i 番目の繰り返しを始める直前には、配列の最初の \(\mathrm{i}-1\) 個の位置(A[1]から A[i-1])に、元の配列の要素が何らかの順序で配置されています。不変式は、「考えられる全ての\((i-1)\)-順列(つまり、元の \(n\) 個の要素から \(i-1\) 個を選んで並べたもの全て)について、実際に \(\mathrm{A}[1]\) から \(\mathrm{A}[\mathrm{i}-1]\) にその\((i-1)\)-順列が配置されている確率は、全て等しく \((n-i+1)!/ n!\) である」と主張しています。
なぜ確率が \((n-i+1)!/ n!\) という形なのでしょう?元の \(n\) 個の要素を全て並べた \(n!\) 通りの順列全体を考えます。その中で、最初の \(i-1\) 個の位置に特定の \((i-1)\)-順列 \(\left(\mathrm{x}_1, \ldots, \mathrm{x}_{\mathrm{i}-1}\right)\) が現れる順列は、残りの \(n-(i-1)=n-i+1\) 個の要素を後ろの \(n-i+1\) 個の位置にどのように並べてもよいので、 \((n-i+1)!\) 通りあります。もし最初から配列の要素がランダムな順序で並んでいたなら、特定の \((i-1)\)-順列が最初の \(i-1\) 個の位置に現れる確率は \((n-i+1)!/ n!\) となります。不変式は、ループの各繰り返しが始まる時点で、最初の \(i-1\) 個の要素が、この「ランダムな並び替え」で期待される確率 と同じ確率で、あらゆる \((i-1)\)-順列になっていることを主張しているのです。
この不変式が正しいことを、以下の3つのステップで示します。
1.初期化
ループが開始される直前( \(\mathrm{i}=1\) の直前)に不変式が成り立つことを示します。
\(\mathrm{i}=1\) のとき、不変式はサブ配列 \(\mathrm{A}[1: 1-1]=\mathrm{A}[1: 0]\)(これは空の配列)が、考えられる全ての \((1-1)-\) 順列 \(=0-\) 順列(これは要素が 0 個の順列 で、 1 つだけ存在する)を、確率 \((n-1+1)!/ n!=n!/ n!=1\) で含んでいる、と主張します。
空の配列は、要素が 0 個の 0 -順列を確かに含んでいます。そして、それが起こる確率は 1 です。したがって、不変式はループが開始される直前に成り立 ちます。
2.維持
i 番目のイテレーションの直前で不変式が成り立っていると仮定し、i 番目のイテレーションが完了した後(すなわち i+ 1 番目のイテレーションの直前)でも不変式が成り立つことを示します。(数学的帰納法のようなイメージ)
つまり、サブ配列 \(\mathrm{A}[1: \mathrm{i}]\) が、考えられる全ての \(\mathrm{i}-\) 順列を確率 \((n-i)!/ n!\) で含んでいることを示せばよいのです。( i が \(\mathrm{i}+1\) に増えることで、不変式の \((n-(i+1)+1)!/ n!=(n-i)!/ n!\) という形になるため。)
特定の \(\mathrm{i}-\) 順列 \(\left(x_1, x_2, \ldots, x_i\right)\) を考えます。この \(\mathrm{i}-\) 順列が \(\mathrm{A}[1: \mathrm{i}]\) に現れる確率はいくらでしようか?
この i-順列が現れるためには、以下の2つのイベントが連続して起こる必要があります。
- イベント \(\mathrm{E}_1\) :i 番目のイテレーションの直前で、サブ配列 \(\mathrm{A}[1: \mathrm{i}-1]\) が、この \(\mathrm{i}-\) 順列の最初の \((\mathrm{i}-1)\)-要素である \(\left(x_1, \ldots, x_{i-1}\right)\) という ( \(\mathrm{i}-1\) )-順列になっている。
- イベント \(E_2\) :i 番目のイテレーションにおいて、要素 \(x_i\) が \(\mathrm{A}[i]\)の位置に置かれる。(手続きでは交換によって起こります。)
求めたい確率は、E₁ と E₂ が両方とも起こる確率 \(Pr\{E_1 \cap E_2\}\) です。条件付き確率の定義により、
$$Pr\{E_1 \cap E_2\} = Pr\{E_2 \mid E_1\} \times Pr\{E_1\}$$
\(Pr\{E_1\}\) は、不変式の仮定から分かります。i 番目のイテレーションの直前では不変式が成り立っているので、サブ配列 A[1: i - 1] が特定の (i - 1)-順列 \((x_1, \ldots, x_{i-1})\) である確率は、\((n - i + 1)! / n!\) です。
$$Pr\{E_1\} = \frac{(n - i + 1)!}{n!}$$
\(Pr\left\{E_2 \mid E_1\right\}\) を計算します。イベント \(\mathrm{E}_1\) が起こった、つまり \(\mathrm{A}[1: \mathrm{i}-1]\) は \(\left(x_1, \ldots, x_{i-1}\right)\) という特定の( \(\mathrm{i}-1\) )-順列になった、という条件下で考えます。
\(i\) 番目のイテレーションでは、 \(A[i]\) と \(A[R A N D O M(i, n)]\) が交換されます。 \(A[R A N D O M(i, n)]\) は、 \(A[i]\) から \(A[n]\) までの \(n-i+1\) 個の要素の中 から、ランダムに(確率 \(1 /(n-i+1)\) で)選ばれた位置です。
イベント \(\mathrm{E}_1\) が起こった後、配列 \(\mathrm{A}\)は現在の要素( \(\mathrm{A}[1 . . \mathrm{i}-1]\) )と残りの要素( \(\mathrm{A}[\mathrm{i} . . \mathrm{n}]\) )に分かれています。特に、 \(\mathrm{A}[1 . . \mathrm{i}-1]\) は \(\left(x_1, \ldots, x_{i-1}\right)\) です。そして、元の配列に含まれていた残りの \(n-(i-1)=n-i+1\) 個の要素が \(\mathrm{A}[\mathrm{i} . . \mathrm{n}]\) に入っています。
\(\mathrm{A}[1..\mathrm{i}]\)が最終的に \(\left(x_1, \ldots, x_i\right)\) という i-順列になるためには、目的の要素 \(x_i\) が、i 番目のイテレーションの開始時点( \(\mathrm{E}_1\) が起こった後)でサブ配列 \(\mathrm{A}[\mathrm{i}: \mathrm{n}]\) のどこかに存在している必要があります。もし \(x_i\)が \(\mathrm{A}[1: \mathrm{i}-1]\) に含まれてしまっていたら(つまり、もし \(x_i\)が \(\left\{x_1, \ldots, x_{i-1}\right\}\) の中にあったら)、A[1:i]が\((x_1. \ldots, x_i)\)となることはあり得ません(異なる要素からなる順列を考えているため)。
したがって、イベント \(\mathrm{E}_1\) が起こったという条件下で、\(x_i\)は必ず \(\mathrm{A}[\mathrm{i}: \mathrm{n}]\) の中に含まれています(そうでないと、A[1:i]が\((x_1. \ldots, x_i)\)になり得ないため)。
i 番目のイテレーションでは、A[RANDOM(i,n)]によって、A[i:n]の中の \(n-i+1\) 個の位置のいずれかが等確率で選ばれます。目的の要素 \(x_i\)が入っているその特定の1つの位置が選ばれる確率は、 \(1 /(n-i+1)\) です。これが、A[i]に \(x_i\)が置かれる確率、つまり \(Pr\left\{E_2 \mid E_1\right\}\) です。
$$Pr\{E_2 \mid E_1\} = \frac{1}{n - i + 1}$$
最後に、\(Pr\{E_1 \cap E_2\}\) を計算します。
$$Pr\{E_1 \cap E_2\} = Pr\{E_2 \mid E_1\} \times Pr\{E_1\} = \frac{1}{n - i + 1} \times \frac{(n - i + 1)!}{n!} = \frac{1}{n - i + 1} \times \frac{(n - i + 1) \times (n - i)!}{n!} = \frac{(n - i)!}{n!}$$
これは、サブ配列 \(\mathrm{A}[1: \mathrm{i}]\) が特定の \(\mathrm{i}-\) 順列 \(\left(x_1, \ldots, x_i\right)\) である確率が \((n-i)!/ n!\) であることを示しています。 この結果は、i+1 番目のイテレーションの直前に不変式が主張する確率の形と一致します。したがって、ループ不変式は維持されます。
3.終了
ループはi が 1 から \(n\) まで変化するので、正確に \(n\) 回繰り返して終了します。ループが終了したとき、iの値は \(n+1\) になります。 このとき、不変式はサブ配列 \(\mathrm{A}[1: ~ \mathrm{n}+1-1]=\mathrm{A}[1: \mathrm{n}] \quad\)(これは配列全体)が、考えられる全ての \((\mathrm{n}+1-1)-\) 順列 \(=n-\) 順列(つまり、配列全体を並 べ替えたもの)を、確率 \((n-(n+1)+1)!/ n!=0!/ n!=1 / n!\) で含んでいる、と主張します。 \(0!=1\) なので、この確率は \(1 / n!\) です。
\(0! = 1\) なので、この確率は \(1/n!\) です。
これは、配列 A の全ての要素からなる任意の特定の \(n\)-順列が、確率 \(1/n!\) で生成されることを意味します。これはまさに「一様ランダム順列」の定義です!
証明の3つのステップ全てが完了したため、手続き RANDOMLY-PERMUTE は一様ランダム順列を計算することが数学的に保証されました。□
ランダムな入力とは?

多くの競技プログラミングの問題では、「制約を満たすどんな入力ケースでも正解すること」が求められます。これは、アルゴリズムが考えうる最も意地悪な入力(最悪ケース)に対しても効率的に動作する必要があることを意味します。先の数当てゲームで、決定的な順番で質問する戦略が意地悪な入力に弱いことを見たのは、この考え方に基づいています。
一方で、現実世界で遭遇する問題やデータは、必ずしもアルゴリズムの弱点を突くように巧妙に作られているわけではありません。多くの場合、データは何らかの自然なプロセスやランダムな要因によって生成され、「偏りがない」または特定の確率分布に従うという性質を持つことがあります。このような入力を「ランダムな入力(random input)」と呼びます。
「一般のデータ」と「ランダムなデータ」はどう違うでしょうか?一般のデータには、特定のパターンに集中していたり(偏っていたり)、アルゴリズムにとって扱いにくように特別に作られた「意地悪なデータ」が含まれる可能性があります。一方、「ランダムなデータ」は、通常、データの分布に大きな偏りがありません。
この「偏りがない」という性質を利用することで、決定的なアルゴリズムであっても、ランダムな入力に対しては非常に効率的に問題を解ける場合があります。このような解析手法を確率的解析(Probablisitic Analysis)と呼びます。ここでは、アルゴリズムは決定的ですが、そのパフォーマンスを入力の確率分布に基づいて解析します。
指示確率変数
確率的解析を進めるうえで非常に便利なツールが指示確率変数 (Indicator Random Variable) です。
サンプル空間 \(S\) とその中の事象 \(A\) が与えられたとき、事象 \(A\) に関連付けられた指示確率変数 \(I\{A\}\) は、次のように定義されます。
$$
I\{A\}= \begin{cases}1 & (\text { 事象 } A \text { が発生する場合 }) \\ 0 & (\text { 事象 } A \text { が発生しない場合 })\end{cases}
$$
確率変数の最も重要な性質は、その期待値が事象が発生する確率に等しいことです。
補題
サンプル空間 \(S\) とサンプル空間 \(S\) 内の事象 \(A\) が与えられたとき、 \(X_A=I\{A\}\) とします。このとき、 \(E\left[X_A\right]=\operatorname{Pr}\{A\}\) です。
指示確率変数は、複数の事象が関わる状況で期待値を計算する際に特に役立ちます。これは期待値の線形性(Linearity of Expectation)と組み合わせることで強力な手法となります。期待値の線形性とは、「確率変数の和の期待値は、個々の確率変数の期待値の和に等しい」という性質です。
$$
E\left[X_1+X_2+\cdots+X_n\right]=E\left[X_1\right]+E\left[X_2\right]+\cdots+E\left[X_n\right]
$$
この性質の素晴らしい点は、確率変数\(X_1, \ldots, X_n\) が互いに独立している必要がないという点です。どんな場合でも成り立ちます。
例として、公正なコインを \(n\) 回投げたときに表が出る回数の期待値を計算してみましょう。
\(i\) 番目の投げが表になる事象を \(H_i\) とします。 \(i\) 番目の投げが表になるかどうかを示す指示確率変数 \(X_i=I\left\{H_i\right\}\) を定義します。
\(n\) 回の投げで表が出た合計回数を表す確率変数を \(X\) とすると、 \(X=\sum_{i-1}^n X_i\) と書けます。
期待値の線形性より、 \(E[X]=E\left[\sum_{i-1}^n X_i\right]=\sum_{i-1}^n E\left[X_i\right]\) です。
公正なコインの場合、 \(i\) 番目の投げが表になる確率 \(\operatorname{Pr}\left\{H_i\right\}=1 / 2\) なので、指示確率変数の期待値は \(E\left[X_i\right]=\operatorname{Pr}\left\{H_i\right\}=1 / 2\) です。
したがって、期待される表の回数は \(E[X]=\sum_{i-1}^n 1 / 2=n \times(1 / 2)=n / 2\) となります。これは直感的にも分かりやすい結果ですが、指示確率変数を使うと非常に簡潔に導出できます。
採用問題の確率的解析
先ほど触れた採用問題(新しいオフィスアシスタントを雇う問題)に戻りましょう。この問題を確率的解析の観点から見てみます。決定的な手続き HIRE-ASSISTANT を使用しますが、入力(候補者が到着する順序)がランダムであると仮定します。具体的には、n人の候補者の能力には全順序があり、到着する順序は能力のランクの一様ランダム順列であると仮定します。
新しいオフィスアシスタントを雇う回数 X の期待値を計算します。
\(i\) 番目の候補者が雇われる事象に関連付けられた指示確率変数 \(X_i=I\{\) 候補者iが雇われた \(\}\) を定義します。
雇われる合計回数 \(X=\sum_{i-1}^n X_i\) です。
期待値の線形性により、 \(E[X]=\sum_{i-1}^n E\left[X_i\right]\) です。
補題より、 \(E\left[X_i\right]=\operatorname{Pr}\{\) 候補者iが雇われた \(\}\) です。
では、候補者 \(i\) が雇われる確率 \(\operatorname{Pr}\{\) 候補者iが雇われた \(\}\) はいくつでしょうか?HIRE-ASSISTANTの手続きでは、候補者 \(i\) は、それまでに面接した候補者 \(1,2, \ldots, i-1\) の誰よりも優秀である場合に限り雇われます。候補者がランダムな順序で到着すると仮定したため、最初の \(i\) 人の候補者(候補者 1 か ら \(i\) まで)はランダムな順序で現れています。これら最初の \(i\) 人の中で最も優秀な候補者が、候補者 \(i\) である確率はいくらでしょうか?最初の \(i\) 人の候補者のうち、誰が最も優秀である可能性も等しく高いです。したがって、候補者 \(i\) が最初の \(i\) 人の中で最も優秀である確率は \(1 / i\) です。そして、候補者 \(i\) が雇われるのは、候補者 \(i\) が最初の \(i\) 人の中で最も優秀である場合に限ります。
したがって、 \(\operatorname{Pr}\{\) 候補者iが雇われた \(\}=1 / i\) です
雇われる回数の期待値は、
$$
E[X]=\sum_{i=1}^n E\left[X_i\right]=\sum_{i=1}^n \frac{1}{i}
$$
これは調和級数(Harmonic Series)と呼ばれ、 \(H_n\) と表記されます。調和級数は自然対数を用いて近似できます
$$
H_n=\sum_{i-1}^n \frac{1}{i}=\ln n+O(1)
$$
したがって、候補者がランダムな順序で提示されると仮定すると、平均的には約 \(\ln n\) 人しか雇いません。面接コストを \(c_i\) 、採用コストを \(c_h\) とすると、総 コストは \(O\left(c_i n+c_h m\right)\) であり、面接コスト \(c_i n\) は固定ですが、採用コスト \(c_h m\) は変動します。期待される採用回数 \(E[m]\) は \(E[X] \approx \ln n\) なので、平均ケースの総採用コストは \(O\left(c_h \ln n\right)\) となります。これは最悪ケースのコスト \(O\left(c_h n\right)\) に比べて大幅な改善です。
確率的解析と指示確率変数のさらなる応用
このセクションでは、確率的解析と指示確率変数を用いて、興味深いいくつかの確率問題を詳しく分析します。これらの問題は、一見簡単に見えるものから、直感に反する結果を示すものまで様々ですが、基本的な確率論と期待値の線形性というツールがいかに強力であるかを示してくれます。
誕生日のパラドックス (Birthday Paradox)
問題
部屋に \(k\) 人の人がいます。うるう年を無視し、1年は365日であるとし、誕生日は1年のどの日でも等確率に起こり、各人の誕生日は独立であると仮定します。
何人の人が集まれば、「少なくとも2人が同じ誕生日である」確率が50%を超えるでしょうか?
直感的には、365日もあるのだから、かなりの人数が集まらないと誕生日が一致する確率は低そうに思えます。しかし、実際の結果は多くの人の予想を裏切ります。これが「パラドックス」と呼ばれる所以です(ただし、これは論理的なパラドックスではなく、直感とのずれによるものです)。
この問題を解くために、2つの異なるアプローチを考えます。
ボールとビン / クーポンコレクター問題 (Balls and Bins / Coupon Collector's Problem)
問題
\(n\) 個のボールを、\(b\)個のビン(番号 1 から \(b\)) にランダムかつ独立に投げ入れます。各ボールが任意のビンに入る確率は等しく \(1/b\) です。
全てのビンに少なくとも1個のボールが入るまで、何個のボールを投げなければならないか(投げ入れ回数 \(n\) の期待値)を求めなさい。
この問題は、スーパーのキャンペーンで \(b\)種類のシールを集めようとしている人が、ランダムにシールがもらえるお菓子を何個買えば全ての種類のシールが集まるか、という状況に似ています。このため、「クーポンコレクター問題」とも呼ばれます。
この問題の期待値を計算するために、プロセスを段階に分け、各段階での投げ入れ回数の期待値を求め、それらを合計するというアプローチをとります。これは指示確率変数を使うほどではありませんが、期待値の線形性を効果的に使う例です。
ストリーク (Streaks)
問題
公正なコインを \(n\) 回投げます。
期待される最長の連続した「表」(または「裏」)のストリークの長さはどれくらいでしょうか?
\(n\) 回のコイン投げの列 \((R_1, R_2, \ldots, R_n)\) を考えます。ここで \(R_i\) は \(i\) 回目の投げの結果(表または裏)です。最長のストリークの長さとは、列の中に同じ結果が連続して現れる最も長い部分列の長さです。例えば、「表表裏表表表裏」という投げ入れ列では、最長の表のストリークの長さは3です。
この問題の解析は少し複雑ですが、期待される最長ストリークの長さが、投げ入れ回数 \(n\) の対数 \(\lg n\) (2を底とする対数)のオーダーになることが示されます。すなわち、\(\Theta(\lg n)\) です。
この結果を示すために、期待される最長ストリーク長の**上限**と**下限**をそれぞれ評価します。
オンライン採用問題 (Online Hiring Problem)

問題
\(n\) 人の候補者が順番に面接に来ます。あなたは各候補者と面接した後、直ちにその場で採用するか、拒否するかを決めなければなりません。一度拒否した候補者を後で採用することはできません。目標は、最も優秀な候補者(全体の最高ランクの候補者)を一人だけ採用することです。候補者はランダムな順序で現れると仮定します。
どのような戦略をとれば、最も優秀な候補者を採用できる確率を最大化できるでしょうか? そしてその最大確率は?
この問題は、これまでの採用問題の変種です。全ての候補者と面接する前に決定を下す必要がある「オンライン」な問題です。
採用戦略として、以下のシンプルな方法を考えます。
まず、最初の \(k\) 人の候補者と面接しますが、彼らを絶対に採用しません。彼らの面接を通して、これまでの最高ランク(最高のスキルを持つ候補者)が誰かを記録しておきます。
次に、\(k+1\) 番目以降の候補者と面接します。このとき、もし面接した候補者が、最初の \(k\) 人の中で最も優秀だった候補者よりも優秀であれば、その候補者を直ちに採用し、プロセスを終了します。
もし、\(n\) 番目の候補者まで誰も採用しなかった場合、最後の \(n\) 番目の候補者を(たとえ最初の \(k\) 人より優秀でなくても)採用します。
この戦略において、最も優秀な候補者を採用できる確率は、最初の \(k\) 人を何人「見送るか」という \(k\) の値に依存します。\(k\) が小さすぎると、すぐに誰かを採用してしまい、後に来るかもしれないもっと優秀な候補者を逃す可能性があります。\(k\) が大きすぎると、最も優秀な候補者自身が最初の \(k\) 人の中に入ってしまい、採用する機会を逃してしまいます。最適な \(k\) が存在しそうです。
候補者はランダムな順序で現れると仮定します。最も優秀な候補者を採用できる確率 \(Pr\{S\}\) を計算します。
最も優秀な候補者が \(i\)番目に現れた場合に、この戦略が成功する事象を \(S_i\) とします。これらの事象 \(S_i\) は、互いに排反(同時に起こらない)です。全体の成功確率は、それぞれの事象が起こる確率の合計です。
\[
Pr\{S\} = \sum_{i=1}^n Pr\{S_i\}
\]
戦略上、最初の \(k\) 人は採用しません。したがって、もし最も優秀な候補者が最初の \(k\) 人の中にいた場合(つまり \(i \le k\) の場合)、戦略は成功しません (\(Pr\{S_i\} = 0\) for \(i \le k\))。
成功する可能性があるのは、最も優秀な候補者が \(k+1\) 番目以降に現れた場合のみです (\(i > k\))。
\[
Pr\{S\} = \sum_{i=k+1}^n Pr\{S_i\}
\]
では、\(i > k\) の場合の \(Pr\{S_i\}\) はいくつでしょうか? 最も優秀な候補者が \(i\) 番目に現れたときに戦略が成功するためには、以下の2つのことが同時に起こる必要があります。
1. 全体の最も優秀な候補者が、ちょうど \(i\) 番目の位置にいる。この事象を \(B_i\) とします。候補者はランダムな順序で現れるので、最も優秀な候補者がどの位置に現れる可能性も等しいです。したがって、\(Pr\{B_i\} = 1/n\) です。
2. \(k+1\)番目から \(i-1\) 番目までの候補者の誰も採用されなかった。この事象を \(O_i\) とします。戦略によると、これは、これらの候補者たちが、最初の \(k\) 人の中で最も優秀だった候補者よりも優秀でなかった場合に起こります。
事象 \(B_i\) と \(O_i\) は独立です。なぜなら、\(B_i\) は \(i\) 番目の候補者の絶対的なランク(全体の中で一番)に関することであり、\(O_i\) は \(k+1\) 番目から \(i-1\) 番目までの候補者の、最初の \(k\) 人の中での最高ランクに対する相対的なランクに関することだからです。これらは互いに影響しません。
したがって、\(Pr\{S_i\} = Pr\{B_i \cap O_i\} = Pr\{B_i\} \times Pr\{O_i\}\) です。
\(Pr\{O_i\}\) はいくつでしょうか? 事象 \(O_i\) は、「候補者 \(k+1\) から \(i-1\) の中に、最初の \(k\) 人の誰よりも優秀な候補者はいなかった」という事象です。これは言い換えれば、「最初の \(i-1\) 人の候補者の中で最も優秀だった候補者が、最初の \(k\) 人の中にいた」という事象と同じです。
最初の \(i-1\) 人の候補者はランダムな順序で現れています。この \(i-1\) 人の中で最も優秀な候補者が誰である可能性も等しく高いです。その中で、最も優秀な候補者が最初の \(k\) 人の中にいる確率は、\(\frac{\text{最初の } k \text{ 人}}{\text{最初の } i-1 \text{ 人}} = \frac{k}{i-1}\) です。したがって、\(Pr\{O_i\} = k/(i-1)\) です(\(i-1 \ge k\) と仮定。もし \(i-1 < k\) なら \(O_i\) は常に真で確率は1ですが、ここでは \(i > k\) の場合を考えているので \(i-1 \ge k\) です)。
\(i > k\) の場合の \(Pr\{S_i\}\) は、
\[
Pr\{S_i\} = Pr\{B_i\} \times Pr\{O_i\} = \frac{1}{n} \times \frac{k}{i-1} = \frac{k}{n(i-1)}
\]
全体の成功確率 \(Pr\{S\}\) は、
\[
Pr\{S\} = \sum_{i=k+1}^n Pr\{S_i\} = \sum_{i=k+1}^n \frac{k}{n(i-1)}
\]
和の添え字を \(j = i-1\) と置き換えると、\(i=k+1\) のとき \(j=k\)、\(i=n\) のとき \(j=n-1\) となります。
\[
Pr\{S\} = \frac{k}{n} \sum_{j=k}^{n-1} \frac{1}{j}
\]
この和は、調和級数 \(H_{n-1} - H_{k-1}\) です。大きな \(n\) に対して、この和は積分を用いて近似できます。
\[
\sum_{j=k}^{n-1} \frac{1}{j} \approx \int_k^n \frac{1}{x} dx = [\ln x]_k^n = \ln n - \ln k = \ln\left(\frac{n}{k}\right)
\]
したがって、成功確率はおよそ
\[
Pr\{S\} \approx \frac{k}{n} \ln\left(\frac{n}{k}\right)
\]
この近似式を最大にする \(k\) を求めます。関数 \(f(k) = \frac{k}{n} \ln\left(\frac{n}{k}\right)\) を \(k\) について微分し、0と置きます。
\(f(k) = \frac{1}{n} (k \ln n - k \ln k)\)
\(f'(k) = \frac{1}{n} (\ln n - (1 \cdot \ln k + k \cdot \frac{1}{k})) = \frac{1}{n} (\ln n - \ln k - 1)\)
\(f'(k) = 0\) となるのは、\(\ln n - \ln k - 1 = 0\) 、すなわち \(\ln n - 1 = \ln k\) 、\(\ln(n/e) = \ln k\) のときです。
したがって、最適な \(k\) は、
\[
k = \frac{n}{e}
\]
ここで \(e\) は自然対数の底(約2.718)です。
最適な戦略は、**最初の約 \(n/e\) 人を見送り、それ以降で最初の \(n/e\) 人の誰よりも優秀な候補者が現れたら採用する**ことです。
この最適な \(k = n/e\) を成功確率の近似式に代入すると、最大の成功確率が得られます。
\[
Pr\{S\} \approx \frac{n/e}{n} \ln\left(\frac{n}{n/e}\right) = \frac{1}{e} \ln(e) = \frac{1}{e}
\] したがって、最適な戦略をとった場合、最も優秀な候補者を採用できる確率は、候補者の数 \(n\) にかかわらず、およそ \(1/e \approx 0.368\) となります。これは、約36.8%の確率で全体の最も優秀な候補者を雇えることを意味します。たとえ全体のリストを全て見られなくても、ランダムな順序という性質を利用することで、意外と高い確率で最高の候補者を見つけられるのです。
まとめ
本記事は、コンピューター科学における「ランダム性」が、アルゴリズムの性能を向上させる強力なツールであることを解説しています。通常、アルゴリズムは予測可能な決まった手順で動作しますが、「意地悪な入力」によって効率が著しく低下することがあります。ランダム性を利用することで、この問題を回避し、効率的で安定した性能を実現できます。



