File Coverage

blib/lib/Math/NumSeq/CollatzSteps.pm
Criterion Covered Total %
statement 106 120 88.3
branch 36 50 72.0
condition 2 7 28.5
subroutine 17 17 100.0
pod 6 7 85.7
total 167 201 83.0


line stmt bran cond sub pod time code
1             # Copyright 2011, 2012, 2013, 2014 Kevin Ryde
2              
3             # This file is part of Math-NumSeq.
4             #
5             # Math-NumSeq is free software; you can redistribute it and/or modify
6             # it under the terms of the GNU General Public License as published by the
7             # Free Software Foundation; either version 3, or (at your option) any later
8             # version.
9             #
10             # Math-NumSeq is distributed in the hope that it will be useful, but
11             # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12             # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13             # for more details.
14             #
15             # You should have received a copy of the GNU General Public License along
16             # with Math-NumSeq. If not, see .
17              
18              
19             # Maybe "before_drop", "after_drop"
20             # Maybe "before_peak", "after_peak"
21             # Maybe +1 count steps from 1.
22             #
23              
24             # on_values=>'even' is 2*i gives +1 for "both" and "down", no change to "up"
25             # on_values=>'odd' is 2*i+1
26             # starts 3*(2i+1)+1 = 6i+4 -> 3i+2
27             #
28             # i=0 for odd same as Odd->ith() ?
29              
30             # 2^E(N) = 3^O(N) * N * Res(N)
31             # log(2^E(N)) = log(3^O(N) * N * Res(N))
32             # log(2^E(N)) = log(3^O(N)) + log(N) + log(Res(N))
33             # E(N)*log(2) = O(N)*log(3) + log(N) + log(Res(N))
34             # log(Res(N)) = O(N)*log(3) - E(N)*log(2) + log(N)
35              
36             # "Glide" how many steps to get a value < N.
37             #
38              
39             package Math::NumSeq::CollatzSteps;
40 2     2   14950 use 5.004;
  2         11  
  2         109  
41 2     2   15 use strict;
  2         8  
  2         88  
42              
43 2     2   12 use vars '$VERSION', '@ISA';
  2         5  
  2         186  
44             $VERSION = 71;
45              
46 2     2   639 use Math::NumSeq;
  2         6  
  2         61  
47 2     2   576 use Math::NumSeq::Base::IterateIth;
  2         7  
  2         476  
48             @ISA = ('Math::NumSeq::Base::IterateIth',
49             'Math::NumSeq');
50             *_is_infinite = \&Math::NumSeq::_is_infinite;
51              
52             # uncomment this to run the ### lines
53             # use Smart::Comments;
54              
55             # use constant name => Math::NumSeq::__('Collatz Steps');
56             sub description {
57 6     6 1 32 my ($self) = @_;
58 6 100       23 if (ref $self) {
59 3 100       16 if ($self->{'step_type'} eq 'up') {
60 1         6 return Math::NumSeq::__('Number of up steps to reach 1 in the Collatz "3n+1" problem.');
61             }
62 2 100       13 if ($self->{'step_type'} eq 'down') {
63 1         6 return Math::NumSeq::__('Number of down steps to reach 1 in the Collatz "3n+1" problem.');
64             }
65             }
66 4         17 return Math::NumSeq::__('Number of steps to reach 1 in the Collatz "3n+1" problem.');
67             }
68              
69             sub default_i_start {
70 61     61 0 82 my ($self) = @_;
71 61 50       232 return ($self->{'on_values'} eq 'odd' ? 0 : 1);
72             }
73             sub values_min {
74 21     21 1 47 my ($self) = @_;
75 21         51 return ($self->ith($self->i_start));
76             }
77 2     2   12 use constant characteristic_count => 1;
  2         7  
  2         137  
78 2     2   11 use constant characteristic_smaller => 1;
  2         4  
  2         96  
79 2     2   13 use constant characteristic_increasing => 0;
  2         5  
  2         419  
