確率解説 [SRM 418 Div2 500/Div1 250]

目次

問題

昨日、くじの広告を見掛けた。「 1 以上 n 以下の m 個の数字を選んでください。その後で我々もランダムに 1 以上 n 以下の m 個の数字を選びます。少なくとも k 個の数字が一致していればあなたの勝ち!」あなたが勝つ確率を求めよ。ただし 2 <= n <= 8, 1 <= m <= n-1, 1 <= k <= m とする。

※Firefox4 など数式表示に対応したブラウザを使用している場合はこちら

出題元 SRM 418 Div2 500

解答

つかさえっと、どっから手をつけたら良いかわからないよ。。。
かがみTopCoder の問題はひねってあるからね。
こういうときは簡単な問題に分解してみましょう。まずはこの問題。

Q1) n 個の中に 1 個の当たりがある場合、 1 個引いて当たりである確率を求めよ。

つかささすがにこれは分かるよ。 1/n でしょ?
かがみふむ、では次の問題。

Q2) n 個の中に m 個の当たりがある場合、 1 個引いて当たりである確率を求めよ。

つかさぐっと難しくなったね。。。
かがみここでつまずくということは Q1 をきちんと理解していないということね。
つかさえー、そうなの?。
かがみもう一度 Q1 に戻りましょう。
確率の問題の基本は「場合の数」よ。「事象が起こる場合の数」/「全体の場合の数」で求めるの。
つかさうん
かがみまず「事象が起こる場合の数」というのは、「当たりを引く場合の数」ということね。
つかさうん
かがみつまり「 1 個の当たりの中から 1 個を取り出す場合の数」ということなの。
つかさ何か回りくどいね。結局 1C1=1 ってことでしょ?
かがみそうね。今は回りくどいと思うだろうけど、後で役に立つから。
次は「全体の場合の数」ね。これは「n 個の中から 1 個を取り出す場合の数」なのでnC1。
よって確率は(1C1)/(nC1) = 1/n。
つかさそこまでは大丈夫。
かがみこれをもとに Q2 を考えるの。まず「事象が起こる場合の数」は?
つかさ「当たりを引く場合の数」っていう言葉は Q1 と同じだけど、今回は当たりが m 個あるよね。
こういう場合はいくつになるんだろう。
かがみm 個ある当たりくじの中から 1 個を取り出すと考えたら?
つかさmC1?
かがみそういうことね。
つかさちょ、ちょっと待って。 Q1 の場合はどうなんだっけ。
かがみ1 個しかないものから 1 個取り出すから 1C1。
つかさそっかー Q1 はそれで 1 なんだー。今やっと Q1 が分かった気がする。
かがみ次に「全体の場合の数」は?
つかさ n 個の中から 1 個を取るというのは Q1 と変わらないから Q1 と同じで nC1 かな?
かがみそうよ。なので Q2 の答えは(mC1)/(nC1) = m/n。
つかさぷはー。なかなか大変。あとどれくらい?
かがみまだ一割ぐらいよ。。。
つかさう、うそ。。。ちょっとお菓子食べて休憩したいなー、なんて。。。
かがみしょうがないわね。休憩終わったら Q3 を始めるわよ。

Q3) n 個の中に m 個の当たりがある場合、 m 個引いて全て当たりである確率を求めよ。

つかさ「事象が起こる場合の数」=「m 個引いて全て当たりの場合の数」だけど、
これはいくつになるんだろう。
かがみm 個の当たりの中から m 個を取り出すことになるわね。
つかさそうすると、 mCm?
かがみそう
つかさ「全体の場合の数」は、「 n 個の中から m 個を取り出す場合の数」だから nCm?
かがみそう
つかさじゃあ答えは (mCm)/(nCm) = 1/(nCm) だー。何かコツがわかった気がするー。
かがみふむ、いいわね。次。

Q4) n 個の中に m 個の当たりがある場合、 m 個引いて 1 個が当たりである確率を求めよ。

かがみここが一番の山場よ。
こなたじゃあ、私は隣でお菓子を食べながら二人を眺めるとするよ。
かがみ急に出てきたわね。邪魔しないでよね。。。
こなたあー、続けて続けて。私はかがみんの新しい魅力を堪能しに来ただけだから。
伊達メガネ掛けて差し棒を持ってタイトスカートにワイシャツを着てもらえば言う事無いんだけどねー。
こなたあ、胸のボタンは第二まで開けてね。
かがみ帰れ。
つかさえ、えーと「事象が起こる場合の数」から求めるんだよね?
かがみとりあえず外野は無視して続けましょう。。。
つかさこれは今までのやり方で行くと、「事象が起こる場合の数」=「m 個の当たりの中から 1 個を取り出す場合の数」= mC1。
かがみ多分そうやると思ったけど不正解ね。それだと n 個から 1 個を取り出す場合の数になっちゃうわ。今回は n 個から m 個取り出すのよ。そして取り出した m 個の中の 1 個が当たりなの。
つかさそっか。。。どうやって考えたらいいんだろ。。。
かがみn 個から 1 個の当たりと m - 1 個のはずれを同時に取り出すと考えたら?
つかさ一瞬「なるほど!」って思ったけど式にはできません。。。
かがみ分けて考えるの。まず当たりを取り出す場合の数は m 個の中から 1 個の当たりを取り出すからmC1。はずれは n - m 個で、この中から m - 1 個を選ぶので (n - m)C(m - 1)。
つかさ「全体の場合の数」はわかるかも。 n 個から m 個取り出すからnCm?
かがみそうね。よって確率は (mC1 * (n-m)C(m-1))/nCm になるわ。
つかさこれで Q4 は終わりだね。
かがみでもこの問題には引っ掛けがあるの。n = 7, m = 5の場合はどうなる?
つかさ(5C1 * 2C4)/7C5。分母の2C4は変だね。2個の中から4個選ぶことになっちゃう。
かがみそうね。ちょっとさっき求めた数式を離れて考えると、n = 7, m = 5の場合っていうのは、
7 個のうち 5 個が当たりで、その中から 5 個を選ぶってことよね。
つかさうん
かがみ下の図のように、選び方は3通りなんだけど、どの選び方をしても3 個以上 5 個以下の当たりが必ず含まれるの。だから Q4 のように 1 個選ばれる確率を求めようとすると 0 になっちゃうのよ。
n = 7, m = 5
つかさそっかー。
かがみこれを一般化すると、 n 個から m 個取り出して、 m 個に含まれる最小の当たりの個数 > 1 なら確率は 0 になるわね。言い換えると m 個に最も多くはずれを含ませた場合の、 m 個内の当たりの個数 > 1 ということね。
かがみはずれの個数は n - m 個だから、 m - (n - m) > 1 つまり 2 * m - n > 1 なら確率は 0 になるわ。
つかさ機械的にやるんじゃだめなんだねー。
かがみここまでくれば、後はそんなに難しく無いわ。

