Filename | /homes/dcw/public_html/PSD/article13/v12.pl |
Statements | Executed 9427692 statements in 4.61s |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 4.60s | 4.60s | findall | main::
1 | 1 | 1 | 2.26ms | 3.39ms | BEGIN@33 | main::
1 | 1 | 1 | 1.29ms | 4.50ms | BEGIN@32 | main::
1 | 1 | 1 | 1.18ms | 1.18ms | BEGIN@31 | main::
1 | 1 | 1 | 242µs | 267µs | BEGIN@30 | main::
24 | 2 | 1 | 216µs | 216µs | CORE:say (opcode) | main::
280 | 4 | 1 | 60µs | 60µs | CORE:match (opcode) | main::
12 | 1 | 1 | 34µs | 34µs | suset | main::
1 | 1 | 1 | 21µs | 27µs | show_seqs | main::
1 | 1 | 1 | 8µs | 8µs | BEGIN@29 | main::
1 | 1 | 1 | 4µs | 4µs | VERSION (xsub) | UNIVERSAL::
3 | 3 | 1 | 900ns | 900ns | SvREADONLY (xsub) | Internals::
1 | 1 | 1 | 400ns | 400ns | method_changed_in (xsub) | mro::
0 | 0 | 0 | 0s | 0s | RUNTIME | main::
0 | 0 | 0 | 0s | 0s | show_sus | main::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | #!/usr/bin/perl | ||||
2 | # | ||||
3 | # Challenge 1: "Generate a longest sequence of the following "English Pokemon" | ||||
4 | # names where each name starts with the last letter of the previous name: | ||||
5 | # | ||||
6 | # audino bagon baltoy banette bidoof braviary bronzor carracosta | ||||
7 | # charmeleon cresselia croagunk darmanitan deino emboar emolga | ||||
8 | # exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur | ||||
9 | # jellicent jumpluff kangaskhan kricketune landorus ledyba loudred | ||||
10 | # lumineon lunatone machamp magnezone mamoswine nosepass petilil | ||||
11 | # pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz | ||||
12 | # registeel relicanth remoraid rufflet sableye scolipede scrafty | ||||
13 | # seaking sealeo silcoon simisear snivy snorlax spoink starly | ||||
14 | # tirtouga trapinch treecko tyrogue vigoroth vulpix wailord | ||||
15 | # wartortle whismur wingull yamask" | ||||
16 | # | ||||
17 | # My notes: Clearly defined, nice, potentially tricky, let's do it. | ||||
18 | # | ||||
19 | # optimization v12: turn SU pair into SLU triple, changing list to str ???s | ||||
20 | # refactoring v11: confine knowledge of SUs inside findall() 4.9s | ||||
21 | # optimization v10: more efficient reuse of arrays in new findall() 4.9s | ||||
22 | # refactoring v9: merge outer for loop and lengthen() into findall 5.7s | ||||
23 | # optimization v8: alter used set rather than rebuild it 5.8s | ||||
24 | # optimization v7: seqs->sus (don't rebuild used set each time) 6.0s | ||||
25 | # optimization v6: complete reimplementation, iterative version 7.9s | ||||
26 | # ... | ||||
27 | # optimization v1: baseline code before starting to optimize: 32.6s. | ||||
28 | |||||
29 | 2 | 24µs | 1 | 8µs | # spent 8µs within main::BEGIN@29 which was called:
# once (8µs+0s) by main::NULL at line 29 # spent 8µs making 1 call to main::BEGIN@29 |
30 | 2 | 155µs | 2 | 269µs | # spent 267µs (242+25) within main::BEGIN@30 which was called:
# once (242µs+25µs) by main::NULL at line 30 # spent 267µs making 1 call to main::BEGIN@30
# spent 2µs making 1 call to strict::import |
31 | 2 | 1.12ms | 2 | 1.19ms | # spent 1.18ms (1.18+6µs) within main::BEGIN@31 which was called:
# once (1.18ms+6µs) by main::NULL at line 31 # spent 1.18ms making 1 call to main::BEGIN@31
# spent 4µs making 1 call to warnings::import |
32 | 2 | 56µs | 2 | 4.61ms | # spent 4.50ms (1.29+3.21) within main::BEGIN@32 which was called:
# once (1.29ms+3.21ms) by main::NULL at line 32 # spent 4.50ms making 1 call to main::BEGIN@32
# spent 113µs making 1 call to Function::Parameters::import |
33 | 2 | 241µs | 2 | 3.40ms | # spent 3.39ms (2.26+1.12) within main::BEGIN@33 which was called:
# once (2.26ms+1.12ms) by main::NULL at line 33 # spent 3.39ms making 1 call to main::BEGIN@33
# spent 14µs making 1 call to Exporter::import |
34 | |||||
35 | 1 | 600ns | my $debug = @ARGV>0; | ||
36 | |||||
37 | 1 | 3µs | my @words = qw(audino bagon baltoy banette bidoof braviary bronzor carracosta | ||
38 | charmeleon cresselia croagunk darmanitan deino emboar emolga | ||||
39 | exeggcute gabite girafarig gulpin haxorus heatmor heatran ivysaur | ||||
40 | jellicent jumpluff kangaskhan kricketune landorus ledyba loudred | ||||
41 | lumineon lunatone machamp magnezone mamoswine nosepass petilil | ||||
42 | pidgeotto pikachu pinsir poliwrath poochyena porygon2 porygonz | ||||
43 | registeel relicanth remoraid rufflet sableye scolipede scrafty | ||||
44 | seaking sealeo silcoon simisear snivy snorlax spoink starly | ||||
45 | tirtouga trapinch treecko tyrogue vigoroth vulpix wailord | ||||
46 | wartortle whismur wingull yamask); | ||||
47 | #@words = qw(hello ollie excellent thanks shelter runaround set to); | ||||
48 | |||||
49 | 1 | 100ns | my %sw; # hash from letter L to list of word nos of words STARTING with L | ||
50 | |||||
51 | my @stopword;# list of stop word nos (word nos of words with no words going | ||||
52 | # "out" from them onto another word, ie. word numbers N where | ||||
53 | # no other word starts with the last letter of word N) | ||||
54 | |||||
55 | my %ew; # hash from letter L to list of word nos of words ENDING with L | ||||
56 | |||||
57 | my @inword; # array from word no N to array of wordnos of words going "in" | ||||
58 | # to word N, i.e. ending with the first letter of word N | ||||
59 | # if there are no such words, then [] | ||||
60 | |||||
61 | # build %sw | ||||
62 | 1 | 800ns | foreach my $wn (0..$#words) | ||
63 | { | ||||
64 | 70 | 7µs | my $word = $words[$wn]; | ||
65 | 70 | 49µs | 70 | 15µs | $word =~ /^(.)/; # spent 15µs making 70 calls to main::CORE:match, avg 210ns/call |
66 | 70 | 8µs | my $firstletter = $1; | ||
67 | 70 | 13µs | $sw{$firstletter} //= []; | ||
68 | 70 | 18µs | push @{$sw{$firstletter}}, $wn; | ||
69 | } | ||||
70 | #die Dumper \%sw; | ||||
71 | |||||
72 | # build %ew | ||||
73 | 1 | 400ns | foreach my $wn (0..$#words) | ||
74 | { | ||||
75 | 70 | 7µs | my $word = $words[$wn]; | ||
76 | 70 | 47µs | 70 | 16µs | $word =~ /(.)$/; # spent 16µs making 70 calls to main::CORE:match, avg 221ns/call |
77 | 70 | 7µs | my $lastletter = $1; | ||
78 | 70 | 11µs | $ew{$lastletter} //= []; | ||
79 | 70 | 17µs | push @{$ew{$lastletter}}, $wn; | ||
80 | } | ||||
81 | #die Dumper \%ew; | ||||
82 | |||||
83 | # build @stopword, using %sw | ||||
84 | 1 | 400ns | foreach my $wn (0..$#words) | ||
85 | { | ||||
86 | 70 | 6µs | my $word = $words[$wn]; | ||
87 | 70 | 45µs | 70 | 14µs | $word =~ /(.)$/; # spent 14µs making 70 calls to main::CORE:match, avg 203ns/call |
88 | 70 | 7µs | my $lastletter = $1; | ||
89 | 70 | 11µs | my $aref = $sw{$lastletter} // []; | ||
90 | 70 | 13µs | push @stopword, $wn if @$aref==0; | ||
91 | } | ||||
92 | #die Dumper [ map { $words[$_] } @stopword ]; | ||||
93 | |||||
94 | # build @inword, using %ew | ||||
95 | 1 | 300ns | foreach my $wn (0..$#words) | ||
96 | { | ||||
97 | 70 | 6µs | my $word = $words[$wn]; | ||
98 | 70 | 48µs | 70 | 16µs | $word =~ /^(.)/; # spent 16µs making 70 calls to main::CORE:match, avg 226ns/call |
99 | 70 | 7µs | my $firstletter = $1; | ||
100 | 70 | 12µs | my $aref = $ew{$firstletter} // []; | ||
101 | 70 | 14µs | $inword[$wn]= $aref; | ||
102 | } | ||||
103 | #die Dumper \@inword; | ||||
104 | |||||
105 | # No longer need %sw or %ew.. only @inword and @stopword | ||||
106 | |||||
107 | 1 | 294µs | 1 | 4.60s | my @seqs = findall(); # spent 4.60s making 1 call to main::findall |
108 | |||||
109 | 1 | 300ns | show_seqs( @seqs ) if $debug; | ||
110 | |||||
111 | # show just one of the longest sequences | ||||
112 | 1 | 4µs | 1 | 27µs | show_seqs( @seqs[0..0] ); # spent 27µs making 1 call to main::show_seqs |
113 | |||||
114 | 1 | 100ns | exit 0; | ||
115 | |||||
116 | |||||
117 | # | ||||
118 | # my @suset = suset( $wno ); | ||||
119 | # Form a SUset in which all word nos are unused, except $wno. | ||||
120 | # | ||||
121 | # spent 34µs within main::suset which was called 12 times, avg 3µs/call:
# 12 times (34µs+0s) by main::findall at line 172, avg 3µs/call | ||||
122 | 36 | 3µs | { | ||
123 | 12 | 9µs | my @suset = (0) x scalar(@words); | ||
124 | 12 | 1µs | $suset[$wno] = 1; | ||
125 | 12 | 24µs | return @suset; | ||
126 | 1 | 56µs | 1 | 4µs | } # spent 4µs making 1 call to Function::Parameters::_register_info |
127 | |||||
128 | |||||
129 | # | ||||
130 | # show_seqs( @seqs ); | ||||
131 | # Show the sequences (as words, not word nos) | ||||
132 | # | ||||
133 | # spent 27µs (21+6) within main::show_seqs which was called:
# once (21µs+6µs) by main::RUNTIME at line 112 | ||||
134 | 1 | 400ns | { | ||
135 | 1 | 2µs | foreach my $s (@seqs) | ||
136 | { | ||||
137 | 1 | 4µs | my @wn = split(/-/, $s); | ||
138 | 1 | 9µs | my $str = join( ',', map { $words[$_] } @wn ); | ||
139 | 1 | 10µs | 1 | 6µs | say $str; # spent 6µs making 1 call to main::CORE:say |
140 | } | ||||
141 | 1 | 56µs | 1 | 4µs | } # spent 4µs making 1 call to Function::Parameters::_register_info |
142 | |||||
143 | |||||
144 | # | ||||
145 | # show_sus( @sus ); | ||||
146 | # Show the sequences (as words, not word nos) contained in SUlist @sus | ||||
147 | # | ||||
148 | fun show_sus( @sus ) | ||||
149 | { | ||||
150 | foreach my $su (@sus) | ||||
151 | { | ||||
152 | my( $s, $u ) = @$su; | ||||
153 | my @wn = split(/-/, $s); | ||||
154 | my $str = join( ',', map { $words[$_] } @wn ); | ||||
155 | say $str; | ||||
156 | } | ||||
157 | 1 | 123µs | 1 | 4µs | } # spent 4µs making 1 call to Function::Parameters::_register_info |
158 | |||||
159 | |||||
160 | # | ||||
161 | # my @seqs = findall(); | ||||
162 | # Find all sequences, starting with sequences of length 1 (stop words), | ||||
163 | # then working back, i.e. prepending words onto the front of existing | ||||
164 | # sequences. Delivers the list of all maximal-length sequences. | ||||
165 | # | ||||
166 | # spent 4.60s (4.60+243µs) within main::findall which was called:
# once (4.60s+243µs) by main::RUNTIME at line 107 | ||||
167 | 1 | 200ns | { | ||
168 | 1 | 0s | my $sus; # all SUs for sequences of length N, each entry is | ||
169 | # a [ $wd, sequence, usedarrayref ] triple | ||||
170 | |||||
171 | # convert each stopword wordno into a SU triple, building a list | ||||
172 | 13 | 21µs | 12 | 34µs | $sus = [ map { [ $_, $_, [ suset($_) ] ] } @stopword ]; # spent 34µs making 12 calls to main::suset, avg 3µs/call |
173 | |||||
174 | 1 | 20µs | for( my $N=1 ; ; $N++) | ||
175 | { | ||||
176 | 23 | 13µs | my $nseq = @$sus; | ||
177 | 23 | 330µs | 23 | 209µs | say "Have $nseq sequences of length $N"; # spent 209µs making 23 calls to main::CORE:say, avg 9µs/call |
178 | #show_seqs( @$sus ); | ||||
179 | |||||
180 | # Take @$sus, a list of SUs of length N and ending in a | ||||
181 | # stopword, and try to lengthen them all backwards, ie. | ||||
182 | # prepend a word number to the start of each sequence. | ||||
183 | |||||
184 | # If this is possible, ie. if there is at least one extensible | ||||
185 | # SU, then change $sus to be the new, longer SUlist (all of | ||||
186 | # length N+1 now), and carry on looping. Else break out. | ||||
187 | |||||
188 | 23 | 15µs | my $new = []; # new list of SUs | ||
189 | |||||
190 | 23 | 34µs | foreach my $su (@$sus) # foreach current SU | ||
191 | { | ||||
192 | 1346584 | 333ms | my( $sw, $s, $used ) = @$su; | ||
193 | 1346584 | 93.0ms | my $list = $inword[$sw]; # list of word nos into sw | ||
194 | 1346584 | 656ms | foreach my $wno (grep { ! $used->[$_] } @$list) | ||
195 | { | ||||
196 | # make a single length N+1 sequence with wno in front of s | ||||
197 | 1346572 | 313ms | my $oneseq = "$wno-$s"; | ||
198 | |||||
199 | # alter the used array, marking $wno used. | ||||
200 | 1346572 | 90.9ms | $used->[$wno] = 1; | ||
201 | |||||
202 | # it's a new SU! | ||||
203 | 1346572 | 1.75s | push @$new, [ $wno, $oneseq, [ @$used ] ]; | ||
204 | #say "debug: ", Dumper(\@oneseq) if $N==22; | ||||
205 | |||||
206 | # alter used back | ||||
207 | 1346572 | 260ms | $used->[$wno] = 0; | ||
208 | } | ||||
209 | } | ||||
210 | 23 | 6µs | last unless @$new; | ||
211 | 22 | 1.11s | $sus = $new; | ||
212 | } | ||||
213 | |||||
214 | # now extract and return all the maximal length sequences | ||||
215 | 1 | 2.13ms | return map { $_->[1] } @$sus; | ||
216 | 1 | 47µs | 1 | 3µs | } # spent 3µs making 1 call to Function::Parameters::_register_info |
# spent 900ns within Internals::SvREADONLY which was called 3 times, avg 300ns/call:
# once (500ns+0s) by constant::BEGIN@24 at line 33 of constant.pm
# once (300ns+0s) by constant::import at line 164 of constant.pm
# once (100ns+0s) by constant::BEGIN@24 at line 34 of constant.pm | |||||
# spent 4µs within UNIVERSAL::VERSION which was called:
# once (4µs+0s) by Function::Parameters::BEGIN@7 at line 24 of Scalar/Util.pm | |||||
# spent 60µs within main::CORE:match which was called 280 times, avg 215ns/call:
# 70 times (16µs+0s) by main::RUNTIME at line 98, avg 226ns/call
# 70 times (16µs+0s) by main::RUNTIME at line 76, avg 221ns/call
# 70 times (15µs+0s) by main::RUNTIME at line 65, avg 210ns/call
# 70 times (14µs+0s) by main::RUNTIME at line 87, avg 203ns/call | |||||
sub main::CORE:say; # opcode | |||||
# spent 400ns within mro::method_changed_in which was called:
# once (400ns+0s) by constant::import at line 198 of constant.pm |