80              
81 2         14 use constant parameter_info_array =>
82             [
83             # { name => 'end_type',
84             # share_key => 'end_type_1drop',
85             # display => Math::NumSeq::__('End Type'),
86             # type => 'enum',
87             # default => 'one',
88             # choices => ['one','drop','to_peak','from_peak','pow2'],
89             # choices_display => [Math::NumSeq::__('One'),
90             # Math::NumSeq::__('Drop'),
91             # Math::NumSeq::__('To Peak'),
92             # Math::NumSeq::__('From Peak'),
93             # Math::NumSeq::__('Pow2'),
94             # ],
95             # # description => Math::NumSeq::__(''),
96             # },
97              
98             { name => 'step_type',
99             share_key => 'step_type_bothupdown',
100             display => Math::NumSeq::__('Step Type'),
101             type => 'enum',
102             default => 'both',
103             choices => ['both','up','down',
104             # 'diff', 'both+1',
105             ],
106             choices_display => [Math::NumSeq::__('Both'),
107             Math::NumSeq::__('Up'),
108             Math::NumSeq::__('Down'),
109             ],
110             description => Math::NumSeq::__('Which steps to count, the 3*n+1 ups, the n/2 downs, or both.'),
111             },
112              
113             # secret extra 'odd1' for 2*i-1 starting i=1 to help offset of A075680
114             { name => 'on_values',
115             share_key => 'on_values_aoe',
116             display => Math::NumSeq::__('On Values'),
117             type => 'enum',
118             default => 'all',
119             choices => ['all','odd','even'],
120             choices_display => [Math::NumSeq::__('All'),
121             Math::NumSeq::__('Odd'),
122             Math::NumSeq::__('Even')],
123             description => Math::NumSeq::__('The values to act on, either all integers or just odd or even.'),
124             },
125 2     2   13 ];
  2         7  
