ペプシ算ファイナル

サンプルスクリプト

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;

解説

もっとブラックボックス化できた。
食玩の種類数を指定するだけで、必要な数字を取得できるようにした。ひゃっほう。

感想

でもさあ、よく考えたら、今の食玩って中身まる見えでないといけないんだよね。コンプするのに買い込む必要がないと言うか……。