「オイラープロジェクト381~390」の編集履歴(バックアップ)一覧はこちら
オイラープロジェクト381~390 - (2012/09/08 (土) 15:20:49) の最新版との変更点
追加された行は緑色になります。
削除された行は赤色になります。
*問い389
1つの偏りのない四面体のサイコロを振り、出た目Tを記録する。
T個の偏りのない六面体のサイコロを振り、出た目の合計Cを記録する。
C個の偏りのない八面体のサイコロを振り、出た目の合計Oを記録する。
O個の偏りのない十二面体のサイコロを振り、出た目の合計Dを記録する。
D個の偏りのない二十面体のサイコロを振り、出た目の合計Iを記録する。
Iの分散を四捨五入して小数点以下第4位まで求めよ。
この問題は難しい、最初読んだとき一瞬何を問われてるか意味がわからなかった。
多段動的計画法とでも呼べそうなものをでっちあげて解こうとしてるもののコードが間違ってないかlong doubleで精度や桁数が足りるのか,最後の四捨五入でほんの少しだけ違う値にならないか?
とにかく難しい。
書きかけコード。
とりあえず動的計画法を使って、memo20に20面さいころを振った場合合計がnになる場合の組み合わせ数をmemo[n]に保管しています。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include <iomanip>
//#ifdef __FreeBSD__
//#include <floatingpoint.h> /* 必ず include する. */
//#endif
long double memo6[25]={0},memo8[193]={0},memo12[2305]={0},memo20[46081]={0};
long double memo4[5]={0,1,1,1,1};//4つからスタート
//一般化しないとこの問題は難しすぎるので抽象化して解く
long double memo[46081]={0},memo2[46081];//memo[さいの目の合計数]
//long double memo[25],memo2[25];
void calc(int d,int max,long double* multiMemo,long double* nextMemo){
//dはダイスの面数、maxはダイスの個数,multiMemoはさいころを転がす回数の組、nextMemoは次の結果
memset(memo, 0,sizeof(memo));
memset(memo2,0,sizeof(memo2));
memo[0]=1;
int up=max*d;
for(int i=1;i<=max;i++){
//振るさいころの数
for(int j=1;j<=d;j++){
//さいころの目
for(int L=up-j;L>=0;L--){
//さいころの合計数に新しいサイの結果を足す
memo2[L+j]+=memo[L];
}
}
for(int j=1;j<=up;j++){
nextMemo[j]+=memo2[j]*multiMemo[i];
}
memcpy(memo,memo2,sizeof(memo));
memset(memo2,0,sizeof(memo2));
}
}
int main(){
//#ifdef __FreeBSD__
// fpsetprec(FP_PE); /* これを演算の実行文の前におく. */
//#endif
//桁数足りるかな、、、
//4 24 24*8=192 192*12=2304, 2304*20=46080
//long doubleで精度や桁数が足らなかったらどうしよう、、、
calc(6,4,memo4,memo6);
calc(8,24,memo6,memo8);
calc(12,192,memo8,memo12);
calc(20,2304,memo12,memo20);
long double all=0,E=0,ans=0;
for(int i=1;i<46081;i++){
all+=memo20[i];
}
for(int i=1;i<46081;i++){
E+=(memo20[i]/all)*i;
}
for(long double i=1;i<46081;i++){
ans+=(i-E)*(i-E)*memo20[(int)i]/all;
}
std::cout.precision(4);
std::cout.setf(std::ios::fixed);
std::cout<<"総数"<<all<<"\n平均="<<E<<"\n分散"<<ans<<"\n";
}
*問い389
1つの偏りのない四面体のサイコロを振り、出た目Tを記録する。
T個の偏りのない六面体のサイコロを振り、出た目の合計Cを記録する。
C個の偏りのない八面体のサイコロを振り、出た目の合計Oを記録する。
O個の偏りのない十二面体のサイコロを振り、出た目の合計Dを記録する。
D個の偏りのない二十面体のサイコロを振り、出た目の合計Iを記録する。
Iの分散を四捨五入して小数点以下第4位まで求めよ。
解法
この問題は難しい、最初読んだとき一瞬何を問われてるか意味がわからなかった。
とりあえず多段動的計画法とでも呼べそうなものをでっちあげて解こうとしてるもののコードが間違ってないかlong doubleで精度や桁数が足りるのか,最後の四捨五入でほんの少しだけ違う値にならないか?
4面の結果から動的計画法で6面の組み合わせ数を求め、6面の結果から8面、、12面から20面の結果を求めるコードです。
最後に20面さいころを振った場合、目の合計がnになる場合の組み合わせ数をmemo20[n]に保管してみました。
一応数学の定義通りに平均と分散を出してみたのですが不正解となっています。
コードに記述ミスがあるのか精度が悪いのか方法が悪いのかよくわからない状態です。
#include<stdio.h>
#include<iostream>
#include<string.h>
#include <iomanip>
#include <quadmath.h>
__float128 memo6[25]={0},memo8[193]={0},memo12[2305]={0},memo20[46081]={0};//memo[目の合計]の組み合わせ数が入る
__float128 memo4[5]={0,1,1,1,1};//4面ダイスからスタート
//一般化しないとこの問題は難しすぎるので抽象化して解く
__float128 memo[46081]={0},memo2[46081];//memo[さいの目の合計数]
//__float128 memo[25],memo2[25];
void calc(int d,int max,__float128* multiMemo,__float128* nextMemo){
//dはダイスの面数、maxはダイスの個数,multiMemoはさいころを転がす回数の組、nextMemoは次の結果
//動的計画法で次のダイスを振る数を求める
memset(memo, 0,sizeof(memo));
memset(memo2,0,sizeof(memo2));
memo[0]=1;
int up=max*d;
for(int i=1;i<=max;i++){
//振るさいころの数
for(int j=1;j<=d;j++){
//さいころの目
for(int L=up-j;L>=0;L--){
//さいころの合計数に新しいサイの結果を足す
memo2[L+j]+=memo[L];
}
}
for(int j=1;j<=up;j++){
nextMemo[j]+=memo2[j]*multiMemo[i];
}
memcpy(memo,memo2,sizeof(memo));
memset(memo2,0,sizeof(memo2));
}
}
int main(){
//4 4*6=24 24*8=192 192*12=2304, 2304*20=46080
calc(6,4,memo4,memo6);//動的計画法で6面ダイスを1個振った場合から4個振った場合までの組み合わせ数をmemo6に代入
calc(8,24,memo6,memo8);//8面ダイスを1個振った場合から24個振った場合までの組み合わせ数をmemo8に代入
calc(12,192,memo8,memo12);//以下同じ
calc(20,2304,memo12,memo20);//20面になると入ってる組み合わせ数が凄いことに、、、
//組み合わせの総数を求める
__float128 all=0,E=0,ans=0;
for(int i=1;i<46081;i++){
all+=memo20[i];
}
//20面の全平均を出す
for(int i=1;i<46081;i++){
E+=(memo20[i]/all)*i;
}
//分散を出す
for(__float128 i=1;i<46081;i++){
ans+=(i-E)*(i-E)*memo20[(int)i]/(all);
}
//分散ansを出力したいが方法が分からない,,,
char c[80];
quadmath_snprintf(c,80,"%.4Qf",ans);//サイト通り記述したのにコンパイルエラーが出てます
}