crunch_this! has asked for the
wisdom of the Perl Monks concerning the following question:
Hi again
I've got this part of my program, which is meant to go through all possibilities of w, x, y, z, except for when they're evenly spaced apart, or in arithmetic sequence if that makes sense to you. Since I already know those cases won't give me what I'm looking for I want to save perl the trouble of considering them. Here's what I've come up with, but I have a feeling there's a way to make it go faster. I wonder if I really need that s variable since it's just one more thing to deal with:
foreach my $w (3..100) {
foreach my $x (2..$w1) {
foreach my $y (1..$x1) {
foreach my $z (0..$y1) {
foreach my $s (1..20) {
unless ( $x == $w + $s && $y == $x + $s && $z == $
+y + $s ) {
I can add more context if you want, this is just part of the program which works fine I just thought I'd see if there's a way to streamline it.
Re: help with simplifying program by BrowserUk (Pope) on May 24, 2013 at 05:36 UTC 
That makes no sense?
Each of your inner loops is limited to the value of the preceding loop counter1, so can never attain a value that would trigger your condition.
Ie. $x is limited to $w1, so $x can never become equal to $w + $s for any value of $s, unless $s was 1 or less, but your $s loop start from 1 and increases.
In other words; your unless condition will always be false, and so it is doing nothing (except consuming cycles).
With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
Examine what is said, not who speaks  Silence betokens consent  Love the truth but pardon error.
"Science is about questioning the status quo. Questioning authority".
In the absence of evidence, opinion is indistinguishable from prejudice.
 [reply] 

 [reply] [d/l] [select] 
Re: help with simplifying program by tobyink (Abbot) on May 24, 2013 at 05:38 UTC 
Untested, but makes sense to me...
foreach my $w (3..100) {
foreach my $x (2..$w1) {
foreach my $y (1..$x1) {
foreach my $z (0..$y1) {
next if ($z$y==$y$x and $y$x==$x$w);
...;
}
}
}
}
package Cow { use Moo; has name => (is => 'lazy', default => sub { 'Mooington' }) } say Cow>new>name
 [reply] [d/l] 
Re: help with simplifying program by snoopy (Deacon) on May 24, 2013 at 05:50 UTC 
Hi There,
I'm not sure, if the unless condition is actually filtering anything:
use strict;
my $n1;
my $n2;
foreach my $w (3..100) {
foreach my $x (2..$w1) {
foreach my $y (1..$x1) {
foreach my $z (0..$y1) {
foreach my $s (1..20) {
$n1++;
unless ( $x == $w + $s
&& $y == $x + $s
&& $z == $y + $s ) {
$n2++;
}
}
}
}
}
}
print "n1: $n1\n";
print "n2: $n2\n";
Output:
n1: 81658500
n2: 81658500
 [reply] [d/l] [select] 
Re: help with simplifying program by hdb (Prior) on May 24, 2013 at 08:40 UTC 
From your description I take you want (w, x, y, z) such that 0 <= z < y < x < w <= 100 and not wx == xy == yz. In order to achieve this I would parametrize the loops using the differences dwx = wx, dxy = xy, dyz = yz.
use strict;
use warnings;
my $n = 5; # should be 100
my $c1 = my $c2 = 0;
for my $w (3..$n) {
for my $dwx (1..$w2) {
my $x = $w  $dwx;
for my $dxy (1..$x1) {
my $y = $x  $dxy;
for my $dyz (1..$y) {
my $z = $y  $dyz;
$c1++;
next unless $dwx != $dxy or $dxy != $dyz;
$c2++;
print "$w $x $y $z\n";
}
}
}
}
print "Found $c2 out of $c1 combinations\n";
UPDATE: Changes next unless... for better readibility.
 [reply] [d/l] [select] 

next unless $dwx != $dxy and $dxy != $dyz;
otherwise you will not get all of the valid combinations  [reply] [d/l] [select] 

next if $dwx == $dxy and $dxy == $dyz;
which I should have used in the first place.
 [reply] [d/l] [select] 

Re: help with simplifying program by BillKSmith (Deacon) on May 24, 2013 at 14:19 UTC 
 [reply] 

# $lep means smallest nonzero root in the interval [0, $rep]
# $rep means right endpoint of the interval [0, $rep]
my $lep = int 1;
my $rep = int 100;
foreach my $x ($lep+2 .. $rep ) {
foreach my $y ($lep+1 .. $x1 ) {
foreach my $z ($lep .. $y1 ) {
foreach my $s (1..$rep/4) {
unless ($y == $x + $s && $z == $y + $s ) {
# assigns a truth value to whether or not it is wi
+thin 0.0001 of an integer (1=true, 0=false)
sub is_approximately_an_integer {
my $eps = 0.0001;
while( my $w = shift ) {
# need to use "round", "int" does not work!
return 0 if abs( $wround($w) ) > $eps;
}
return 1
}
}
}
push @wants,
map { { join(', ', $x, $y, $z) => $_ } }
grep { is_approximately_an_integer( @$_ ) } [
poly_roots(
poly_derivative(
# expanded form of x*(x  $x)*(x  $y)*(x
+ $z)
1, $x  $y  $z, $x*$y + $x*$z + $y*$z, 
+$x*$y*$z, 0
)
)
];
}
}
}
 [reply] [d/l] 

Oops, I was slightly wrong. Combinations will only replace three of your loops. You still have to select the applicable combinations.
use Algorithm::Combinatorics qw(combinations);
my $lep = int 1;
my $rep = int 100;
my $iter = combinations( [$lep+2 ..$rep], 3 );
while (my $c = $iter>next) {
my ($x, $y, $z) = @$c;
foreach my $s (1..$rep/4) {
# as before
}
}
 [reply] [d/l] 

use strict;
use warnings;
use Algorithm::Combinatorics qw(combinations);
my $rep = 5; # should be 100
my @data = 0..$rep;
my $iter = combinations( \@data, 4 );
while( my $p = $iter>next ) {
my ( $z, $y, $x, $w ) = @$p;
next unless $w2*$x+$y or $x2*$y+$z;
print "$w, $x, $y, $z\n";
}
 [reply] [d/l] 


