Calculate 100 factorial with all the digits - perl

I came across a problem of calculating 100 factorial.
Here is what I tried first in Perl to calculate 100! :
#!/usr/bin/perl
use strict;
use warnings;
use Math::BigInt;
my $n=<>;
chomp($n);
print fac($n);
sub fac
{
my ($m) = #_;
return 1 if($m <=1 );
return $m*fac($m-1);
}
But this is giving me 9.33262154439441e+157.
I need the answer with all of the digits.
What do I do?

Doubles (which most Perls use) only have ~16 digits of precision. You need to use another system to get the 158 digits of precision you need.
use bigint;
This will cause Perl to automatically treat all numbers in your script as Math::BigInt objects.
If you need finer control (to treat some numbers as BigInt and some numbers as floating point) then see Krishnachandra Sharma's solution and explicitly use the Math::BigInt constructor.
Math::BigInt has a builtin factorial function, by the way:
$ perl -MMath::BigInt -e 'print Math::BigInt->bfac(100)'
93326215443944152681699238856266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

Doubles (which most Perls use) only have ~16 digits of precision. You need to another system to get the 158 digits of precision you need. Try using Math::BigInt.
Here is the code.
#!/usr/bin/perl
use strict;
use warnings;
use Math::BigInt;
my $n=100;
Math::BigInt->new($n);
print fac($n);
sub fac
{
my ($m) = #_;
return 1 if($m <=1 );
return Math::BigInt->new($m*fac($m-1));
}
Produces 9332621544394415268169923e266700490715968264381621468592963895217599993229915608941463976156518286253697920827223758251185210916864000000000000000000000000

By definition, bigint works by overloading handling of integer and floating point literals, converting them to Math::BigInt objects. So with the help of simple for loop we can achieve the factorial of very big integers.
use bigint;
my $fact = 1;
for my $n (1..100) {
$fact *= $n;
}
print "Factorial: \n", $fact , "\n";
This produces the below output:
Factorial: 933262154439441526816992388562667004907159682643816214685929638952175
99993229915608941463976156518286253697920827223758251185210916864000000000000000
000000000
whereas the normal program like this would tremble with no meaningful output
use integer;
my $fact = 1;
for my $n (1..100) {
$fact *= $n;
}
print "Factorial: \n", $fact , "\n";
Output:
Factorial:
0

Related

Multiplying floats and ints in perl through an if statement

My goal is to utilize perl to multiply a float and an int, I have got this far and am still researching, many thanks to any help.
#!/usr/bin/perl
$float1 = 0.90
print "give me an integer";
$that_integer = <>;
if ($that_integer<=5000) {
print "$that_integer * $float1";
}
Welcome to Perl. A few tips:
Always include use strict; and use warnings; at the top of EVERY Perl script.
chomp your input from <STDIN> to remove the newline at the end.
You can't interpolate expressions. However, you can easily include them in a string easily using printf.
As demonstrated:
#!/usr/bin/perl
use strict;
use warnings;
my $float1 = 0.90;
print "give me an integer: ";
chomp( my $that_integer = <> );
if ( $that_integer <= 5000 ) {
printf "%f\n", $that_integer * $float1;
}
Arbitrary expressions can't be interpolated into double-quotes. Try:
print $that_integer * $float1, "\n";
The perlop documentation page includes all the gory details of parsing quoted constructs.

How to print in decimal form rather than exponential form in perl

