管理人のタツです。
世間はゴールデンウイーク真っ只中。
私も地元に帰省しています。
正月やお盆など、混むのがわかってる時期にわざわざ帰省しない民ですが、
ゴールデンウィークに関しては、
この時期を逃すと帰省のタイミングがなくなってしまうので、今回は帰省しています。
毎年、ゴールデンウィーク、7月の海の日当たり、11月の勤労感謝の日当たりの計3回帰省します。
さて、今回は題の通りです。
少し前まで、とある試験問題(過去問計800問)を毎日ランダムで10問解いていたのですが、
今更になって、それって全部の問題に出会うのに何日かかるのだろう、と今朝疑問に思いました。
重複なければもちろん800÷10=80日ですが、今回は重複ありです。
では早速。
あ、今回はJavaです。
プログラム組んでみた
あーだこーだ考えて、結局落ち着いたソースを一気に紹介します。
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;
public class Sample01
{
public static void main(String[] args)
{
int iNumQ = 800;
int iOneTime = 10;
int iJikken = 100;
String strOut = "";
String strPath = "C:\\Users\\tatsu\\work\\Temp\\text1.csv";
for(int k = 0; k < iJikken; k++)
{
boolean[] bField = new boolean[iNumQ];
int iCnt = 0;
while(!bCheckField(bField))
{
for(int i = 0; i < iOneTime; i++)
{
int iTemp = iRandom(0, iNumQ - 1);
bField[iTemp] = true;
}
iCnt++;
}
strOut += iCnt;
if(k != iJikken - 1)
strOut += ",";
}
FileOutput(strPath, strOut);
}
//ランダム生成
static int iRandom(int iFrom, int iTo)
{
int iInterval = iTo - iFrom + 1;
Random rand = new Random();
int iRtn = rand.nextInt(iInterval) + iFrom;
return iRtn;
}
//ファイル出力
static void FileOutput(String strFilePath, String strText)
{
try
{
// FileWriterクラスのオブジェクトを生成する
FileWriter file = new FileWriter(strFilePath);
// PrintWriterクラスのオブジェクトを生成する
PrintWriter pw = new PrintWriter(new BufferedWriter(file));
//ファイルに書き込む
pw.println(strText);
//ファイルを閉じる
pw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
static boolean bCheckField(boolean[] bField)
{
for(int i = 0; i < bField.length; i++)
{
if(!bField[i])
return false;
}
return true;
}
}少しだけ解説
今回、何日かかるか計算したうえで、CSV出力しています。
まずは宣言
- 11行目 iNumQ : 問題数
- 12行目 iOneTime : 1日何問解くか
- 14行目 iJikken : 100回処理を試す
- 15行目 strOut : CSV出力用のString型
- 16行目 strPath : CSV出力先パス
やり方は、boolean型1次元配列を作り、
10か所ランダムでtrueにします。
それを繰り返し、配列すべての要素がtrueになったら処理終了、
というところです。
- 20行目 bField[] : boolean型配列(初期値false)
- 21行目 iCnt : 何日かかるか
- 26行目 iTemp : ランダムで要素番号を作る
- 27行目 : 26行目で作った要素番号の要素をtrueに変換
- 29行目 : 10か所trueにすれば、日数インクリメント
後は日数を32行目で追加、それを100回ループ。
37行目でファイル出力しています。
ランダム生成関数と、ファイル出力関数は自作メソッドで。
ファイル出力メソッドは以下を参考にさせていただきました。

別にCSV出力する意味はあまりないですが、
こういう個所で新しい知識を試さないと身に着かないので。
因みに上記の場合、
1日10問選択し、その10問の中にも重複する場合が考えられますが、
今回はそれも許容します。
許容しなければもう少しコードを付け加えなければいけませんが。
出力結果
これ位だと処理する時間に1秒もかかりません。
CSVを開きますと

400前後で落ち着きそうですね。
averageとmedianを計算してみます。
今回外れ値が出てくるかもしれませんが、それも考慮します。
可能性はありますからね。
average = 410.32
median = 394
やはり大体400日前後かかりますか。
ちょっとだけ、試行回数を増やしてみます。
iJikkenを1,000にして実行。
average = 418.711
median = 403
でした。
やはり400日程度でいいでしょう。
どうでもいいですが、試行回数を1000にすると、
Excelで開いた時一番最後の列番号がALLだったの、少しフフッってなりました。
因みに散布図です。

数学的には?
調べたところ、おそらく「クーポン収集問題」に類似しているみたいです。
ただ私は解析系はあまりわからず…。
そこで、ChatGPTさんに尋ねたところ以下のような回答が返ってきました。
この計算は解析的には複雑なので、通常はシミュレーションを行うか、近似式を用いることが一般的です。クーポン収集問題の一般的な近似式は nlogn+cn(n は総アイテム数、c は小さな定数)ですが、10種の問題を同時に取り出す場合の具体的な計算には高度な確率論または実験的アプローチが求められます。
この種の問題の正確な解は実験的なシミュレーションに依存することが多いため、実際の値を得るにはプログラミングや数値シミュレーションを行う必要があります。
ChatGPTより
だ、そうです。私の入力方法が間違えていなければ。
終わりに
Javaでプログラムを組んでみました。
ここまで記事書いてそういえばと思いましたが。
実験的なプログラムとはいえ、コメントは書いた方がいいなー、と。
5月、会社命令でとあるプログラミング試験を受けるのですが、
そこでしっかりとしたコード書いて、見てもらうためにも
日ごろからコメントを書く癖はつけておかないと。
業務(C++)だと、これでもかってくらいコメント残してるんですがね。
それをブログでもやらないと。
では、今回はここまでです。

コメント