126              
127             #------------------------------------------------------------------------------
128             # cf
129             # A139399 steps to reach cycle 4-2-1, so is steps-2 except at 4,2,1
130             # A112695 similar
131             # A064433 steps to reach 2, which is -1 except at n=1
132             #
133             # A066861 steps x/2 and (3x+1)/2
134             # A058633 steps of n cumulative
135             # A070975 steps for n prime
136             # A075677 one reduction 3x+1/2^r on the odd numbers, r as big as possible
137             # A014682 one step 3x+1 or x/2 on the integers
138             # A006884 new record for highest point reached in iteration
139             # A006885 that record high position
140             #
141             # A102419 "dropping time" steps to go below initial
142             # A217934 new highs of dropping time steps to go below initial
143             # A060412 n where those highs occur
144             #
145             # A074473 "dropping time" + 1, counting initial as step 1
146             # A075476 "dropping time" of numbers 64n+7
147             # A075477 "dropping time" of numbers 64n+15
148             # A075478 "dropping time" of numbers 64n+27
149             # A075480 "dropping time" of numbers 64n+39
150             # A075481 "dropping time" of numbers 64n+47
151             # A075482 "dropping time" of numbers 64n+59
152             # A075483 "dropping time" of numbers 64n+63
153             # A060445 "dropping time" of odd numbers 2n+1
154             # A060412 n start for new record "dropping time"
155             # A217934 new record "dropping time"
156             #
157             # A005184 multiple k*n occurs in Collatz trajectory
158             # A055509 number of odd primes in trajectory
159             # A055510 largest odd prime in trajectory
160             # A060322 how many integers have steps=n
161             # A070917 steps is a divisor of n
162             #
163             # A088975 collatz tree breadth-first
164             # A088976 collatz tree breadth-first
165             # A127824 breadth first sorted within rows
166             #
167             my %oeis_anum =
168             ('one,all,both' => 'A006577',
169             'one,all,up' => 'A006667', # triplings
170             'one,even,up' => 'A006667', # triplings unchanged by even 2*i
171             'one,all,down' => 'A006666', # halvings
172             # OEIS-Catalogue: A006577
173             # OEIS-Catalogue: A006667 step_type=up
174             # OEIS-Other: A006667 step_type=up on_values=even
175             # OEIS-Catalogue: A006666 step_type=down
176              
177             'one,even,both' => 'A008908', # +1 from "all"
178             'one,all,both+1' => 'A008908', # +1 from "all"
179             # OEIS-Catalogue: A008908 on_values=even
180             # OEIS-Other: A008908 step_type=both+1
181              
182             'one,odd,both+1' => 'A064685',
183             'one,odd1,down' => 'A166549',
184             # OEIS-Catalogue: A064685 on_values=odd step_type=both+1
185             # OEIS-Catalogue: A166549 on_values=odd1 step_type=down
186              
187             # A075680 up steps for odd 2n-1 0,2,1,5,6,4,etc starting n=1 2n-1=1
188             # it being defined as steps of (3x+1)/2^r for maximum r
189             'one,odd1,up' => 'A075680',
190             # OEIS-Catalogue: A075680 on_values=odd1 step_type=up
191              
192             #--------------
193             # drop
194              
195             'drop,all,both' => 'A102419', # "dropping time"
196             'drop,all,both+1' => 'A074473', # "dropping time"
197             'drop,all,down' => 'A126241',
198             # OEIS-Catalogue: A102419 end_type=drop
199             # OEIS-Catalogue: A074473 end_type=drop step_type=both+1
200             # OEIS-Catalogue: A126241 end_type=drop step_type=down
201              
202             'drop,odd,both' => 'A060445', # odd numbers 2n+1 so n=0 for odd=1
203             'drop,odd,up' => 'A122458',
204             # OEIS-Catalogue: A060445 end_type=drop on_values=odd
205             # OEIS-Catalogue: A122458 end_type=drop on_values=odd step_type=up
206              
207             # Not quite A087225 is "position" of peak reckoning start as position=1
208             # as opposed to value=0 many "steps" here.
209             'to_peak,all,both+1' => 'A087225',
210             # OEIS-Catalogue: A087225 end_type=to_peak step_type=both+1
211              
212             #--------------
213             # pow2
214              
215             'pow2,all,both' => 'A208981',
216             # OEIS-Catalogue: A208981 end_type=pow2
217             );
218             sub oeis_anum {
219 3     3 1 17 my ($self) = @_;
220 3         19 return $oeis_anum{"$self->{'end_type'},$self->{'on_values'},$self->{'step_type'}"};
221             }
222              
223             #------------------------------------------------------------------------------
224              
225             sub new {
226 5     5 1 2692 my $self = shift->SUPER::new(@_);
227 5   50     39 $self->{'end_type'} ||= 'one';
228 5         21 return $self;
229             }
230              
231 2         9 use constant 1.02 _UV_LIMIT => do { # version 1.02 for leading underscore
232 2         4 my $limit = ~0;
233 2         4 my $bits = 0;
234 2         10 while ($limit) {
235 128         142 $bits++;
236 128         327 $limit >>= 1;
237             }
238 2         11 $bits -= 2;
239 2         1770 (1 << $bits) - 1
240 2     2   14 };
  2         53  
