File: euler.p

package info (click to toggle)
libmath-mpfr-perl 4.38-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 1,652 kB
  • sloc: perl: 1,363; ansic: 517; makefile: 9
file content (146 lines) | stat: -rwxr-xr-x 7,661 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
#################################################################################
# This script requires Math::GMPq, Math::GMPz, and Math::MPFR.                  #
# It calculates the euler number e (2.7182818...), correct to $ARGV[0] bits.    #
# The calculated value is displayed unless $ARGV[1] is both provided and false. #
# With each iteration of the for{} loop (below) we get closer and closer to     #
# the actual value of e. Furthermore, with successive iterations of the for{}   #
# loop, the values alternate between "less than e" and "greater than e".        #
# Hence the actual (irrational) value of e is always between the values         #
# calculated by successive iterations of the for{} loop.                        #
#                                                                               #
# Of course, the simplest and most efficient way to get the value of e, to      #
# $ARGV[0] bits is simply to do:                                                #
#   Rmpfr_exp($rop, Math::MPFR->new(1), MPFR_RNDN)                              #
# where $rop is a $ARGV[0]-bit precision Math::MPFR object.                     #
# But doing it that way is a bit less interesting.                              #
#                                                                               #
# The same for{} loop can be also used to calculate the exact probabilities of  #
# "winning" at a simplistic solitaire-type card game. See demos/solitaire.p     #
# in the Math::GMPz source distro.                                              #
#################################################################################

use strict;
use warnings;
use Math::GMPz qw(:mpz);
use Math::GMPq qw(:mpq);
use Math::MPFR qw(:mpfr);

die "Usage: perl euler.pl bits [True|False]" unless @ARGV;
my $bits = shift;
Rmpfr_set_default_prec($bits);

my $display_value;
$display_value = defined($ARGV[0]) ? shift : 1;

#################################################################################
# For the sanity checks (below), set $e_big_p to e, correct to $bits+100 bits.  #
# Then convert $e_big_p exactly to a rational, $e_q (a Math::GMPq object).      #
#################################################################################
my $e_q = Math::GMPq->new();                                                    #
my $e_big_p = Rmpfr_init2($bits + 100);                                         #
Rmpfr_exp($e_big_p, Math::MPFR->new(1), MPFR_RNDN);                             #
Rmpfr_get_q($e_q, $e_big_p);                                                    #
#################################################################################

#################################################################
# Create some variables, and assign some initial values         #
#################################################################
my $first = Math::GMPz->new(1);                                 #
my $second = Math::GMPz->new(0);                                #
my $current_items = 2;                                          #
my $factorial = Math::GMPz->new(1);                             #
my $e_check = Math::GMPq->new();                                #
my ($e, $e_first_fr, $e_second_fr) = (Math::MPFR->new(),        #
                                      Math::MPFR->new(),        #
                                      Math::MPFR->new(),        #
                                     );                         #
my $chance; # becomes a MATH::GMPz object on assignment         #
my $e_first = Math::GMPq->new(3);                               #
my $e_second = Math::GMPq->new();                               #
my $count = 0;                                                  #
my $t = Math::GMPq->new();                                      #
my $save = Math::GMPq->new(4);                                  #
#################################################################

#################################################################
# Set $e to a $bits-bit approximation of the euler number,      #
# rounded to nearest.                                           #
# This should exactly equal the number that we calculate.       #
#################################################################
Rmpfr_exp($e, Math::MPFR->new(1), MPFR_RNDN);                   #
#################################################################

#########################
# Do the calculations	#
#########################

for(;;) {
  $count++;

  Rmpz_mul_ui($factorial, $factorial, $current_items);  #$factorial *= $current_items;
  $chance = ($current_items - 1) * ($first + $second);

#########################################################
# In this block we just perform some sanity checks.     #
# This block plays no part in the calculation of the    #
# actual value.                                         #
# Assign the calculated rational value to $e_check.     #
# Check that for every 2nd iteration, $e_check > $e_q   #
# and that for every other iteration, $e_check < $e_q   #
# Also check that, with each iteration, we get closer   #
# to the value of e (ie closer to the value of $e_q)    #
#########################################################
  Rmpq_set_num($e_check, $factorial);                   #
  Rmpq_set_den($e_check, $chance);                      #
  Rmpq_canonicalize($e_check); # gcd(num, den) == 1     #
  if($count % 2) {                                      #
    unless($e_check < $e_q) {die "$count: >="}          #
  }                                                     #
  else {                                                #
    unless($e_check > $e_q) {die "$count: <="}          #
  }                                                     #
  Rmpq_sub($t, $e_q, $e_check);                         #
  if(abs($t) < $save) {Rmpq_set($save, abs($t))}        #
  else {die "$count: No closer to e"}                   #
#########################################################

  Rmpq_set_num($e_second, $factorial);
  Rmpq_set_den($e_second, $chance);
  Rmpq_canonicalize($e_second); # gcd(num, den) == 1
  Rmpfr_set_q($e_first_fr, $e_first, MPFR_RNDN);
  Rmpfr_set_q($e_second_fr, $e_second, MPFR_RNDN);

#########################################################
# Exit the loop when $e_first_fr == $e_second_fr        #
# as this equivalence indicates that both variables     #
# contain the euler number, correct to $bits bits.      #
#########################################################
  last if Rmpfr_equal_p($e_first_fr, $e_second_fr);     #
#########################################################

  Rmpz_set($first, $second);
  Rmpz_set($second, $chance);
  Rmpq_set($e_first, $e_second);
  $current_items++;
}

if($e == $e_first_fr) {
  print "Iterations: $count ok\n";
  if($display_value) {print "$e_first_fr\n"}
}
else {print print "Iterations: $count not ok\n$e\n$e_first_fr\n"}

__END__

The sequence:
With 1st iteration, e = 2!  divided by 1       (ie divided by 1 * (1     + 0     ))
With 2nd iteration, e = 3!  divided by 2       (ie divided by 2 * (0     + 1     ))
With 3rd iteration, e = 4!  divided by 9       (ie divided by 3 * (1     + 2     ))
With 4th iteration, e = 5!  divided by 44      (ie divided by 4 * (2     + 9     ))
With 5th iteration, e = 6!  divided by 265     (ie divided by 5 * (9     + 44    ))
With 6th iteration, e = 7!  divided by 1854    (ie divided by 6 * (44    + 265   ))
With 7th iteration, e = 8!  divided by 14833   (ie divided by 7 * (265   + 1854  ))
With 8th iteration, e = 9!  divided by 133496  (ie divided by 8 * (1854  + 14833 ))
With 9th iteration, e = 10! divided by 1334961 (ie divided by 9 * (14833 + 133496))