ペプシ算クラス
ペプシ算 - 永字八法の続き
ペプシ算のクラスを作ってみた。
※書き直した!
サンプルスクリプト
use Pepsi; # 問題設定をする。 my $pepsi = Pepsi->new( kind=>5, # 5種類のおまけつきを bottle=>12 # 1ダース買う ); $pepsi->calc; # 計算する my $result = $pepsi->total; # 計算結果を得る # 計算結果は、何種類そろったかをキーとして確率を格納したハッシュリファレンス # 計算結果を表示する。 print 'buy '.$pepsi->bottle." bottles.\n"; foreach my $get_kind ( sort { $a <=> $b } keys %{$result} ) { print 'get '.$get_kind.' kind(s) : %'.($result->{$get_kind}*100)."\n"; }
モジュール
package Pepsi; use strict; use bignum; use base qw ( Class::Accessor ); __PACKAGE__->mk_accessors(qw( open_bottle bottle kind get_kind patern )); sub new { my $invocant = shift; my $class = ref $invocant; $class ||= $invocant; my $self = bless {patern=>1, @_}=>$class; return $self; } sub rest_kind { my $self = shift; my $rest = $self->kind - $self->get_kind; return $rest; } sub calc { my $self = shift; $self->is_end and return; my @child = (); push @child, $self->new( open_bottle=>(1+$self->open_bottle), bottle=>$self->bottle, kind=>$self->kind, get_kind=>(1+$self->get_kind), patern=>($self->rest_kind * $self->patern) ); # 新種が出た場合&最初の一本は必ず新種 if ( $self->open_bottle > 0 ) { push @child, $self->new( open_bottle=>(1+$self->open_bottle), bottle=>$self->bottle, kind=>$self->kind, get_kind=>$self->get_kind, patern=>($self->get_kind * $self->patern) ); # 新種が出なかった場合 } $self->{child} = \@child; map { $_->calc } @child; return 1; } sub is_end { my $self = shift; $self->get_kind < $self->kind or return 1; $self->open_bottle < $self->bottle or return 1; return undef; } sub is_live { return shift->is_end ? undef : 1; } sub ends { my $self = shift; my $result = shift; $result ||= []; if ( $self->{child} ) { map { $_->ends($result) } @{$self->{child}}; } else { push @{$result}, $self; # print "End!\n"; } return $result; } sub fraction { my $self = shift; my $patern = $self->patern; my $open_bottle = $self->open_bottle; while ( $open_bottle < $self->bottle ) { $patern *= $self->kind; ++ $open_bottle; } return $patern; } sub total_child { my $self = shift; my %hash = (); map { $hash{$_->get_kind} += $_->fraction } @{$self->ends}; return \%hash; } sub total_mother { my $self = shift; return $self->kind ** $self->bottle; } sub total { my $self = shift; my $child = $self->total_child; my $mother = $self->total_mother; foreach my $num ( keys %{$child} ) { $child->{$num} /= $mother; } return $child; } 1;
考え方
まず、0種類の食玩がそろっているとする。
最初のボトルをあけると、0が1になる。
次のボトルをあけると、この1が1のままか2になるか、分岐が発生する。
ボトルがそろうか、全部開けてしまうかするまで、このツリーを全て調べる。
ペプシ算ファイナル
サンプルスクリプト
use Pepsi; # 問題設定をする。 my $pepsi = Pepsi->new( kind=>5 # 5種類のおまけつき食玩具 ); print $pepsi->percent(10); # 10本あけて、コンプする確率は? print "\n"; print $pepsi->limit(0.9); # では、コンプ率が90%を超えるのは何本から? print "\n"; print $pepsi->limit(0.95); # では、コンプ率が95%を超えるのは何本から? print "\n"; my $res = $pepsi->probability(15); # 15本開けた時、どんな確率で何種類集まる? my $num = 0; foreach my $proba ( @{$res} ) { print 'get '.$num.' kind(s) : % '.$proba."\n"; ++ $num; } print "\n"; $res = $pepsi->get_patern(15); # 同じ内容を、分母で割る前の値で取得したい。 $num = 0; foreach my $proba ( @{$res} ) { print 'get '.$num.' kind(s) : % '.$proba."\n"; ++ $num; } print "\n"; print $pepsi->mother(15); # その時の分母を取得する。
実行結果
0.5225472 18 21 get 0 kind(s) : % 0 get 1 kind(s) : % 0.00000000016384 get 2 kind(s) : % 0.00001073676288 get 3 kind(s) : % 0.00466963857408 get 4 kind(s) : % 0.166550372352 get 5 kind(s) : % 0.8287692521472 get 0 kind(s) : % get 1 kind(s) : % 5 get 2 kind(s) : % 327660 get 3 kind(s) : % 142506060 get 4 kind(s) : % 5082714000 get 5 kind(s) : % 25292030400 30517578125
モジュール
package Pepsi; use strict; use bignum; use base qw ( Class::Accessor ); __PACKAGE__->mk_accessors(qw( bottle kind )); sub new { my $invocant = shift; my $class = ref $invocant; $class ||= $invocant; my $self = bless {@_}=>$class; $self->init; return $self; } sub open_bottle { my $self = shift; my $now_bottle = $self->bottle; my $next_bottle = $now_bottle + 1; foreach my $kind ( 0..$self->kind ) { # かぶる場合 my $new_num = $self->{table}->[$now_bottle]->[$kind] * $kind; $new_num and $self->{table}->[$next_bottle]->[$kind] += $new_num; # 新しい種類が出る場合 $new_num = $self->{table}->[$now_bottle]->[$kind] * ($self->kind - $kind); $new_num and $self->{table}->[$next_bottle]->[$kind+1] += $new_num; } ++ $self->{bottle}; } sub get_patern { my $self = shift; my $bottle = undef; if ( @_ ) { $bottle = shift; } else { $bottle = $self->bottle; } $bottle += 0; until ( $self->{table}->[$bottle] ) { $self->open_bottle; } return $self->{table}->[$bottle]; } sub percent { my $self = shift; my ( $bottle, $kind ) = (); @_ and ( $bottle, $kind ) = @_; $bottle ||= $self->bottle; $kind ||= $self->kind; my $patern = $self->get_patern($bottle); my $result = $patern->[$kind]; $result /= $self->mother($bottle); return $result; } sub probability { my $self = shift; my $bottle = undef; if ( @_ ) { $bottle = shift; } else { $bottle = $self->bottle; } my $patern = $self->get_patern($bottle); my $mother = $self->mother($bottle); my @res = @{$patern}; map { $_ /= $mother } @res; return \@res; } sub limit { my $self = shift; my $limit = shift; $limit ||= 0.9; my $bottle = 1; while ( $self->percent($bottle) < $limit ) { ++ $bottle; } return $bottle; } sub mother { my $self = shift; my $bottle = undef; if ( @_ ) { $bottle = shift; } else { $bottle = $self->bottle; } $bottle += 0; $self->{mother}->[$bottle] ||= $self->mother($bottle-1) * $self->kind; return $self->{mother}->[$bottle]; } sub init { my $self = shift; $self->{bottle} = 0; $self->{mother} = [1]; $self->{table} = [[1]]; } 1;
感想
でもさあ、よく考えたら、今の食玩って中身まる見えでないといけないんだよね。コンプするのに買い込む必要がないと言うか……。
ペプシ算クラス
ペプシ算 - 永字八法の続き
ペプシ算のクラスを作ってみた。
※書き直した!
サンプルスクリプト
use Pepsi; # 問題設定をする。 my $pepsi = Pepsi->new( kind=>5, # 5種類のおまけつきを bottle=>12 # 1ダース買う ); $pepsi->calc; # 計算する my $result = $pepsi->total; # 計算結果を得る # 計算結果は、何種類そろったかをキーとして確率を格納したハッシュリファレンス # 計算結果を表示する。 print 'buy '.$pepsi->bottle." bottles.\n"; foreach my $get_kind ( sort { $a <=> $b } keys %{$result} ) { print 'get '.$get_kind.' kind(s) : %'.($result->{$get_kind}*100)."\n"; }
モジュール
package Pepsi; use strict; use bignum; use base qw ( Class::Accessor ); __PACKAGE__->mk_accessors(qw( open_bottle bottle kind get_kind patern )); sub new { my $invocant = shift; my $class = ref $invocant; $class ||= $invocant; my $self = bless {patern=>1, @_}=>$class; return $self; } sub rest_kind { my $self = shift; my $rest = $self->kind - $self->get_kind; return $rest; } sub calc { my $self = shift; $self->is_end and return; my @child = (); push @child, $self->new( open_bottle=>(1+$self->open_bottle), bottle=>$self->bottle, kind=>$self->kind, get_kind=>(1+$self->get_kind), patern=>($self->rest_kind * $self->patern) ); # 新種が出た場合&最初の一本は必ず新種 if ( $self->open_bottle > 0 ) { push @child, $self->new( open_bottle=>(1+$self->open_bottle), bottle=>$self->bottle, kind=>$self->kind, get_kind=>$self->get_kind, patern=>($self->get_kind * $self->patern) ); # 新種が出なかった場合 } $self->{child} = \@child; map { $_->calc } @child; return 1; } sub is_end { my $self = shift; $self->get_kind < $self->kind or return 1; $self->open_bottle < $self->bottle or return 1; return undef; } sub is_live { return shift->is_end ? undef : 1; } sub ends { my $self = shift; my $result = shift; $result ||= []; if ( $self->{child} ) { map { $_->ends($result) } @{$self->{child}}; } else { push @{$result}, $self; # print "End!\n"; } return $result; } sub fraction { my $self = shift; my $patern = $self->patern; my $open_bottle = $self->open_bottle; while ( $open_bottle < $self->bottle ) { $patern *= $self->kind; ++ $open_bottle; } return $patern; } sub total_child { my $self = shift; my %hash = (); map { $hash{$_->get_kind} += $_->fraction } @{$self->ends}; return \%hash; } sub total_mother { my $self = shift; return $self->kind ** $self->bottle; } sub total { my $self = shift; my $child = $self->total_child; my $mother = $self->total_mother; foreach my $num ( keys %{$child} ) { $child->{$num} /= $mother; } return $child; } 1;
考え方
まず、0種類の食玩がそろっているとする。
最初のボトルをあけると、0が1になる。
次のボトルをあけると、この1が1のままか2になるか、分岐が発生する。
ボトルがそろうか、全部開けてしまうかするまで、このツリーを全て調べる。
ペプシ算ファイナル
サンプルスクリプト
use Pepsi; # 問題設定をする。 my $pepsi = Pepsi->new( kind=>5 # 5種類のおまけつき食玩具 ); print $pepsi->percent(10); # 10本あけて、コンプする確率は? print "\n"; print $pepsi->limit(0.9); # では、コンプ率が90%を超えるのは何本から? print "\n"; print $pepsi->limit(0.95); # では、コンプ率が95%を超えるのは何本から? print "\n"; my $res = $pepsi->probability(15); # 15本開けた時、どんな確率で何種類集まる? my $num = 0; foreach my $proba ( @{$res} ) { print 'get '.$num.' kind(s) : % '.$proba."\n"; ++ $num; } print "\n"; $res = $pepsi->get_patern(15); # 同じ内容を、分母で割る前の値で取得したい。 $num = 0; foreach my $proba ( @{$res} ) { print 'get '.$num.' kind(s) : % '.$proba."\n"; ++ $num; } print "\n"; print $pepsi->mother(15); # その時の分母を取得する。
実行結果
0.5225472 18 21 get 0 kind(s) : % 0 get 1 kind(s) : % 0.00000000016384 get 2 kind(s) : % 0.00001073676288 get 3 kind(s) : % 0.00466963857408 get 4 kind(s) : % 0.166550372352 get 5 kind(s) : % 0.8287692521472 get 0 kind(s) : % get 1 kind(s) : % 5 get 2 kind(s) : % 327660 get 3 kind(s) : % 142506060 get 4 kind(s) : % 5082714000 get 5 kind(s) : % 25292030400 30517578125
モジュール
package Pepsi; use strict; use bignum; use base qw ( Class::Accessor ); __PACKAGE__->mk_accessors(qw( bottle kind )); sub new { my $invocant = shift; my $class = ref $invocant; $class ||= $invocant; my $self = bless {@_}=>$class; $self->init; return $self; } sub open_bottle { my $self = shift; my $now_bottle = $self->bottle; my $next_bottle = $now_bottle + 1; foreach my $kind ( 0..$self->kind ) { # かぶる場合 my $new_num = $self->{table}->[$now_bottle]->[$kind] * $kind; $new_num and $self->{table}->[$next_bottle]->[$kind] += $new_num; # 新しい種類が出る場合 $new_num = $self->{table}->[$now_bottle]->[$kind] * ($self->kind - $kind); $new_num and $self->{table}->[$next_bottle]->[$kind+1] += $new_num; } ++ $self->{bottle}; } sub get_patern { my $self = shift; my $bottle = undef; if ( @_ ) { $bottle = shift; } else { $bottle = $self->bottle; } $bottle += 0; until ( $self->{table}->[$bottle] ) { $self->open_bottle; } return $self->{table}->[$bottle]; } sub percent { my $self = shift; my ( $bottle, $kind ) = (); @_ and ( $bottle, $kind ) = @_; $bottle ||= $self->bottle; $kind ||= $self->kind; my $patern = $self->get_patern($bottle); my $result = $patern->[$kind]; $result /= $self->mother($bottle); return $result; } sub probability { my $self = shift; my $bottle = undef; if ( @_ ) { $bottle = shift; } else { $bottle = $self->bottle; } my $patern = $self->get_patern($bottle); my $mother = $self->mother($bottle); my @res = @{$patern}; map { $_ /= $mother } @res; return \@res; } sub limit { my $self = shift; my $limit = shift; $limit ||= 0.9; my $bottle = 1; while ( $self->percent($bottle) < $limit ) { ++ $bottle; } return $bottle; } sub mother { my $self = shift; my $bottle = undef; if ( @_ ) { $bottle = shift; } else { $bottle = $self->bottle; } $bottle += 0; $self->{mother}->[$bottle] ||= $self->mother($bottle-1) * $self->kind; return $self->{mother}->[$bottle]; } sub init { my $self = shift; $self->{bottle} = 0; $self->{mother} = [1]; $self->{table} = [[1]]; } 1;
感想
でもさあ、よく考えたら、今の食玩って中身まる見えでないといけないんだよね。コンプするのに買い込む必要がないと言うか……。
ペプシ算クラス
ペプシ算 - 永字八法の続き
ペプシ算のクラスを作ってみた。
※書き直した!
サンプルスクリプト
use Pepsi; # 問題設定をする。 my $pepsi = Pepsi->new( kind=>5, # 5種類のおまけつきを bottle=>12 # 1ダース買う ); $pepsi->calc; # 計算する my $result = $pepsi->total; # 計算結果を得る # 計算結果は、何種類そろったかをキーとして確率を格納したハッシュリファレンス # 計算結果を表示する。 print 'buy '.$pepsi->bottle." bottles.\n"; foreach my $get_kind ( sort { $a <=> $b } keys %{$result} ) { print 'get '.$get_kind.' kind(s) : %'.($result->{$get_kind}*100)."\n"; }
モジュール
package Pepsi; use strict; use bignum; use base qw ( Class::Accessor ); __PACKAGE__->mk_accessors(qw( open_bottle bottle kind get_kind patern )); sub new { my $invocant = shift; my $class = ref $invocant; $class ||= $invocant; my $self = bless {patern=>1, @_}=>$class; return $self; } sub rest_kind { my $self = shift; my $rest = $self->kind - $self->get_kind; return $rest; } sub calc { my $self = shift; $self->is_end and return; my @child = (); push @child, $self->new( open_bottle=>(1+$self->open_bottle), bottle=>$self->bottle, kind=>$self->kind, get_kind=>(1+$self->get_kind), patern=>($self->rest_kind * $self->patern) ); # 新種が出た場合&最初の一本は必ず新種 if ( $self->open_bottle > 0 ) { push @child, $self->new( open_bottle=>(1+$self->open_bottle), bottle=>$self->bottle, kind=>$self->kind, get_kind=>$self->get_kind, patern=>($self->get_kind * $self->patern) ); # 新種が出なかった場合 } $self->{child} = \@child; map { $_->calc } @child; return 1; } sub is_end { my $self = shift; $self->get_kind < $self->kind or return 1; $self->open_bottle < $self->bottle or return 1; return undef; } sub is_live { return shift->is_end ? undef : 1; } sub ends { my $self = shift; my $result = shift; $result ||= []; if ( $self->{child} ) { map { $_->ends($result) } @{$self->{child}}; } else { push @{$result}, $self; # print "End!\n"; } return $result; } sub fraction { my $self = shift; my $patern = $self->patern; my $open_bottle = $self->open_bottle; while ( $open_bottle < $self->bottle ) { $patern *= $self->kind; ++ $open_bottle; } return $patern; } sub total_child { my $self = shift; my %hash = (); map { $hash{$_->get_kind} += $_->fraction } @{$self->ends}; return \%hash; } sub total_mother { my $self = shift; return $self->kind ** $self->bottle; } sub total { my $self = shift; my $child = $self->total_child; my $mother = $self->total_mother; foreach my $num ( keys %{$child} ) { $child->{$num} /= $mother; } return $child; } 1;
考え方
まず、0種類の食玩がそろっているとする。
最初のボトルをあけると、0が1になる。
次のボトルをあけると、この1が1のままか2になるか、分岐が発生する。
ボトルがそろうか、全部開けてしまうかするまで、このツリーを全て調べる。
ペプシ算ファイナル
サンプルスクリプト
use Pepsi; # 問題設定をする。 my $pepsi = Pepsi->new( kind=>5 # 5種類のおまけつき食玩具 ); print $pepsi->percent(10); # 10本あけて、コンプする確率は? print "\n"; print $pepsi->limit(0.9); # では、コンプ率が90%を超えるのは何本から? print "\n"; print $pepsi->limit(0.95); # では、コンプ率が95%を超えるのは何本から? print "\n"; my $res = $pepsi->probability(15); # 15本開けた時、どんな確率で何種類集まる? my $num = 0; foreach my $proba ( @{$res} ) { print 'get '.$num.' kind(s) : % '.$proba."\n"; ++ $num; } print "\n"; $res = $pepsi->get_patern(15); # 同じ内容を、分母で割る前の値で取得したい。 $num = 0; foreach my $proba ( @{$res} ) { print 'get '.$num.' kind(s) : % '.$proba."\n"; ++ $num; } print "\n"; print $pepsi->mother(15); # その時の分母を取得する。
実行結果
0.5225472 18 21 get 0 kind(s) : % 0 get 1 kind(s) : % 0.00000000016384 get 2 kind(s) : % 0.00001073676288 get 3 kind(s) : % 0.00466963857408 get 4 kind(s) : % 0.166550372352 get 5 kind(s) : % 0.8287692521472 get 0 kind(s) : % get 1 kind(s) : % 5 get 2 kind(s) : % 327660 get 3 kind(s) : % 142506060 get 4 kind(s) : % 5082714000 get 5 kind(s) : % 25292030400 30517578125
モジュール
package Pepsi; use strict; use bignum; use base qw ( Class::Accessor ); __PACKAGE__->mk_accessors(qw( bottle kind )); sub new { my $invocant = shift; my $class = ref $invocant; $class ||= $invocant; my $self = bless {@_}=>$class; $self->init; return $self; } sub open_bottle { my $self = shift; my $now_bottle = $self->bottle; my $next_bottle = $now_bottle + 1; foreach my $kind ( 0..$self->kind ) { # かぶる場合 my $new_num = $self->{table}->[$now_bottle]->[$kind] * $kind; $new_num and $self->{table}->[$next_bottle]->[$kind] += $new_num; # 新しい種類が出る場合 $new_num = $self->{table}->[$now_bottle]->[$kind] * ($self->kind - $kind); $new_num and $self->{table}->[$next_bottle]->[$kind+1] += $new_num; } ++ $self->{bottle}; } sub get_patern { my $self = shift; my $bottle = undef; if ( @_ ) { $bottle = shift; } else { $bottle = $self->bottle; } $bottle += 0; until ( $self->{table}->[$bottle] ) { $self->open_bottle; } return $self->{table}->[$bottle]; } sub percent { my $self = shift; my ( $bottle, $kind ) = (); @_ and ( $bottle, $kind ) = @_; $bottle ||= $self->bottle; $kind ||= $self->kind; my $patern = $self->get_patern($bottle); my $result = $patern->[$kind]; $result /= $self->mother($bottle); return $result; } sub probability { my $self = shift; my $bottle = undef; if ( @_ ) { $bottle = shift; } else { $bottle = $self->bottle; } my $patern = $self->get_patern($bottle); my $mother = $self->mother($bottle); my @res = @{$patern}; map { $_ /= $mother } @res; return \@res; } sub limit { my $self = shift; my $limit = shift; $limit ||= 0.9; my $bottle = 1; while ( $self->percent($bottle) < $limit ) { ++ $bottle; } return $bottle; } sub mother { my $self = shift; my $bottle = undef; if ( @_ ) { $bottle = shift; } else { $bottle = $self->bottle; } $bottle += 0; $self->{mother}->[$bottle] ||= $self->mother($bottle-1) * $self->kind; return $self->{mother}->[$bottle]; } sub init { my $self = shift; $self->{bottle} = 0; $self->{mother} = [1]; $self->{table} = [[1]]; } 1;
感想
でもさあ、よく考えたら、今の食玩って中身まる見えでないといけないんだよね。コンプするのに買い込む必要がないと言うか……。