[libmath-prime-util-perl] 08/59: Speedup for powmod/mulmod (helps factor, isprime, etc.)
Partha P. Mukherjee
ppm-guest at moszumanska.debian.org
Thu May 21 18:44:52 UTC 2015
This is an automated email from the git hooks/post-receive script.
ppm-guest pushed a commit to annotated tag v0.10
in repository libmath-prime-util-perl.
commit b4d8c78761a567efc1167e2822fafea5b246593d
Author: Dana Jacobsen <dana at acm.org>
Date: Thu Jun 28 13:45:30 2012 -0600
Speedup for powmod/mulmod (helps factor, isprime, etc.)
---
examples/bench-is-prime.pl | 5 +++--
examples/bench-pp-sieve.pl | 5 ++++-
lib/Math/Prime/Util/PP.pm | 42 +++++++++++++++++++-----------------------
3 files changed, 26 insertions(+), 26 deletions(-)
diff --git a/examples/bench-is-prime.pl b/examples/bench-is-prime.pl
index a96a214..83fc71d 100755
--- a/examples/bench-is-prime.pl
+++ b/examples/bench-is-prime.pl
@@ -9,6 +9,7 @@ use Math::Prime::Util;
use Benchmark qw/:all/;
use List::Util qw/min max/;
my $count = shift || -5;
+my $numbers = 1000;
my $is64bit = (~0 > 4294967295);
my $maxdigits = ($is64bit) ? 20 : 10; # Noting the range is limited for max.
@@ -24,14 +25,14 @@ sub test_at_digits {
my $base = ($digits == 1) ? 0 : int(10 ** ($digits-1));
my $max = int(10 ** $digits);
$max = ~0 if $max > ~0;
- my @nums = map { $base+int(rand($max-$base)) } (1 .. 1000);
+ my @nums = map { $base+int(rand($max-$base)) } (1 .. $numbers);
my $min_num = min @nums;
my $max_num = max @nums;
#my $sieve = Math::Prime::FastSieve::Sieve->new(10 ** $magnitude + 1);
#Math::Prime::Util::prime_precalc(10 ** $magnitude + 1);
- print "is_prime for 1000 random $digits-digit numbers ($min_num - $max_num)\n";
+ print "is_prime for $numbers random $digits-digit numbers ($min_num - $max_num)\n";
cmpthese($count,{
#'Math::Primality' => sub { Math::Primality::is_prime($_) for @nums },
diff --git a/examples/bench-pp-sieve.pl b/examples/bench-pp-sieve.pl
index 5464eb3..681c568 100644
--- a/examples/bench-pp-sieve.pl
+++ b/examples/bench-pp-sieve.pl
@@ -17,12 +17,15 @@ my $sum;
# This is like counting, but we want an array returned.
# The subs will compute a sum on the results.
+# In practice you would probably want to return a ref to your array, or return
+# a ref to your sieve structure and let the caller decode it as needed.
+
# Times for 100k.
# Vs. MPU sieve, as we move from 8k to 10M:
# Atkin MPTA, Rosetta 3 & 1, Shootout, Scriptol, DO Array, DJ Array, and
# InMany all slow down. Atkin 2 speeds up (from 65x slower to 54x slower).
# The DJ string methods have almost no relative slowdown, so stretch out their
-# advantage over the others (In Many, DJ Array, DJ Vec, and DO Array).
+# advantage over the other fast ones (In Many, DJ Array, DJ Vec, and DO Array).
my $pc_subs = {
"Rosetta 4" => sub {$sum=0; $sum+=$_ for rosetta4($countarg);$sum;}, # 9/s
"Atkin MPTA"=> sub {$sum=0; $sum+=$_ for atkin($countarg);$sum;}, # 11/s
diff --git a/lib/Math/Prime/Util/PP.pm b/lib/Math/Prime/Util/PP.pm
index 3c0e81a..e95934c 100644
--- a/lib/Math/Prime/Util/PP.pm
+++ b/lib/Math/Prime/Util/PP.pm
@@ -30,6 +30,12 @@ BEGIN {
}
sub _maxbits { $_uv_size }
+# If $n < $_half_word, then $n*$n will be exact.
+my $_half_word = (~0 == 18446744073709551615) ? 4294967296 : # 64-bit
+ (~0 == 4294967295) ? 65536 : # 32-bit
+ (~0 == -1) ? 1000**10 : # bignum
+ 0 ; # No idea
+
my $_precalc_size = 0;
sub prime_precalc {
my($n) = @_;
@@ -128,10 +134,10 @@ sub is_prime {
# .. do something with the prime $n
# }
#
-# We're using method 4, though sadly it is memory intensive.
-#
-# Even so, it is a lot less memory than making an array entry for each number,
-# and the performance is almost 10x faster than a naive array sieve.
+# We're using method 4, though sadly it is memory intensive relative to the
+# other methods. I will point out that it is 30-60x less memory than sieves
+# using an array, and the performance of this function is over 10x that
+# of naive sieves like found on RosettaCode.
sub _sieve_erat_string {
my($end) = @_;
@@ -557,6 +563,7 @@ sub nth_prime {
sub _mulmod {
my($a, $b, $m) = @_;
+ return (($a * $b) % $m) if ($a|$b) < $_half_word;
my $r = 0;
while ($b > 0) {
if ($b & 1) {
@@ -577,7 +584,7 @@ sub _powmod {
my($n, $power, $m) = @_;
my $t = 1;
- if ( (~0 == 18446744073709551615) && ($m < 4294967296) ) {
+ if ($m < $_half_word) {
$n %= $m;
while ($power) {
$t = ($t * $n) % $m if ($power & 1) != 0;
@@ -633,23 +640,12 @@ sub miller_rabin {
my $x = _powmod($a, $d, $n);
next if ($x == 1) || ($x == ($n-1));
- if (~0 == 18446744073709551615) {
- foreach my $r (1 .. $s) {
- $x = ($x < 4294967296) ? ($x*$x) % $n : _mulmod($x, $x, $n);
- return 0 if $x == 1;
- if ($x == ($n-1)) {
- $a = 0;
- last;
- }
- }
- } else {
- foreach my $r (1 .. $s) {
- $x = ($x < 65536) ? ($x*$x) % $n : _mulmod($x, $x, $n);
- return 0 if $x == 1;
- if ($x == ($n-1)) {
- $a = 0;
- last;
- }
+ foreach my $r (1 .. $s) {
+ $x = ($x < $_half_word) ? ($x*$x) % $n : _mulmod($x, $x, $n);
+ return 0 if $x == 1;
+ if ($x == ($n-1)) {
+ $a = 0;
+ last;
}
}
return 0 if $a != 0;
@@ -863,7 +859,7 @@ sub holf_factor {
for my $i (1 .. $rounds) {
my $s = int(sqrt($n * $i));
$s++ if ($s * $s) != ($n * $i);
- my $m = _mulmod($s, $s, $n);
+ my $m = ($s < $_half_word) ? ($s*$s) % $n : _mulmod($s, $s, $n);
# Check for perfect square
my $mcheck = $m & 127;
next if (($mcheck*0x8bc40d7d) & ($mcheck*0xa1e2f5d1) & 0x14020a);
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-perl/packages/libmath-prime-util-perl.git
More information about the Pkg-perl-cvs-commits
mailing list