ペプシ算クラス
ペプシ算 - 永字八法の続き
ペプシ算のクラスを作ってみた。
※書き直した!
サンプルスクリプト
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になるか、分岐が発生する。
ボトルがそろうか、全部開けてしまうかするまで、このツリーを全て調べる。