I'd tried lots of ways to get rid of this problem but... I can't find what's the problem of this code.
I use Perl and I want to find the prime number between 1-100.
use strict;
my $i=0;
my $j=0;
my $count=0;
for ($i=0; $i<101; $i++)
{
for ($j=2; $j<$i; $j++)
{
if ($i%$j==0)
{
$count+=1;
}
}
if ($count==0)
{
print "$i\n";
}
}
There are a few things to think about as you use the Sieve of Eratosthenes. Armali already pointed out that you were reusing the value in $count because you had it in a higher scope, so it didn't reset for each number you wanted to check.
But, I reformatted your code a bit:
use v5.10;
NUM: for( my $i=1; $i < 101; $i++ ) {
DIVISOR: for( my $j=2; $j < $i; $j++ ) {
next NUM if $i%$j == 0;
}
say $i;
}
Instead of using a flag variable ($count) to figure out what to do, you can use loop controls. If you find any divisor, you know that you have found a non-prime and there's no need to continue. That is, you don't need to count divisors.
When you find one, stop and move on to the next number. To do that, I've labeled the looping constructs. That way, in the inner loop I can skip to the next iteration of the outer loop. And, usually, once I label one loop I label them all but you don't need to do that.
Once you figure that part out, you don't need to do so much work. Aside from 2, you know that all the even numbers are not prime. You don't need to check those. So, instead of being clever, I'll just break out 2 as a special case:
use v5.10;
say 2;
NUM: for( my $i = 3; $i < 101; $i += 2 ) {
DIVISOR: for( my $j=2; $j < $i; $j++ ) {
next NUM if $i%$j == 0;
}
say $i;
}
The inner loop is doing too much work too. None of the numbers that you are checking are even, so you don't need to check any even divisors (or those ending 5 once you choose 5). And, you only have to go half way, so you can stop when you get to the square root of the number.
#!perl
use v5.10;
say 2;
NUM: for( my $i = 3; $i < 101; $i += 2 ) {
my $stop_at = int sqrt $i;
DIVISOR: for( my $j=3; $j <= $stop_at; $j += 2 ) {
next NUM if $i % $j == 0;
}
say $i;
}
And, for a final flourish, I'll take the top number from the command-line arguments but default to 100. With that, the comparison in the outer loop changes to <=:
#!perl
use v5.10;
my $limit = $ARGV[0] // 100;
say 2;
NUM: for( my $i = 3; $i <= $limit; $i += 2 ) {
my $stop_at = int sqrt $i;
DIVISOR: for( my $j=3; $j <= $stop_at; $j += 2 ) {
next NUM if $i % $j == 0;
}
say $i;
}
But, ikegami notes in a comment that for my $x (0..$n-1) is more idiomatic. That doesn't easily handle step sizes larger than 1. You can do various things to multiply that number to get the candidate number, or ways to generate the list ahead of time (but that means you have the list all at once). I'll switch to a while instead, and assume that these other bits do their work properly.
The $get_number is some magic subroutine that always gives us back the next number, and the is_prime does what it does to make the determination:
while( my $n = $get_number->() ) {
say $n if is_prime($n);
}
Here's one way that might work. First, there's a nifty Perl regex trick to determine primes. It doesn't matter that I'm using that because you can change it to whatever you like because it's hidden behind is_prime. The biggest benefit here is that it's short (and a bit of a show off):
#!perl
use v5.10;
my $get_number = generate_sub( $ARGV[0] // 100 );
while( my $n = $get_number->() ) {
say $n if is_prime($n);
}
sub is_prime { ( '1' x $_[0] ) !~ /\A(11+?)\1+\z/ }
sub generate_sub {
my( $limit ) = #_;
sub {
state $queue = [ 2, 3 ];
return if $queue->[0] > $limit;
push $queue->#*, $queue->[-1] + 2;
shift $queue->#*;
}
}
The generate_sub is a bit more tricky. First, the 2 makes is a bit tricky. Second, Perl doesn't have a yield like Python or Ruby (would be nice). To get around that, I'll see a queue with the first two numbers then add the next number based on the last on (so, adding 2 to 3 gets 5, and so on). That gets around the unique interval from 2 to 3. This stops if the next number in the queue is above the one that you want.
But, that's a bit complicated and only there to handle the special case of 2. I've been playing with a different idiom lately although I'm not convinced its desirable.
The state is a way to declare a persistent lexical variable. It runs only on the first execution. We'll use a state to return the first 2 right away. Then, the next time we come around, that $rc statement doesn't run and $next has 3. From there, I get the current number (0+$next so it's not the same data), and increment $next in a list, but only return the first in that list. That's just a trick that condenses the if-else:
sub generate_sub {
my( $limit ) = #_;
sub {
state $rc = do { return 2 };
state $next = 3;
return $next <= $limit ? ( 0+$next, $next += 2 )[0] : ();
}
}
I don't recommend this for your problem, but you should consider a way to generate the list of numbers so it's not tightly coupled to the problem. That way, you can get rid of the looping constructs.
But, that's much more than you needed to know.
You initialized my $count=0; outside instead of inside the outer for loop.
Besides that, $i should start from 2 rather than 0.
Related
I came across code:
sub insertDecimal
{
my $number = shift;
my $sigDigRight = shift;
if ($number =~ /\./) { return ($number); }
elsif (length $number < $sigDigRight) { return ($number); }
else
{
my $leftSide = substr($number, 0, (length $number)-$sigDigRight);
my $rightSide = substr($number, (length $number)-$sigDigRight, );
return ($leftSide . "." . $rightSide);
}
}
And I hoped to improve/re-write as:
sub insertDecimal
{
my ($number, $sigDigRight) = #_;
return $number if index ($number, '.') != -1 or length $number < $sigDigRight;
# YES! substr takes an LVALUE ... perldoc it for more :)
substr($number, -$sigDigRight, 0) = '.';
return $number;
}
I was very surprised that a run of some 74mm records had almost no improvement at all with 2nd version.
Questions:
Anyone to overflow with better way to make insertDecimal more efficient ?
How come I see no improvement, at all (just one minute better on 74MM records) ?
If Perl compiler is re-jiggering the code of the first version to be more efficient, is there anyway I can see the improved path to execution that Perl has chosen ?
Both routines would seem to do essentially the same amount of work:
scan $number for a single character (any compiler ought be able to reduce that regex match to an index)
compare the length of $number to a limit
possibly insert a single character somewhere within $number
Using lvalue substr (or, just taking advantage of the fourth argument to substr) may make the insertion a little more efficient, but, after all, things will have to be moved.
To my eye, the biggest opportunity for optimization comes from moving the length check ahead of the check for the decimal point.
I would be tempted to re-write your routine as
sub insertDecimal {
my ($number, $sigDigRight) = #_;
return $number if length($number) < $sigDigRight;
return $number if index($number, '.') >= 0;
substr($number, -$sigDigRight, 0, '.');
$number;
}
I find simple decisions and short lines to be easier to understand. I do not think this should change the correctness of the function.
An ugly alternative is:
sub gah {
my ($number, $sigDigRight) = #_;
my $n = length($number) - $sigDigRight;
return $number unless $n > 0;
$number =~ s{\A ([^.]{$n}) ([^.]+) \z}{$1.$2}x;
$number;
}
That combines the check for . with the replacement operation.
Again, I cannot be certain this is correct wrt your spec, but it is something for you to explore.
I probably would not opt for gah unless the improvement was more than 20% of something that took at least an hour or so. On my system, it slows down a simple example by 1,000%.
Why are the values of $copy_of_i's returned by coderefs in the #coderefs the same?
use Modern::Perl;
my #coderefs = ();
for (my $i = 0; $i < 5; $i++){
push #coderefs, sub {
my $copy_of_i = $i;
return $copy_of_i;
};
}
say $coderefs[1]->();
say $coderefs[3]->();
I thought the $copy_of_i would be local for every coderef added to #coderefs and thus contain the current value of $i assigned to the $copy_of_i at the given iteration of the loop. But if we display the values of a couple of $copi_of_i's with 'say' we'll see that they have the same values as if the $copy_of_i wasn't local for every newly created coderef. Why?
You want to have different values associated with the closures, yet you only have the single variable $i for all the closures to capture. You need to create a variable for each closure to capture, so $copy_of_i should be created outside of the closure. Creating a copy when you call the closure is far too late; $i no longer contains the desired value at that point.
for (my $i = 0; $i < 5; $i++){
my $copy_of_i = $i;
push #coderefs, sub {
return $copy_of_i;
};
}
By the way, for my $i (0 .. 5) is preferred over for (my $i = 0; $i < 5; $i++), and it has the advantage of creating a new variable for each iteration of the loop, so you can simply use
my #coderefs;
for my $i (0 .. 4) {
push #coderefs, sub {
return $i;
};
}
I started learning perl recently and I wrote this code to find the largest palindrome product that can be obtained by multiplying 2 3-digit numbers. (question here: https://projecteuler.net/problem=4 )
Here is the code:
#!/usr/bin/perl
use 5.010;
sub checkpal{
for ($k=0;$k<length($_[0]);$k++){$b = substr ($_[0], $k, 1).$b;}
if ($_[0] eq $b){1}else{0}
}
$pals = $numb1 = undef;
for ($i = 998001; $i>=10000; $i--){
if (&checkpal($i)){
for ($j = 100; $j <1000; $j++){
if ( !($i % $j) && (length $j == 3) ){$numb1 = $j; $pals = $i; last;}
}
}
if (defined $numb1){last}
}
say $numb1." ".($pals/$numb1);
My idea is quite simple. It simply goes through a loop starting from 998001 (the largest value that product of 2 3-digit number can have) and check if the value is palindrome. If it is a palindrome, it goes through another loop to check if it can be obtained by multiplying 2 three digit numbers. Now, this algorithm might not be the most efficient or the best in the world, it should at least give the result. Which it isn't.
The problem isn't in the subroutine checkpal as far as I know. But the if (&checkblock($i)) block doesn't get executed even when $i is a palindrome. And I don't know why. Sorry if it is obvious or something .. but please tell me why it isn't working?
if ( !($i % $j) and length($i/$j)==3) { .. }
instead of
if ( !($i % $j) && (length $j == 3) )
as you want to check whether $i/$j has three digits, not $j which goes anyway from 100 to 999.
As a side-note,
if (checkpal($i))
can be replaced with simple
if ($i eq reverse $i)
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...
What are the arguments for using one of the for loops over the other to count through integers?
for my $i (0..$n) { ... }
for (my $i = 0; $i <= $n; $i++) { ... }
In your case, there isn't any difference.
In other use cases, in a for-each loop you don't have control of or access to the iterator.
Using your example syntax:
for my $i (0..10)
{
print "$i";
$i++;
}
The above actually creates a foreach loop - it's the same as saying foreach my $i (0..10). $i is a value returned from the list, not the iterator. The iterator is internal and you don't have any access to it; you can not control the flow of the loop.
The output from above will print 012345678910.
This:
for ( my $i = 0; $i++ ; $i <= 10)
{
print $i;
$i++;
}
That is an actual for loop. You are controlling and outputting the iterator. It will output:
0246810
In addition:
When you do for (1..$n) you are invoking the range operator vs. doing a simple comparison at the top of the loop. Performance differences, however, would be immeasurable.
The difference between the two styles of for loops in Perl is one of both clarity and efficiency.
When you look at for my $i (0 .. $n) {...} you can instantly see the range being used without having to mentally parse a larger expression.
With for (my $i = 0; $i <= $n; $i++) {...} there is quite a bit more to look at, and more places where errors can creep in.
In addition, foreach over a range is faster than the equivalent C-style loop as shown by the following benchmark:
use Benchmark 'cmpthese';
for my $mag (map 10**$_, 1 .. 6) {
print "\n$mag:\n";
cmpthese -2 => {
loop => sub {my $x = 0; for (my $i = 0; $i <= $mag; $i++) {$x += $i}},
each => sub {my $x = 0; for my $i (0 .. $mag) {$x += $i}},
};
}
which prints:
10:
Rate loop each
loop 613877/s -- -2%
each 625568/s 2% --
100:
Rate loop each
loop 79481/s -- -24%
each 104758/s 32% --
1000:
Rate loop each
loop 8140/s -- -27%
each 11220/s 38% --
10000:
Rate loop each
loop 832/s -- -26%
each 1124/s 35% --
100000:
Rate loop each
loop 81.6/s -- -26%
each 110/s 34% --
1000000:
Rate loop each
loop 6.90/s -- -26%
each 9.27/s 34% --
With the three-part for loop, you are able to $i += 3 and such if you want from within the loop, but with the foreach loop, you are not. That is the difference.
The off-by-one error that you originally had in the C-style loop shows that the C-style loop is more error prone, and should only be used if you actually need access to the loop counter. A foreach loop makes it much more difficult to make an off-by-one error.
I believe that the two loop examples you posted behave identically. In both cases, $i is lexically scoped (since you use the my keyword). I think the 0..$n syntax is clearer, but (surprise!) not everyone would agree with that.