« Javaでexcelファイル(.xlsx)を読む:POIサンプル | トップページ | メーラthunderbirdの導入:メモ »

◆順列組合せの公式はΠ(総乗)を使うべき

 順列組合せの算出方法

n個からr個取り出す取り出し方の数を出したいことは極めて頻繁にあります。

取り出したものの順番を気にするものを「順列」、順番はどうでもよい場合を「組み合わせ」といいます。

「順列の算出法」はとても簡単で、

  • nから順に1を引きながら、r回かけていく
ことで得られます。
例えば7個から3個とりだすなら7×6×5=210となります。10個から2個なら、 10×9=90です。

冒頭の図のΠ(直積)はΣ(総和)の掛け算版というべきもので、ある値から 1ずつインクリメントして最後の値まで掛け算を行います。
「直積」より「総乗」と呼ぶ方が適切ではあります。

「組合せの算出法」はrrをrとn-rの小さい方として

  • 順列(n,rr)の値をrrの階乗で割る
ことで得られます。
例えば7個から3個取り出すなら(7×6×5)/(3×2)=35となります。10個から2個なら、 (10×9)/2=45です。10個から8個取り出すのは10個から2個と同じ(10×9)/2=45となります。

 順列組合せの一般的なトリッキーで愚かな公式

一般的な順列の公式ではΠ(直積/総乗)は用いません。

その代わりのおぞましいトリックを用います。

トリックとは、掛け算を途中で止めることを、それ以下の 掛け算で割ることで「表す」やり方で、 例えば5×4を得るのに(5×4×3×2×1)/(3×2×1)を 計算するという形のものです。

  • 5!/3!=(5×4×3×2×1)/(3×2×1)=5×4
もちろん間違いではありません。同じ結果を得ることができます。 しかし、計算をするための元の考えとは異なる「形」をしています。

n!/(n-r)!を見てその意味(階乗を計算するのではなく、途中まで掛け算をするだけ) を直ちに把握することは困難で、 それゆえ覚えにくいものとなっています。

さらに、意味を把握することなく単純に無意味な計算を行いがちに なります。
例えばn=10,r=2ならnPrは10×9と簡単なものであるにも関わらず 一旦10!(なんと3628800)を計算した上で8!(40430)を計算し、割り算 を行うという愚かな作業を「意味を考えない教条主義者は」 実行することも考えられます。
例えば7個から2個選ぶという設問に対し7×6=42と即座に計算した生徒に対し 「ずるをするな。ちゃんと7の階乗と5の階乗を計算しなさい」と先生は言い 「この公式はそういう意味じゃないんですよ」と生徒が丁寧に説明すると 「先生をバカにするのか!」と怒り出し、大きな木製の三角定規で生徒になぐり かかるといった事態が起こるかも知れません。(あるかも知れないというだけですよ。まあ、 近いことはありました)

 直積/総乗をつかった公式に置き換えよう

現在教科書,参考書に載っている公式

は直ちに廃棄すべきです。
このトリッキーな公式が本来の単純な理屈を分かりづらくし、覚えづらくしています。

Π(直積/総乗)を使った公式

に置き換えるべきです。

「Πなんて記号は教えていないから使えない」などというのは愚劣な言い訳に すぎません。教えればいいのです。Σが使えるならΣの掛け算版のΠが使えない 理由はありません。

階乗の導入だけで済むというのも説得力があるとは思えません。基本要素の 種類が少なくすんでも表現が誤解を生むものになるのでは「楽」とは言えません。

直積/総乗記号に次のような表記方があればさらにスマートになります。

 実際の値:参考

おおよそどの程度の値になるのか参考までに表を載せます。

nPr 順列表
12345678910
11
222
3366
44122424
552060120120
6630120360720720
7742210840252050405040
885633616806720201604032040320
997250430241512060480181440362880362880
101090720504030240151200604800181440036288003628800

nCr 組合せ表
12345678910
11
221
3331
44641
55101051
6615201561
772135352171
88285670562881
993684126126843691
10104512021025221012045101

単純な計算機を載せます。
nとrに値を入れて[計算する]をクリックするとnPr,nCrの 計算を行います。
[nU][nD][rU][rD]はn,rを増加/減少させます。

  n =   r = 
nPr =   nCr = 

次の計算を行っています。

function calcP(n,r){
   var p=1;
   for(var i=0;i<r;++i) p *= n--;
   return p;
   }
function calcC(n,r){
   var rr=Math.min(r,n-r);
   var p=1;
   for(var i=2;i<=rr;++i) p /= i;
   return p;
   }
なお整数でなく実数で計算するなら次の形で計算すれば組合せの オーバーフローの可能性を少し減らすことができます。
function calcC(n,r){
   var rr=Math.min(r,n-r);
   var p=1;
   for(var i=1;i<=rr;++i) p = (p*n--)/i;
   return p;
   }
2015/3/21:さらなる巨大数値での演算機を □2000個から1000個選ぶ!巨大組合せ計算式に置きました。

 ###

2015/3/16
この記事の本題ではありませんが組合せの処理にr=min(r,n-r)を入れました。
10000個から9999個を選ぶ組合せ数(10000C9999)などの値もオーバーフローすることなく取得できます。

2015/3/20
組合せ計算のオーバーフローを少し減らすやり方を追加しました。
1000個から500個 を選ぶ場合2.70×10の299乗になります。javascriptの数値の最大は1.8×10の308乗 のため、このやり方を採用するとぎりぎりオーバーフロー無しで計算できます。
ただし、整数では途中割り切れない値となる可能性がありこの方法は採れません。
割り切れる形での手法は検討中です。

|

« Javaでexcelファイル(.xlsx)を読む:POIサンプル | トップページ | メーラthunderbirdの導入:メモ »

トラックバック


この記事へのトラックバック一覧です: ◆順列組合せの公式はΠ(総乗)を使うべき:

« Javaでexcelファイル(.xlsx)を読む:POIサンプル | トップページ | メーラthunderbirdの導入:メモ »