アルゴリズムイントロダクション 練習問題5.1-3
問題
0と1をそれぞれ確率1/2で出力する問題を考える。利用できるのは0か1かを出力する手続きBIASED-RANDOMである。BIASED-RANDOMは1を確率p、0を確率1-pで出力する(0
やっぱり実装まで。期待実行時間は出していない。あと証明もまだ(これはたぶんすぐできる)。
発想
- PK戦のサドンデスで、先攻が勝ったら1、後攻が勝ったら0を返すとすればよい
public class Utility { public static boolean randomBoolean() { boolean a = true; boolean b = true; double p = 0.7; // Any p is OK. s.t. 0 < p < 1 while (!(a ^ b) ) { a = randomBooleanByProbability(p); b = randomBooleanByProbability(p); } return a; } // BIASED-RANDOM public static boolean randomBooleanByProbability(double p) { if (Math.random() < p ) return true; else return false; } }
import static org.junit.Assert.*; import org.junit.Test; import com.gmail.seizans.algorithm.Utility; public class UtilityTest { @Test public void randomBooleanTest() throws Exception { int numberOfTrials = 20000; double minProb = 0.95; double maxProb = 1.05; int counter = 0; boolean isOk = false; for (int i = 0; i < numberOfTrials; i++) { if (Utility.randomBoolean() ) counter++; } if (numberOfTrials * minProb < counter * 2 && counter * 2 < numberOfTrials * maxProb ) { isOk = true; } assertTrue(isOk); } @Test public void randomBooleanByProbabilityTest() throws Exception { double p = 0.66666; int numberOfTrials = 20000; double minProb = 0.95; double maxProb = 1.05; boolean isOk = false; int counter = 0; for (int i = 0; i < numberOfTrials; i++) { if (Utility.randomBooleanByProbability(p) ) counter++; } if (p * numberOfTrials * minProb < counter && counter < p * numberOfTrials * maxProb) { isOk = true; } assertTrue(isOk); } }
アルゴリズムイントロダクション 練習問題5.1-2
問題:
RANDOM(0,1) に対する呼び出しだけを用いて手続き RANDOM(a,b) を実現せよ。実現された手続きの平均実行時間を a と b の関数として表現せよ。
RANDOM(a,b) とは、a<=x<=bな整数xをランダムに返す関数です。
とりあえず実装まで。
発想:
- RANDOM(a,b)を実現するのはRANDOM(0,n)を実現するのと同値だからこれをやる
- nが2の累乗ならばRANDOM(0,1)をln(n)回やればOKなんだけどなぁ・・・
- あ、無効な値になってしまったときはもう1回やればいいや!
import java.util.Random; public class Utility { // This method means Random(0, 1) public static int randomZeroOne() { Random random = new Random(); int result = random.nextInt(2); return result; } public static int log2(int n) { if (n <= 0) { throw new IllegalArgumentException(); } int result = (int) Math.ceil(Math.log(n) / Math.log(2) ); return result; } public static int random(int a, int b) { if (a >= b || a < 0) { throw new IllegalArgumentException(); } int result = 0; int m = log2(b - a); while (true) { for (int i = 0; i < m; i++) { result += randomZeroOne() * Math.pow(2, i); } result += a; if (result <= b) { return result; } else { result = 0; } } } }
import com.gmail.seizans.algorithm.Utility; import static org.hamcrest.CoreMatchers.is; import static com.gmail.seizans.algorithm.Utility.log2; public class UtilityTest { @Test public void log2Test() throws Exception { assertThat(log2(1), is(0) ); assertThat(log2(2), is(1) ); assertThat(log2(3), is(2) ); assertThat(log2(4), is(2) ); assertThat(log2(5), is(3) ); assertThat(log2(6), is(3) ); assertThat(log2(7), is(3) ); assertThat(log2(8), is(3) ); } @Test public void randomTest() throws Exception { int a = 20; int b = 30; boolean isOk = true; int numberOfTrials = 50; for (int i = 0; i < numberOfTrials; i++) { int r = Utility.random(a, b); if (r < a || b < r) { isOk = false; } System.out.println(r); } assertTrue(isOk); } }
とりあえず動いているかのように見える。
しょっぱい点に気づかれたら教えてくださいませ。どうか。