I have written a program in perl.My requirement is to print the only the decimal numbers, not exponential numbers. Could you please let me know how to implement this ?
My program is calculating the expression 1/2 power(n) , where n can take up integer numbers from 1 to 200 only. And only 100 lines should be printed.
Example:
N=1, print 0.5
N=2, print 0.25
My program looks like:
#!/usr/bin/perl
use strict;
use warnings;
my $exp;
my $num;
my $count_lines = 0;
while($exp = <>)
{
next if($exp =~ m/^$/);
if($exp > 0 and $exp <=200 and $count_lines < 100)
{
$num = 1/(2 ** $exp);
print $num,"\n";
$count_lines++;
}
}
Input values:
If N = 100 , then out is getting printed in exponential form. But, the requirement is it should get printed in decimal form.
A simple print will pick the "best" format to display the value, so it chooses scientific format for very large or very small numberss to avoid printing a long string of zeroes.
But you can use printf (the format specifiers are documented here) to format a number however you want.
0.5200 is a very small number, so you need around 80 decimal places
use strict;
use warnings;
while (my $exp = <>) {
next unless $exp =~ /\S/;
my $count_lines = 0;
if ($exp > 0 and $exp <= 200 and $count_lines < 100) {
my $num = 1 / (2 ** $exp);
printf "%.80f\n", $num;
$count_lines++;
}
}
output for 100
0.00000000000000000000000000000078886090522101181000000000000000000000000000000000
and for 200
0.00000000000000000000000000000000000000000000000000000000000062230152778611417000
If you would like to remove insignificant trailing zeroes then you can use sprintf to put the formatted number into a variable and then use s/// to delete trailing zeroes, like this
my $number = sprintf "%.80f", $num;
$number =~ s/0+$//;
print $number, "\n";
which gives
0.00000000000000000000000000000078886090522101181
and
0.00000000000000000000000000000000000000000000000000000000000062230152778611417
Note that the true value of the calculation has many more digits than this, and the accuracy of the result is limited by the size of the floating point values that your computer uses.
0.5 ^ 200 is too small for a double floating point number, you need to use Math::BigFloat, that will overload basic math operations and output operators such as print for you, for example:
#!/usr/bin/perl
use strict;
use warnings;
use Math::BigFloat;
my $x = Math::BigFloat->new('0.5');
my $y = Math::BigFloat->new('200');
print $x ** $y, "\n";
Or use bignum:
#!/usr/bin/perl
use strict;
use warnings;
use bignum;
print 0.5 ** 200, "\n";
Output:
$ perl t.pl
0.00000000000000000000000000000000000000000000000000000000000062230152778611417071440640537801242405902521687211671331011166147896988340353834411839448231257136169569665895551224821247160434722900390625
You can use printf or sprintf to specify the format of what you want to print out.
#!/usr/bin/perl
use strict;
use warnings;
my $num = 0.000000123;
printf("%.50", $num)
If you need something like Perl 5 formats, take a look at Perl6::Form (note, this is a Perl 5 module, it just implements the proposed Perl 6 version of formats).

Perl's rand() function with localtime as seed

Please look at the following piece of code:
srand(localtime);
for (my $ik = 0; $ik < 3; $ik += 1)
{
print int(rand(10)),"\n";
sleep(1);
}
I invoke the above piece of code multiple times with sufficient time(5-10 sec) in between, still the output sequence is the same.
As I have set the seed to localtime every invocation must use a different seed and perhaps generate a different sequence of three numbers, because of the time gap. Why do I get the same sequence again and again.
Note: The code is NOT in a loop, it is in a Perl file which is being executed multiple times.
The documentation says that this seed fails if multiple instances run in the same 'second' leading to the same seed - here it is not the case.
EDIT:: The solution by #simbabque does help but the randomness expected is not gained. Look at my comment on the aforementioned solution below.
Try running this with use strict and use warnings. It will give you:
Argument "Thu Jun 21 13:04:41 2012" isn't numeric in srand at ...
And right there is your problem. localtime returns a string in scalar context. Try using time instead, which returns the unix timestamp as an integer. srand needs a numerical value to work.
If you add a Data::Dumper to it you'll see that the seed with your code is always 1.
no strict; no warnings;
use Data::Dumper;
print Dumper srand(localtime);
for (my $ik = 0; $ik < 3; $ik += 1)
{
print int(rand(10)),"\n";
sleep(1);
}
Says:
$VAR1 = 1;
0
2
6
What you need is:
use strict; use warnings;
srand(time);
for (my $ik = 0; $ik < 3; $ik += 1)
{
print int(rand(10)),"\n";
sleep(1);
}
Edit:
This still is not a very good idea if you want good randomness. The doc says:
In versions of Perl prior to 5.004 the default seed was just the
current time. This isn't a particularly good seed, so many old
programs supply their own seed value (often time ^ $$ or time ^ ($$ +
($$ << 15)) ), but that isn't necessary any more.
I suggest you just omit the call to srand at all unless you actually want reproducable results (i.e. for testing).
In general, there is no reason to expect better randomness by repeatedly seeding a PRNG.
You can use the following script to check what's going on with your original question:
#!/usr/bin/env perl
use strict; use warnings;
use 5.014;
for (1 .. 3) {
my $seq = newseq(3, 5);
printf "Seed = %s\n", $seq->{seed};
my $it = $seq->{generator};
while (defined(my $r = $it->())) {
print "$r\n";
}
sleep 5;
}
sub newseq {
my ($length, $limit) = #_;
$length //= 10;
$limit //= 10;
my $seed = srand(time);
return {
seed => $seed,
generator => sub {
return unless $length-- > 0;
return rand($limit);
},
};
}
However, if you do need statistically independent generators, you can use Math::Random::MT::Auto and create individual PRNG objects:
#!/usr/bin/env perl
use strict; use warnings;
use 5.014;
use strict;
use warnings;
use Math::Random::MT::Auto qw(:!auto);
my $prng1 = Math::Random::MT::Auto->new(SOURCE => '/dev/random');
my $prng2 = Math::Random::MT::Auto->new(SOURCE => 'random_org');
say $prng1->rand();
say $prng2->irand();

