标题: [技术讨论] Bulls and Cows 猜数字游戏 搜索树生成算法 [打印本页]
作者: 523066680 时间: 2018-9-14 11:48 标题: Bulls and Cows 猜数字游戏 搜索树生成算法
本帖最后由 523066680 于 2018-9-14 12:06 编辑
论坛曾经有过大量讨论,参考链接
http://bbs.bathome.net/thread-44826-1-2.html
http://bbs.bathome.net/thread-44983-1-1.html
同时参考 a20150604 提供的资料
http://slovesnov.users.sourceforge.net/bullscows/bullscows.pdf
http://slovesnov.users.sourceforge.net/index.php?bullscows_tree,english,avgBullsCows
作者: 523066680 时间: 2018-9-14 11:51
本帖最后由 523066680 于 2018-9-14 11:52 编辑
代码是去年8月份的,现在分享出来
Perl (内联C,提高判断效率),分别生成 Perl 哈希结构 和 JSON 格式的搜索树。- =info
- 生成最平均指标搜索树
-
- Code-by : 523066680
- Date : 2017-08
- =cut
-
- use List::Util qw/max sum/;
-
- use JSON;
- use Inline C;
- use IO::Handle;
- use Data::Dumper;
- use File::Slurp;
- use Time::HiRes qw/sleep/;
- STDOUT->autoflush(1);
- $Data::Dumper::Indent = 1;
-
- #生成排列
- our @orders;
- permute( [0 .. 9] , [], 4, \@orders);
-
- #生成树
- print "Make tree\n";
- my $tree;
- $tree = { "0123" => {} };
- maketree( $tree, \@orders, 0 );
-
- #导出
- print "Dump tree\n";
- write_file("./Tree_avg2.perl.txt", Dumper $tree);
- write_file("./Tree_avg2.json.txt", encode_json($tree) );
-
- sub maketree
- {
- our @orders;
- my $orders;
- my ($ref, $arr, $lv) = @_;
- my $AB = "00";
- my %keymap;
- my %hash;
- my $minkey;
- my $minval = 10000000;
- my $amount;
-
- if ($lv == 0) { $orders = ["0123"] }
- else { $orders = \@orders }
-
- #根据参考指标,选出最优的测试项
- for my $e ( @$orders )
- {
- $amount = 0;
- %hash = ();
-
- for my $k ( @$arr )
- {
- bullcow( $k, $e, $AB );
- $hash{$AB}++;
- }
-
- #各项反馈下子集的平方和作为参考,值越小,分布越平坦,越均匀
- grep { $amount += $_ * $_ } values %hash;
- $amount = $amount/scalar(keys %hash);
-
- $keymap{$e} = $amount;
-
- if ($amount < $minval)
- {
- $minval = $amount;
- $minkey = $e;
- }
- }
-
- #如果筛选集中有符合条件的项,优先选择
- for my $k ( @$arr )
- {
- if ( $keymap{$k} == $minval ) { $minkey = $k; last; }
- }
-
- #建立反馈项和缩小集合组
- for my $k ( @$arr )
- {
- #删除 $minkey 以外的项(当前层)
- delete $ref->{$k} if ($k ne $minkey);
-
- #创建反馈项以及对应子集 / 反馈为 40 的项无需子集
- bullcow( $minkey, $k, $AB );
- $ref->{$minkey}{$AB}{$k} = {} if ( $AB ne "40" );
- }
-
- for my $ab ( keys %{ $ref->{ $minkey } } )
- {
- printf " $lv -> $ab, %d\n", $#$arr;
- maketree( $ref->{$minkey}{$ab}, [ sort keys %{$ref->{$minkey}{$ab}} ], $lv+1 );
- }
- }
-
- sub permute
- {
- my ( $a, $b, $n, $aref ) = @_;
- my $last = $#$a;
-
- if ( $#$b >= ($n-1) )
- {
- push @$aref, join("", @$b);
- return;
- }
-
- for my $idx ( 0 .. $last )
- {
- permute( [ @$a[0 .. $idx-1, $idx+1 .. $last] ], [ @$b, $a->[$idx] ], $n, $aref );
- }
- }
-
- __END__
- __C__
- void bullcow(char *stra, char *strb, char *AB)
- {
- int idx;
- char a = '0';
- char b = '0';
-
- for ( idx = 0; idx < 4; idx++ )
- {
- if ( stra[idx] == strb[idx] )
- a++;
- else
- if ( strchr(stra, strb[idx]) != 0 )
- {
- b++;
- }
- }
-
- AB[0] = a;
- AB[1] = b;
- }
复制代码
- use JSON;
- use Data::Dumper;
- use IO::Handle;
- use File::Slurp;
- use Time::HiRes qw/sleep time/;
- STDOUT->autoflush(1);
-
- $Data::Dumper::Indent = 1;
-
- my $time_a = time();
-
- our $nums = [];
- our @orders_init;
- our @orders;
- permute( [0 .. 9] , [], 4, \@orders_init);
-
- my $json;
- my $tree;
- my $struct = read_file("Tree_avg2.perl.txt");
- my $tree = eval $struct;
-
- my $iter = 0;
- my $loopi = 0;
- my $AB;
- my $guess;
- my $ref;
- my %record;
- my $times;
-
- while ( $iter <= $#orders_init )
- {
- $ref = $tree;
- $nums = $orders_init[$iter];
- print "Target Number: $nums\n";
-
- ($guess) = keys %$ref;
- #print $guess;
-
- $times = 0;
- SUB: while (1)
- {
- $loopi++;
- $times++;
- $AB = guess( $nums, $guess );
- print "[$AB] $guess\n";
-
- last SUB if ( $AB eq "40" );
-
- if ( exists $ref->{$guess}{$AB} )
- {
- $ref = $ref->{$guess}{$AB};
- ($guess) = keys %{$ref};
- }
- else
- {
- printf "there is some problem\n";
- exit;
- }
-
- if ($guess eq "")
- {
- printf "there is some problem\n";
- exit;
- }
-
- #print "-> $guess\n";
- }
- $record{$times}++;
-
- $iter++;
- }
-
- printf "Times: %d, average: %f \n", $loopi, $loopi/5040;
-
- for my $k ( sort keys %record )
- {
- printf "%s, %s\n", $k, $record{$k};
- }
-
- printf "%.3f\n", time()-$time_a;
-
- sub guess
- {
- my ($nums, $guess) = @_;
- my ($A, $B) = (0, 0);
- my $t;
-
- for my $i ( 0 .. 3 )
- {
- if ( substr($nums, $i, 1) eq substr($guess, $i, 1) )
- {
- $A++;
- }
- else
- {
- $t = substr($guess, $i, 1);
- $B++ if ( $nums =~/$t/ );
- }
- $i++;
- }
-
- return $A . $B;
- }
-
- sub permute
- {
- my ( $a, $b, $n, $aref ) = @_;
- my $last = $#$a;
-
- if ( $#$b >= ($n-1) )
- {
- push @$aref, join("", @$b);
- return;
- }
-
- for my $idx ( 0 .. $last )
- {
- permute( [ @$a[0 .. $idx-1, $idx+1 .. $last] ], [ @$b, $a->[$idx] ], $n, $aref );
- }
- }
复制代码
Times: 26537, average: 5.265278
1, 1
2, 8
3, 66
4, 574
5, 2430
6, 1852
7, 108
8, 1
作者: 523066680 时间: 2018-9-14 11:55
本帖最后由 523066680 于 2018-9-14 12:03 编辑
Landy 方案- =info
- Landy方案
-
- Code-by : 523066680
- Date : 2017-08
- =cut
-
- use List::Util qw/max sum min/;
-
- use JSON;
- use Inline C;
- use IO::Handle;
- use Data::Dumper;
- use File::Slurp;
- use Time::HiRes qw/sleep/;
- STDOUT->autoflush(1);
- $Data::Dumper::Indent = 1;
-
- #生成排列
- our @orders;
- permute( [0 .. 9] , [], 4, \@orders);
-
- #生成树
- print "Make tree\n";
- my $tree;
- $tree = { "0123" => {} };
- maketree( $tree, \@orders, 0 );
-
- #导出
- print "Dump tree\n";
- write_file("./Tree_landy.perl.txt", Dumper $tree);
- write_file("./Tree_landy.json.txt", encode_json($tree) );
-
- sub maketree
- {
- our @orders;
- my $orders;
- my ($ref, $arr, $lv) = @_;
- my $AB = "00";
- my %keymap;
- my %hash;
- my $minkey;
- my $minval = 10000000;
- my $amount;
-
- if ($lv == 0) { $orders = ["0123"] }
- else { $orders = \@orders }
-
- #根据参考指标,选出最优的测试项
- for my $e ( @$orders )
- {
- $amount = 0;
- %hash = ();
-
- for my $k ( @$arr )
- {
- bullcow( $k, $e, $AB );
- $hash{$AB}++;
- }
-
- #Times: 26439, average: 5.245833
- grep { $amount += $_ * landy($_) } values %hash;
- if ( exists $ref->{$e} ) { $amount -= 1.0 }
-
- $keymap{$e} = $amount;
-
- if ($amount < $minval)
- {
- $minval = $amount;
- $minkey = $e;
- }
- }
-
- #建立反馈项和缩小集合组
- for my $k ( @$arr )
- {
- #删除 $minkey 以外的项(当前层)
- delete $ref->{$k} if ($k ne $minkey);
-
- #创建反馈项以及对应子集 / 反馈为 40 的项无需子集
- bullcow( $minkey, $k, $AB );
- $ref->{$minkey}{$AB}{$k} = {} if ( $AB ne "40" );
- }
-
- for my $ab ( keys %{ $ref->{ $minkey } } )
- {
- printf " $lv -> $ab, %d\n", $#$arr;
- maketree( $ref->{$minkey}{$ab}, [ sort keys %{$ref->{$minkey}{$ab}} ], $lv+1 );
- }
- }
-
- sub permute
- {
- my ( $a, $b, $n, $aref ) = @_;
- my $last = $#$a;
-
- if ( $#$b >= ($n-1) )
- {
- push @$aref, join("", @$b);
- return;
- }
-
- for my $idx ( 0 .. $last )
- {
- permute( [ @$a[0 .. $idx-1, $idx+1 .. $last] ], [ @$b, $a->[$idx] ], $n, $aref );
- }
- }
-
- sub landy
- {
- my $n = shift;
- my $x = 0.0;
- my $plus = 1.0;
- my $pow;
-
- return 1 if ($n == 1);
-
- while (1)
- {
- $pow = $x ** $x;
-
- if ( $pow < $n )
- {
- $x += $plus;
- }
- elsif ( $pow > $n )
- {
- $x -= $plus;
- $plus *= 0.1;
- }
- else
- {
- last;
- }
-
- last if ($plus < 0.000001);
- }
-
- return $x;
- }
- __END__
- __C__
- void bullcow(char *stra, char *strb, char *AB)
- {
- int idx;
- char a = '0';
- char b = '0';
-
- for ( idx = 0; idx < 4; idx++ )
- {
- if ( stra[idx] == strb[idx] )
- a++;
- else
- if ( strchr(stra, strb[idx]) != 0 )
- {
- b++;
- }
- }
-
- AB[0] = a;
- AB[1] = b;
- }
复制代码
Times: 26439, average: 5.245833
1, 1
2, 7
3, 79
4, 598
5, 2438
6, 1815
7, 101
8, 1
作者: 523066680 时间: 2018-9-14 11:58
Log 估值方案- =info
- 生成搜索树 - log估值方案
-
- Code-by : 523066680
- Date : 2017-08
- =cut
-
- use List::Util qw/max sum min/;
-
- use JSON;
- use Inline C;
- use IO::Handle;
- use Data::Dumper;
- use File::Slurp;
- use Time::HiRes qw/sleep/;
- STDOUT->autoflush(1);
- $Data::Dumper::Indent = 1;
- $Data::Dumper::Sortkeys = 1;
-
- #生成排列
- our @orders;
- permute( [0 .. 9] , [], 4, \@orders);
-
- #生成树
- print "Make tree\n";
- my $tree;
- $tree = { "0123" => {} };
- maketree( $tree, \@orders, 0 );
-
- #导出
- print "Dump tree\n";
- write_file("./Tree_log.perl.txt", Dumper $tree);
- #write_file("./Tree_log.json.txt", encode_json($tree) );
-
- sub maketree
- {
- our @orders;
- my $orders;
- my ($ref, $arr, $lv) = @_;
- my $AB = "00";
- my %keymap;
- my %hash;
- my $minkey;
- my $minval = 10000000;
- my $amount;
-
- if ($lv == 0) { $orders = ["0123"] }
- else { $orders = \@orders }
-
- #根据参考指标,选出最优的测试项
- for my $e ( @$orders )
- {
- $amount = 0;
- %hash = ();
-
- for my $k ( @$arr )
- {
- bullcow( $k, $e, $AB );
- $hash{$AB}++;
- }
-
- #5.243
- grep { $amount += $_ * log($_) } values %hash;
- #$amount /= scalar(keys %hash);
-
- #5.239
- if ( exists $ref->{$e} ) { $amount -= 2*log(2) }
-
- $keymap{$e} = $amount;
-
- #if ($amount < $minval) 26408
- if ($amount < $minval) #26405
- {
- $minval = $amount;
- $minkey = $e;
- }
- }
-
- #建立反馈项和缩小集合组
- for my $k ( @$arr )
- {
- #删除 $minkey 以外的项(当前层)
- delete $ref->{$k} if ($k ne $minkey);
-
- #创建反馈项以及对应子集 / 反馈为 40 的项无需子集
- bullcow( $minkey, $k, $AB );
- $ref->{$minkey}{$AB}{$k} = {} if ( $AB ne "40" );
- }
-
- for my $ab ( keys %{ $ref->{ $minkey } } )
- {
- printf " $lv -> $ab, %d\n", $#$arr;
- maketree( $ref->{$minkey}{$ab}, [ sort keys %{$ref->{$minkey}{$ab}} ], $lv+1 );
- }
- }
-
- sub permute
- {
- my ( $a, $b, $n, $aref ) = @_;
- my $last = $#$a;
-
- if ( $#$b >= ($n-1) )
- {
- push @$aref, join("", @$b);
- return;
- }
-
- for my $idx ( 0 .. $last )
- {
- permute( [ @$a[0 .. $idx-1, $idx+1 .. $last] ], [ @$b, $a->[$idx] ], $n, $aref );
- }
- }
-
- __END__
- __C__
- void bullcow(char *stra, char *strb, char *AB)
- {
- int idx;
- char a = '0';
- char b = '0';
-
- for ( idx = 0; idx < 4; idx++ )
- {
- if ( stra[idx] == strb[idx] )
- a++;
- else
- if ( strchr(stra, strb[idx]) != 0 )
- {
- b++;
- }
- }
-
- AB[0] = a;
- AB[1] = b;
- }
复制代码
Times: 26408, average: 5.239683
1, 1
2, 7
3, 70
4, 647
5, 2400
6, 1811
7, 103
8, 1
欢迎光临 批处理之家 (http://bbs.bathome.net/) |
Powered by Discuz! 7.2 |