Perl's rand() function with localtime as seed - perl

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();

Related

Perl: How do I get the value of a variable in a loop if it is in a1 a2 a3 format

Basically i am trying to access the predefined variable in a perl program.
the variables are in the form a1 a2 a3 format.
I want to access them in a loop. In the loop I will increment postfix scalar value
#!/usr/bin/perl
use strict;
use warnings;
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
my $var = ${a$i};
print $var;
}
WHAT I EXPECT:
When I print $var in loop, I need the values 10,12 .. defined earlier.
WHAT I CAN NOT DO:
I am aware that such situation can be handled with a hash. But I do not have any control over the variable naming, hence I can not use hash or change variable format.
I appreciate your help!
If you want to avoid turning off strict, you could use eval:
#!/usr/bin/perl
use strict;
use warnings;
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
print eval "\$a$i";
}
Update: using more readable version suggested by Сухой27 in the comments
Use an array instead of multiple similarly named variables, as this is their main use case,
use strict;
use warnings;
my #a = (10,12,14,16);
for my $i (0 .. $#a) {
my $var = $a[$i];
print $var, "\n";
}
alternatively you can use array of scalar references
use strict;
use warnings;
my ($a0,$a1,$a2,$a3) = (10,12,14,16);
my #a = \($a0,$a1,$a2,$a3);
for my $i (0 .. $#a) {
my $var = ${ $a[$i] };
print $var, "\n";
}
What you are doing here is called a symbolic reference, and it is an EXTREMELY bad idea.
Please take a look through this article: http://perl.plover.com/varvarname.html
But the long and short of it is - using a variable as a variable name - which you're doing - is dangerous and unnecessary. It causes all sorts of potential problems in your code, including bugs in completely unrelated pieces of code. This is why strict won't let you do it.
More importantly - it's completely unnecessary, because perl has the hash as a native data type.
Instead of your code, consider instead:
my %h;
( $h{0}, $h{1}, $h{2}, $h{3} ) = ( 10, 12, 14, 16 );
foreach my $key ( sort keys %h ) {
print "$key = $h{$key}\n";
}
Now, it's added a few characters to your code, but by doing so - you've created a lexically scoped namespace called %h. (I'd suggest calling it something more meaningful, personally - and definitely avoid $a and $b because they have special meanings).
But there is no danger of this namespace trampling over other parts of your code, and for bonus points - you no longer need your 'for' loop, you can simply iterate on keys instead. (So you always have the right number).
(Or as another user has suggested - just use an array)
You can get round strict's restriction on dynamic variable names like this.
#!/usr/bin/perl
use strict;
use warnings;
{
no strict 'refs';
my ($a0,$a1,$a2,$a3)= (10,12,14,16);
for(my $i=0; $i<=3; $i++) {
my $var = ${a$i};
print $var;
}
}
I don't think this is a good idea, though!

Vector binning algorithm in Perl

I have the following vector:
19.01
20.2572347267
16.4893617021
19.0981432361
36.3636363636
20.41
It's actually much longer, but that doesn't matter. I need an algorithm to bin these values into a hash. The hash keys must be floating point values that start from the minimum value + 1 (in this case 17.48...) and increase by 1. The values of the hash must be the number of elements that fall into the corresponding bin, i.e. the end result should be:
$hash{17.49}=1
$hash{18.49}=0
$hash{19.49}=2
$hash{20.49}=2
$hash{21.49}=0
$hash{22.49}=0
.
.
.
$hash{35.49}=0
$hash{37.49}=1
Please help guys.
This seems to work:
#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper;
use List::Util qw{ min };
my #vector = qw( 19.01
20.2572347267
16.4893617021
19.0981432361
36.3636363636
20.41
);
my %hash;
my $min = min(#vector);
for my $n (#vector) {
my $diff = $n - $min;
++$hash{ 1 + $min + int $diff };
}
print Dumper \%hash;
If you need the zeroes as well, just add the follwoing before the loop:
my $max = max(#vector);
my $i = $min;
while ($i <= $max) {
$hash{$i++} = 0;
}
(And include max in the use clause, too.)
Came up with a sweet solution, hopefully somebody else will also find it helpful.
use POSIX;
sub frac { $_[0]-floor($_[0]) } #saw this little function posted somewhere, quddos to the guy who came up with it
for (my $x = ${min_value} + 1; $x <= ${max_value} + 1; $x += 1) # if you don't need the zeroes, remove this loop
{
$bins{$x} = 0;
}
foreach my $n (#array)
{
$bins{floor($n+1)+frac($min_value)}++;
}
floor() or ceil() (and use POSIX;) should be used instead of int(), because int() can produce erenous results - 278 may be internally stored as 277.99999999997899999 (for example), so int(278) turns out equal to 277, which may mess up your computation. Read this somewhere, but can't find the link...

Calculate 100 factorial with all the digits

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

Statistics in Perl Script

I have the following question:
I want to create a perl script that reads from a text file (file with several columns of numbers) and calculate some statistics (mean, median, sd, variance). I already built one script, but as I am not in love yet with perl, I can't fix the problems of syntax on it...
Here is my perl script..
#!/usr/bin/perl -w
use strict;
open(FILEHANDLE, data.txt);
while (<FILEHANDLE>) {
shift #ARGV;
my #array = split(\t,$_);
}
close(FILEHANDLE);
###### mean, sum and size
$N = $sum = 0;
$array[$x-1];
$N++;
$sum += $array[$x-1];
###### minimum and the maximum
($min = 0, $max = 0);
$max = $array[$x-1] if ($max < $array[$x-1]), (my#sorted = sort { $a <=> $b } #samples) {
print join(" ",#sorted);
}
##### median
if ($N % 2==1) {
print "$median = $sorted[int($N/2)]\n"; ## check this out
};
else ($median = ($sorted[$N/2] + $sorted[($N/2)-1]) / 2)) {
print "$median\n"; # check this out
};
##### quantiles 1º and 3º
if $qt1 = $sorted[$r25-1] {
print "\n"; # check this out
};
else $qt1 = $fr*($sorted[$ir] - $sorted[$ir-1]) + $sorted[$ir-1] {
print "\n"; # check this out
};
##### variance
for (my $i=0;
$i<scalar(#samples);
$i++)
{
$Var += ($samples[$i]-$mean)**2;
$Var = $Var/($N-1);
};
###### standard error
($Std = sqrt($Var)/ sqrt($N));
############################################################
print "$min\n";
print "$max\n";
print "$mean\n";
print "$median\n";
print "$qt1\n";
print "$var\n";
print "$std\n";
exit(0);
I want to get it working. Please help. THANKS IN ADVANCE!
Errors in your code:
open(FILEHANDLE, data.txt);
data.txt needs to be quoted. You are not checking the return value of the open, e.g. ... or die $!. You should use a lexical filehandle and three argument open, e.g. open my $fh, '<', "data.txt" or die $!.
shift #ARGV;
This does nothing except remove the first value from you argument list, which is then promptly discarded.
my #array = split(\t,$_);
You are using \t as a bareword, it should be a regex, /\t/. Your #array is declared inside a lexical scope of the while loop, and will be undefined outside this block.
$N = $sum = 0;
Both variables are not declared, which will cause the script to die when you use strict (which is a very good idea). Use my $N to solve that. Also, $N is not a very good variable name.
$array[$x-1];
This will do nothing. $x is not declared (see above), and also undefined. The whole statement does nothing, it is like having a line 3;. I believe you will get an error such as Useless use of variable in void context.
$N++;
This increments $N to 1, which is a useless thing to do, since you only a few lines above initialized it to 0.
Well.. the list goes on. I suggest you start smaller, use strict and warnings since they are very good tools, and work out the errors one by one. A very good idea would be to make subroutines of your calculations, e.g.:
sub sum {
# code here
return $sum;
}
Go to perldoc.perl.org and read the documentation. Especially useful would be the syntax related ones and perlfunc.
Also, you should be aware that this functionality can be found in modules, which you can find at CPAN.
Your main problem is you have not declared your variables such as $N, $max, etc.
You need to introduce all new variables with my the first time you reference them. Just like you did with $array and $i. So for example
$N = $sum = 0;
Should become
my( $N, $sum ) = ( 0, 0 );

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";