Parse scientific integer representation in perl

What is the most elegant way to parse an integer given in scientific representation, i.e. I have an input file with lines like
value=1.04738e+06
Sure I can match the all the components (leading digit, decimal positions, exponent) and calculate the result, but it seems to me there is a more straight-forward way.
% perl -e 'print "1.04738e+06" + 0'
1047380
You just need to coerce it to a number and Perl will DWIM.
FYI: looks_like_number() from Scalar::Util might come in handy.
#!/usr/bin/env perl
use strict;
use warnings;
use Scalar::Util qw( looks_like_number );
my $line = "value=1.04738e+06";
my ( $tag, $value ) = split /\s*=\s*/, $line, 2;
if( looks_like_number( $value ) ){
$value = 0 + $value;
}
print "$tag=$value\n";

Perl factorial subroutine not taking command line arguments

I'm trying to create a simple recursive factorial function in Perl that will take a number from the command line and then return it's factorial, e.g.
>./factorial.pl 3
>6
My subroutine doesn't seem to be taking the command line arguments. However if I take the exact same code without the sub wrapper it does take the command line arguments but obviously won't work as a subroutine. Below is the code:
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
sub fact() {
my $number = shift or return;
return 0 if $number < 0;
my $results = 1;
while ($number--) { $results *= $number--};
return $results;
}
shift in a sub defaults to shifting from #_ (the sub's arguments); outside a sub, it defaults to shifting from #ARGV (the command line parameters).
So either call fact(shift) or explicitly say shift(#ARGV) in fact.
And get rid of the () prototype: sub fact {...
Subroutine arguments are packed in #_. Pass #ARGV to the subroutine (and get rid of the empty prototype -- do not use prototypes unless you know exactly what they do):
#!/usr/bin/env perl
use warnings; use strict;
print fact(#ARGV), "\n";
sub fact {
my ($number) = #_;
# ...
}
Your script has no main thread when you wrap the code in the subroutine. YOu need to actually call your subroutine, example follows.
The () in the function signature are unnecessary as you are only passing in one argument as per Sinan
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
sub fact #() are unnecessary
{
my $number = shift or return;
return 0 if $number < 0;
my $results = 1;
while ($number--) { $results *= $number--};
return $results;
}
my $number = shift;
return fact($number);
The function logfac is a very accurate function of the log(factorial).
In addition.
Usually, when people ask for the factorial function, they really want it to compute a binomial coefficient. I've been using this trick since I was a student (I am only saying that so my current employer doesn't claim it as their intellectual property).
This code is numerically very accurate and very fast. Note the hardcoded limit of 20 for the "list" version... you can crank it up a little bit or tune it for performance.. I didn't bother.
For the "exact" formula, The main idea is that when computing a binomial coefficients there are a lot of cancellations. e.g. 10!/(7!*3) -> 10*9*8/(3*2*1) . To make it numerically more stable, compute this last remnant as 10/3 * 9/2 * 8/1
When the counts are too big, I used an approximation that is better converging than the stirling series. I checked up to 1000 or so.
Just call the choose m in N by 'choose(N,m)'
#Approximation from Srinivasa Ramanujan (Ramanujan 1988)
# Much better than stirling.. when tested in R.
# copyright(c) Hugues Sicotte, 2012
# Permission to use without restriction, with no implied suitability for any purposes
# use at your own risk.
use constant PI => 4 * atan2(1, 1);
sub logfac {
my $logfac=0;
my $n=shift(#_);
if($n&lt20) {
my $fac=1;
for(my $i=1;$i&lt=$n;$i++) {$fac=$fac*$i;}
$logfac=log($fac);
} else {
$logfac=n*log(n)-n+log(n*(1+4*n*(1+2*n)))/6 +log(PI)/2;
}
return $logfac;
}
sub choose {
my $total=shift #_; # N
my $choose=shift #_; # m
if($choose==0) { # N!/(0!N!) == 1, even if N==0
return 1;
} elsif($total==$choose) {#N!/N!*0!
return 1;
}
if($choose&lt20 && $total&lt20) {
my $min=$choose <$total-$choose ? $choose : $total-$choose;
my $res=$total/$min;
while($min&gt1) {
$total--;
$min--;
$res = $res * $total/$min;
}
return $res;
} else {
return exp(logfac($total)-logfac($choose)-logfac($total-$choose));
}
}
I've got it fixed now so that it works and it handles the case where no argument is presented.
#!/usr/bin/perl
use strict;
use warnings;
use diagnostics;
sub fact {
my $number = shift or return "Need argument!";
return 0 if $number < 0;
my $results = 1;
while ($number--) { $results *= $number--};
return $results;
}
my $number= shift;
print fact($number) . "\n";