Q5) n 個の中に m 個の当たりがある場合、 m 個引いて k 個当たりである確率を求めよ。

つかささっきの Q4 の 1 の部分が k に変わっただけだね。
2 * m - n > k なら確率は 0。それ以外の場合は (mCk + (n-m)C(m-k))/(nCm)。
かがみ最後に当初の問題よ。

Q6) n 個の中に m 個の当たりがある場合、 m 個引いて k 個以上が当たりである確率を求めよ。

つかさQ5 で k の場合を求めたから、当たりが k 個の場合から m 個の場合まで足せば良いんだね?
a(k) = mCk + (n-m)C(m-k) とおく。ただし2 * m - n > k ならa(k)は 0。
{a(k) + a(k+1) + a(k+2) + ... + a(m)}/(nCm) だね。
かがみJava で書くとこんな感じね。

import java.util.*;
public class TwoLotteryGames {
	public double getHigherChanceGame(int n, int m, int k) {
		long total = 1;
		for (int i = k; i < m; i++) {
			if (i >= 2 * m - n)
				total += combination(m, i) * combination(n - m, m - i);
		}
		return (double)total / combination(n, m);
	}

	// nCr
	private long combination(int n, int r) {
		if (n == r)
			return 1;
		if (n < r)
			throw new RuntimeException(n + " < " + r);
		if (n - r < r)
			r = n - r;
		long up = 1;
		for (int i = 0; i < r; i++)
			up *= n - i;
		long down = 1;
		for (int i = 2; i <= r; i++)
			down *= i;
		return up / down;
	}
}
つかさDiv2 500 なのに結構深いんだねー。
こなたところでかがみんはこの回には参加したの?
かがみ参加はしてないわ。練習部屋で解いただけ。
こなた結果は?
かがみSystem test failed...
こなたえっ、そうなの?
かがみそ、その後で修正してちゃんと通したもん。。。
こなたへー。自分が間違えたくせに偉そうに解説なんかしてくれちゃってんのかー。
やれやれかがみんも偉くなったもんだよ。
いやー、まったく度胸があると言うかずうずうしいと言うか身の程知らずというか分不相応というか
こなたあれ?かがみん何で工具を持ってるの。。。何か作るの。。。
みゆきああ、それは世に言う「バールのような物」ですわね☆
こなたま、待って!かがみん落ち着い(ガス

補足

chokudaiうーんうーん、私は、問題サイズ見た瞬間全探査しちゃうなぁ 0 から 1<<m まで i, j それぞれ bitcount が m になるのだけ調べて、 & 演算して bitcount して k 個以上か調べて終わりだから、あそこまで数学的要素は要らないかなーって印象
みゆきというコメントが届いているみたいです。
あ、かがみさんが席をはずしておられますので、せんえつながら私が対応させていただきます。
みゆき確かにその通りですね。 n ≦ 8 と小さいので、 O(2^n) でも充分なサイズです。ですのでこちらのコードでも System test を通すことができます。

import java.util.*;
public class TwoLotteryGames {
	public double getHigherChanceGame(int n, int m, int k) {
		int match = 0, total = 0;
		for (int i = 0; i < (1 << n); i++) {
			if (bitCount(i) == m) {
				total++;
				if (bitCount(i & ((1 << m) - 1)) >= k)
					match++;
			}
		}
		return (double)match / total;
	}

	private int bitCount(int val) {
		int ret = 0;
		for (int i = 0; i < 32; i++) {
			if ((val & (1 << i)) != 0) {
				ret++;
			}
		}
		return ret;
	}
}
みゆきただこちらのコードですと n が 29 以下という条件の場合は TLE (*1) してしまいます。
条件に応じて適切なアルゴリズムを選択すべきという好例ですね。
(*1) TLE: 時間超過 (Time Limit Exceeded) の略。 TopCoder においては 2 秒以内にプログラムが終了しなければならない。
みゆきあっ!かがみさんが戻ってきたわ!いけない!怒りに我を忘れている!
早く逃げて下さい!
chokudaiえっ
みゆきあなたは Challenge し過ぎる!もう光弾も蟲笛も効かない!
chokudaiわー、みょー(ガス

こちらのらき☆すたアイコンを使わせていただきました

戻る