Are there any ways to make this program faster? I am thinking about some faster tools for user input etc.
Here is my code:
sub partia {
my ( $u, $v ) = @_;
if ( $u == $v ) { return $u }
if ( $u == 0 ) { return $v }
if ( $v == 0 ) { return $u }
if ( ~$u & 1 ) {
if ( $v & 1 ) {
return partia( $u >> 1, $v );
}
else {
return partia( $u >> 1, $v >> 1 ) << 1;
}
}
if ( ~$v & 1 ) {
return partia( $u, $v >> 1 );
}
if ( $u > $v ) {
return partia( ( $u - $v ) >> 1, $v );
}
return partia( ( $v - $u ) >> 1, $u );
}
sub calosc {
$t = <>;
while ($t) {
@tab = split( /\s+/, <> );
print( partia( $tab[0], $tab[1] ), "\n" );
$t--;
}
}
calosc();
How does program works :
Generally it returns greatest common divisor for 2 numbers inputed by user. It's mostly Stein's algorithm.
INPUT :
First line:
How many pairs user wants to check.[enter]
Second line:
first number [space] second number[enter]
OUTPUT:
GCD[enter]
In Python I would use things like :
from sys import stdin
t=int(stdin.readline())
instead of
t=input()
Is there any way to do it?
It appears that you're simply trying to get the GCD of two numbers, and wanting to do so quickly.
You're apparently using the recursive version of the Binary GCD Algorithm. Typically speaking, it is much better to use an iterative algorithm for both speed and scalability. However, I would assert that it is almost certainly worth it to try the much simpler Euclidean algorithm first.
I've adapted your script to take 3 number pairs from the __DATA__
block as input. The first pair are just two small numbers, then I have two numbers from the Fibonacci Sequence, and finally two larger numbers including some shared powers of two.
I then coded two new subroutines. One of them uses the Iterative Stein's Algorithm (the method your using), and the other is just a simple Euclidean Algorithm. Benchmarking your partia
subroutine versus my two subroutine for 1 million iterations report that the iterative is 50% faster, and that Euclid is 3 times faster.
use strict;
use warnings;
use Benchmark;
#use Math::Prime::Util::GMP qw(gcd);
# Original solution
# - Stein's Algorithm (recursive)
sub partia {
my ( $u, $v ) = @_;
if ( $u == $v ) { return $u }
if ( $u == 0 ) { return $v }
if ( $v == 0 ) { return $u }
if ( ~$u & 1 ) {
if ( $v & 1 ) {
return partia( $u >> 1, $v );
}
else {
return partia( $u >> 1, $v >> 1 ) << 1;
}
}
if ( ~$v & 1 ) {
return partia( $u, $v >> 1 );
}
if ( $u > $v ) {
return partia( ( $u - $v ) >> 1, $v );
}
return partia( ( $v - $u ) >> 1, $u );
}
# Using Euclidian Algorithm
sub euclid {
my ( $quotient, $divisor ) = @_;
return $divisor if $quotient == 0;
return $quotient if $divisor == 0;
while () {
my $remainder = $quotient % $divisor;
return $divisor if $remainder == 0;
$quotient = $divisor;
$divisor = $remainder;
}
}
# Stein's Algorithm (Iterative)
sub stein {
my ($u, $v) = @_;
# GCD(0,v) == v; GCD(u,0) == u, GCD(0,0) == 0
return $v if $u == 0;
return $u if $v == 0;
# Remove all powers of 2 shared by U and V
my $twos = 0;
while ((($u | $v) & 1) == 0) {
$u >>= 1;
$v >>= 1;
++$twos;
}
# Remove Extra powers of 2 from U. From here on, U is always odd.
$u >>= 1 while ($u & 1) == 0;
do {
# Remove all factors of 2 in V -- they are not common
# Note: V is not zero, so while will terminate
$v >>= 1 while ($v & 1) == 0;
# Now U and V are both odd. Swap if necessary so U <= V,
# then set V = V - U (which is even). For bignums, the
# swapping is just pointer movement, and the subtraction
# can be done in-place.
($u, $v) = ($v, $u) if $u > $v;
$v -= $u;
} while ($v != 0);
return $u << $twos;
}
# Process 3 pairs of numbers
my @nums;
while (<DATA>) {
my ($num1, $num2) = split;
# print "Numbers = $num1, $num2\n";
# print ' partia = ', partia($num1, $num2), "\n";
# print ' euclid = ', euclid($num1, $num2), "\n";
# print ' stein = ', stein($num1, $num2), "\n";
# print ' gcd = ', gcd($num1, $num2), "\n\n";
push @nums, [$num1, $num2];
}
# Benchmark!
timethese(1_000_000, {
'Partia' => sub { partia(@$_) for @nums },
'Euclid' => sub { euclid(@$_) for @nums },
'Stein' => sub { stein(@$_) for @nums },
# 'GCD' => sub { gcd(@$_) for @nums },
});
__DATA__
20 25 # GCD of 5
89 144 # GCD of Fibonacci numbers = 1
4789084 957196 # GCD of 388 = 97 * 2 * 2
Outputs:
Benchmark: timing 1000000 iterations of Euclid, Partia, Stein...
Euclid: 9 wallclock secs ( 8.31 usr + 0.00 sys = 8.31 CPU) @ 120279.05/s (n=1000000)
Partia: 26 wallclock secs (26.00 usr + 0.00 sys = 26.00 CPU) @ 38454.14/s (n=1000000)
Stein: 18 wallclock secs (17.36 usr + 0.01 sys = 17.38 CPU) @ 57544.02/s (n=1000000)
The fastest solutions are likely to be C implementations of these algorithms though. I therefore recommend finding already coded versions like that provided by Math::Prime::Util::GMP
.
Running benchmarks including this new function shows that it is twice again as fast as the basic Euclidean algorithm that I programmed:
Benchmark: timing 1000000 iterations of Euclid, GCD, Partia, Stein...
Euclid: 8 wallclock secs ( 8.32 usr + 0.00 sys = 8.32 CPU) @ 120264.58/s (n=1000000)
GCD: 3 wallclock secs ( 3.93 usr + 0.00 sys = 3.93 CPU) @ 254388.20/s (n=1000000)
Partia: 26 wallclock secs (25.94 usr + 0.00 sys = 25.94 CPU) @ 38546.04/s (n=1000000)
Stein: 18 wallclock secs (17.55 usr + 0.00 sys = 17.55 CPU) @ 56976.81/s (n=1000000)