241              
242             sub ith {
243 185     185 1 667 my ($self, $i) = @_;
244             ### CollatzSteps ith(): $i
245             ### end_type: $self->{'end_type'}
246              
247 185 50       696 if ($self->{'on_values'} eq 'odd') {
    50          
    50          
248 0         0 $i = 2*$i+1; # i=0 is odd number 1
249             } elsif ($self->{'on_values'} eq 'odd1') {
250 0         0 $i = 2*$i-1; # i=1 is odd number 1
251             } elsif ($self->{'on_values'} eq 'even') {
252 0         0 $i *= 2;
253             }
254 185         194 my $orig_i = $i;
255              
256 185         201 my $ups = 0;
257 185         181 my $downs_sans_trailing = 0;
258 185         180 my $downs = 0;
259 185         198 my $peak_ups = 0;
260 185         179 my $peak_downs = 0;
261 185 100       319 if ($i >= 2) {
262 137 50       289 if (_is_infinite($i)) {
263 0         0 return $i;
264             }
265              
266 137         168 my $peak_i = $i;
267 137 50       299 my $end = ($self->{'end_type'} eq 'drop' ? $i : 1);
268              
269 137         134 OUTER: for (;;) {
270 458         501 $downs_sans_trailing = $downs;
271 458         872 until ($i & 1) {
272 838         793 $i >>= 1;
273 838         1000 $downs++;
274 838 100       1898 last OUTER if $i <= $end;
275             }
276             ### odd: $i
277              
278 322 100       534 if ($i > _UV_LIMIT) {
279 1         5 $i = Math::NumSeq::_to_bigint($i);
280              
281             ### using bigint: "$i"
282 1         81 for (;;) {
283             ### odd: "$i"
284 309         27443 $i->bmul(3);
285 309         32900 $i->binc();
286 309         9634 $ups++;
287              
288 309 100       4076 if ($i > $peak_i) {
289 1         312 $peak_ups = $ups;
290 1         2 $peak_downs = $downs;
291 1         3 $peak_i = $i;
292             }
293              
294 309         10441 $downs_sans_trailing = $downs;
295 309         707 until ($i->is_odd) {
296 554         24992 $i->brsft(1);
297 554         73936 $downs++;
298 554 100       1441 last OUTER if $i <= $end;
299             }
300             }
301             }
302              
303 321         331 $i = 3*$i + 1;
304 321         283 $ups++;
305              
306 321 100       601 if ($i > $peak_i) {
307 170         152 $peak_ups = $ups;
308 170         160 $peak_downs = $downs;
309 170         188 $peak_i = $i;
310             }
311             }
312             }
313              
314             ### $ups
315             ### $downs
316             ### $downs_sans_trailing
317              
318 185 50       652 if ($self->{'end_type'} eq 'to_peak') {
    50          
    50          
319 0         0 $ups = $peak_ups;
320 0         0 $downs = $peak_downs;
321             } elsif ($self->{'end_type'} eq 'from_peak') {
322 0         0 $ups -= $peak_ups;
323 0         0 $downs -= $peak_downs;
324             } elsif ($self->{'end_type'} eq 'pow2') {
325 0         0 $downs = $downs_sans_trailing;
326             }
327              
328 185         264 my $step_type = $self->{'step_type'};
329 185 100       308 if ($step_type eq 'up') {
330 61         191 return $ups;
331             }
332 124 100       216 if ($step_type eq 'down') {
333 61         192 return $downs;
334             }
335 63 50       104 if ($step_type eq 'diff') {
336 0         0 return $downs - $ups;
337             }
338 63 50       97 if ($step_type eq 'completeness') {
339             # maximum C(N) < ln(2)/ln(3) = 0.63
340 0         0 return $ups / $downs;
341             }
342 63 50       112 if ($step_type eq 'gamma') {
343 0   0     0 return $downs / (log($orig_i) || 1);
344             }
345 63 50       98 if ($step_type eq 'residue') {
346             # log(Res(N)) = Odd(N)*log(3) - Even(N)*log(2) + log(N)
347 0         0 return $ups*log(3) - $downs*log(2) + log($orig_i);
348             }
349 63 50       109 if ($step_type eq 'both+1') {
350 0         0 return $ups + $downs + 1;
351             }
352             # $step_type eq 'both'
353 63         228 return $ups + $downs;
354             }
355              
356             sub pred {
357 18     18 1 102 my ($self, $value) = @_;
358 18   33     52 return ($value == int($value)
359             && $value >= $self->values_min);
360             }
361              
362             1;
363             __END__