Math-NumSeq

 view release on metacpan or  search on metacpan

lib/Math/NumSeq/ReRound.pm  view on Meta::CPAN

  ### ReRound pred(): $value

  my $extra_multiples = $self->{'extra_multiples'};

  if (_is_infinite($value)) {
    return undef;
  }
  if ($value <= 1 || $value != int($value)) {
    return ($value == 1);
  }

  # special case m=1 stepping down to an even number
  if (($value -= $extra_multiples) % 2) {
    return 0;
  }

  my $m = 2;
  while ($value > $m) {
    ### at: "value=$value  m=$m"

    if (($value -= $extra_multiples*$m) <= 0) {
      ### no, negative: $value
      return 0;
    }
    ### subtract to: "value=$value"

    ### rem: "modulus=".($m+1)." rem ".($value%($m+1))
    my $rem;
    if (($rem = ($value % ($m+1))) == $m) {
      ### no, remainder: "rem=$rem  modulus=".($m+1)
      return 0;
    }

    $value -= $rem;
    $m++;
  }

  ### final ...
  ### $value
  ### $m

  return ($value == $m);
}

# # v1.02 for leading underscore
use constant 1.02 _PI => 4*atan2(1,1); # similar to Math::Complex pi()

# value = m*1+m*2+m*3+...+m*i
# value = m*i*(i+1)/2
# i*i + i - 2v/m = 0
# i = (-1 + sqrt(1 + 4*2v/m)) / 2
#   = -1/2 + sqrt(1 + 4*2v/m)/2
#   = -1/2 + sqrt(4 + 2v/m)
# i -> sqrt(2v/m)
#
# as extra_multiples dominates the estimate changes from
#     extra_multiples==0         est = sqrt(pi*value)
# up to
#     extra_multiples==m large   est = sqrt(2*value/m)
#
# What formula that progressively morphs pi to 2/m ?
#
sub value_to_i_estimate {
  my ($self, $value) = @_;
  if ($value < 0) { return 0; }

  if ($self->{'extra_multiples'} == 0) {
    # use sqrt(pi)~=296/167 to preserve Math::BigInt
    return int( (sqrt(int($value)) * 296) / 167);
  } else {
    return int(sqrt(int (2 * $value / $self->{'extra_multiples'})));

    # return int(sqrt(int ($value * _PI/($self->{'extra_multiples'}+1)**2))
    #            + ($self->{'extra_multiples'} > 0
    #               ? -0.5 + sqrt(4 + 2*$value/$self->{'extra_multiples'})
    #               : 0));
  }



  # # large extra_multiples
  # return int(sqrt(int (2 * $value
  #                      / ($self->{'extra_multiples'}+1))));
  #
  # return int(sqrt(int (($value * 355)
  #                      / (113 * ($self->{'extra_multiples'}+1)))));
  #
  #
  # # extra_multiples==14
  # return int(sqrt(int (_PI * $value)) * 429/2048); #        429=13*11*3
  #
  # # extra_multiples==12
  # return int(sqrt(int (_PI * $value)) * 462/2048); # 231/1024) # 11*7*3=231
  #
  # # extra_multiples==10
  # return int(sqrt(int (_PI * $value))) * 126/512; # 63/256;  # 7*3*2=126
  #
  # # extra_multiples==8
  # return int(sqrt(int (_PI * $value))) * 35/128;             # 7*5=35
  #
  # # extra_multiples==6
  # return int(sqrt(int (_PI * $value))) * 10/32; # 5/16       # 5*2=10
  #
  # # extra_multiples==4
  # return int(sqrt(int (_PI * $value))) * 3/8;
  #
  # # extra_multiples==2
  # return int(sqrt(int (_PI * $value))) * 1/2;
  #
  # # extra_multiples==0
  # return int(sqrt(int (_PI * $value))) * 1/1;
  #
  #  2 1
  #  4 11
  #  6 1010
  #  8 100011
  # 10 1111110
  # 12 111001110
  # 14 110101101

  #

lib/Math/NumSeq/ReRound.pm  view on Meta::CPAN


=head2 Predicate

The rounding procedure can be reversed to test for a ReRound value.

    for i=2,3,4,etc
      value -= extra_multiples*i
      if value < 0 then not a ReRound

      remainder = value mod i
      if remainder==i-1 then not a ReRound

      value -= remainder    # round down to multiple of i

    stop when value <= i
    is a ReRound if value==i, and i is its index

For example to test 28, it's a multiple of 2, so ok for the final rounding
step.  It's predecessor in the rounding steps was a multiple of 3, so round
down to a multiple of 3 which is 27.  The predecessor of 27 was a multiple
of 4 so round down to 24.  But at that point there's a contradiction because
if 24 was the value then it's already a multiple of 3 and so wouldn't have
gone up to 27.  This case where a round-down gives a multiple of both i and
i-1 is identified by the remainder = value % i == i-1.  If value is already
a multiple of i-1 then subtracting an i-1 would leave it still so.

=head2 Value to i Estimate

For the default sequence as noted above the values grow roughly as

    value ~= i^2 / pi

so can be estimated as

    i ~= sqrt(value) * sqrt(pi)

There's no need for high accuracy in pi.  The current code uses an
approximation sqrt(pi)~=296/167 for faster calculation if the value is a
C<Math::BigInt> or C<Math::BigRat>.

    i ~= sqrt(value) * 296/167

C<extra_multiples =E<gt> m> adds a fixed amount i*m at each step, for a
total

    value_EM = m + 2*m + 3*m + 4*m + ... + i*m
             = m * i*(i+1)/2

As m becomes large this part of the value dominates, so

    value =~ m * i*(i+1)/2     for large m

    i =~ sqrt(value*2/m)

As m increases the factor sqrt(pi) progressively morphs towards sqrt(2/m).
For m even it might be sqrt(pi)/2, 3/8*sqrt(pi), 5/16*sqrt(pi),
35/128*sqrt(pi), etc, and for m odd it might be 2*sqrt(1/pi),
4/3*sqrt(1/pi), 16/15*sqrt(1/pi), 32/35*sqrt(1/pi), 256/315*sqrt(1/pi), etc.
What's the pattern?

The current code uses the "large m" formula for any mE<gt>0, which is no
more than roughly a factor 1.25 too big.

=head1 SEE ALSO

L<Math::NumSeq>,
L<Math::NumSeq::ReReplace>

=head1 HOME PAGE

L<http://user42.tuxfamily.org/math-numseq/index.html>

=head1 LICENSE

Copyright 2011, 2012, 2013, 2014, 2016, 2019, 2020 Kevin Ryde

Math-NumSeq is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the Free
Software Foundation; either version 3, or (at your option) any later
version.

Math-NumSeq is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
more details.

You should have received a copy of the GNU General Public License along with
Math-NumSeq.  If not, see <http://www.gnu.org/licenses/>.

=cut



( run in 0.674 second using v1.01-cache-2.11-cpan-e1769b4cff6 )