File Coverage

blib/lib/Algorithm/Evolve/Util.pm
Criterion Covered Total %
statement 55 89 61.8
branch 7 26 26.9
condition 8 22 36.3
subroutine 10 12 83.3
pod 8 8 100.0
total 88 157 56.0


line stmt bran cond sub pod time code
1             package Algorithm::Evolve::Util;
2              
3 1     1   724 use strict;
  1         2  
  1         39  
4 1     1   4 use base 'Exporter';
  1         2  
  1         126  
5 1     1   6 use List::Util qw/shuffle/;
  1         2  
  1         75  
6 1     1   6 use Carp qw/croak carp/;
  1         2  
  1         1412  
7              
8             our $VERSION = '0.03';
9             our $UNICODE_STRINGS = 0;
10              
11             our %EXPORT_TAGS = (
12             str => [qw/str_crossover str_mutate str_agreement str_random/],
13             arr => [qw/arr_crossover arr_mutate arr_agreement arr_random/],
14             );
15             our @EXPORT_OK = map { @$_ } values %EXPORT_TAGS;
16              
17             sub str_crossover {
18 0     0 1 0 my ($s1, $s2, $n_point) = @_;
19            
20 0   0     0 $n_point ||= 2;
21 0         0 my $len = length($s1);
22              
23 0 0       0 croak "Can't do ${n_point}-point crossover on length $len string"
24             if $n_point >= $len;
25              
26             ## this allows for duplication of indices. maybe a fixme
27              
28 0         0 my @points = sort { $a <=> $b } map { int(rand $len) } 1 .. $n_point;
  0         0  
  0         0  
29 0 0       0 push @points, $len if $n_point % 2;
30              
31 0         0 for (0 .. @points/2 - 1) {
32 0         0 my ($x, $y) = @points[2*$_, 2*$_+1];
33 0         0 (substr($s1, $x, $y-$x+1), substr($s2, $x, $y-$x+1)) =
34             (substr($s2, $x, $y-$x+1), substr($s1, $x, $y-$x+1));
35             }
36            
37 0         0 return ($s1, $s2);
38             }
39              
40             sub str_agreement {
41 100     100 1 617 my ($s1, $s2) = @_;
42              
43             ## substr is safe for unicode; xor'ing characters is not. But
44             ## xor is about 30 times faster on longish strings...
45              
46 100 50       235 if ($UNICODE_STRINGS) {
47 0         0 my $tally = 0;
48 0         0 for (0 .. length($s1)-1) {
49 0 0       0 $tally++ if substr($s1, $_, 1) eq substr($s2, $_, 1);
50             }
51 0         0 return $tally;
52             }
53              
54 100         266 my $xor = $s1 ^ $s2;
55 100         289 return $xor =~ tr/\x0/\x0/;
56             }
57              
58             sub str_mutate {
59 100     100 1 2109 my ($string, $n, $alphabet) = @_;
60            
61 100   50     275 $n ||= 1;
62 100   50     189 $alphabet ||= [0,1];
63              
64 100 50 33     593 croak "Invalid alphabet"
65             unless ref $alphabet eq 'ARRAY' and @$alphabet > 1;
66              
67 100         132 my $len = length($string);
68 0 0       0 my @mutate_indices = $n < 1
69 100 50       1812 ? map { rand() < $n ? $_ : () } 0 .. $len-1
70             : (shuffle 0 .. $len-1)[ 0 .. int($n)-1 ];
71            
72 100         450 for my $idx (@mutate_indices) {
73 2540         4557 my $char = substr($string, $idx, 1);
74 2540         4554 my @different = grep { $char ne $_ } @$alphabet;
  66040         135272  
75 2540         14218 substr($string, $idx, 1) = $different[ int(rand @different) ];
76             }
77            
78 100         472 return $string;
79             }
80              
81             sub str_random {
82 100     100 1 1855 my ($length, $alphabet) = @_;
83            
84 100   50     216 $alphabet ||= [0,1];
85              
86 100         612 return join '', map { $alphabet->[ rand @$alphabet ] } 1 .. $length;
  10000         20143  
87             }
88              
89             ##########################################
90              
91             sub arr_crossover {
92 0     0 1 0 my ($a1_ref, $a2_ref, $n_point) = @_;
93            
94 0   0     0 $n_point ||= 2;
95 0         0 my @a1 = @$a1_ref;
96 0         0 my @a2 = @$a2_ref;
97 0         0 my $len = @a1;
98              
99 0 0       0 croak "Can't do ${n_point}-point crossover on length $len array"
100             if $n_point >= $len;
101              
102             ## this allows for duplication of indices. maybe a fixme
103              
104 0         0 my @points = sort { $a <=> $b } map { int(rand $len) } 1 .. $n_point;
  0         0  
  0         0  
105 0 0       0 push @points, $len-1 if $n_point % 2;
106              
107 0         0 for (0 .. @points/2 - 1) {
108 0         0 my ($x, $y) = @points[2*$_, 2*$_+1];
109 0         0 my @tmp = @a1[$x .. $y];
110 0         0 @a1[$x .. $y] = @a2[$x .. $y];
111 0         0 @a2[$x .. $y] = @tmp;
112             }
113            
114 0         0 return (\@a1, \@a2);
115             }
116              
117             sub arr_agreement {
118 100     100 1 534 my ($a1, $a2) = @_;
119              
120 100         120 my $tally = 0;
121 100         93 for (0 .. $#{$a1}) {
  100         214  
122 10000 100       17289 $tally++ if $a1->[$_] eq $a2->[$_];
123             }
124              
125 100         240 return $tally;
126             }
127              
128             sub arr_mutate {
129 100     100 1 1706 my ($arr_ref, $n, $alphabet) = @_;
130            
131 100   50     189 $n ||= 1;
132 100   50     173 $alphabet ||= [0,1];
133 100         1427 my @arr = @$arr_ref;
134            
135 100 50 33     524 croak "Invalid alphabet"
136             unless ref $alphabet eq 'ARRAY' and @$alphabet > 1;
137              
138 100         112 my $len = scalar @arr;
139 0 0       0 my @mutate_indices = $n < 1
140 100 50       1185 ? map { rand() < $n ? $_ : () } 0 .. $len-1
141             : (shuffle 0 .. $len-1)[ 0 .. int($n)-1 ];
142            
143 100         461 for my $idx (@mutate_indices) {
144 2408         2714 my $char = $arr[$idx];
145 2408         2788 my @different = grep { $char ne $_ } @$alphabet;
  62608         84779  
146 2408         8339 $arr[$idx] = $different[ int(rand @different) ];
147             }
148            
149 100         341 return \@arr;
150             }
151              
152             sub arr_random {
153 100     100 1 2208 my ($length, $alphabet) = @_;
154            
155 100   50     234 $alphabet ||= [0,1];
156              
157 100         445 return [ map { $alphabet->[ rand @$alphabet ] } 1 .. $length ];
  10000         14625  
158             }
159              
160              
161             ##########################################
162             ##########################################
163             ##########################################
164             1;
165             __END__