Pythagorean triples/Java/Brute force primitives: Difference between revisions

From Rosetta Code
Content added Content deleted
m (Notes about the Triple class, also THREE and FOUR were never used)
m (Extra notes and comments, move a comparison so it is only calculated if needed.)
Line 1: Line 1:
{{works with|Java|1.5+}}
{{works with|Java|1.5+}}
This version brute forces primitive triple candidates and then scales them to find the rest (under the perimeter limit of course). Since it only finds the primitives mathematically it can optimize its candidates based on some of the properties [[wp:Pythagorean_triple#Elementary_properties_of_primitive_Pythagorean_triples|here]] -- namely that a and b have opposite evenness, c is always odd, and that a<sup>2</sup> + b<sup>2</sup> must be a perfect square (which [[wp:Square_number#Properties|don't ever end in 2, 3, 7, or 8]]). For a perimeter limit of 1000, it is about 3 times faster than [[Pythagorean triples#Java|the other brute force version]]. For a perimeter limit of 10000, it is about 5 times faster. It also does not mark the primitives.
This version brute forces primitive triple candidates and then scales them to find the rest (under the perimeter limit of course). Since it only finds the primitives mathematically it can optimize its candidates based on some of the properties [[wp:Pythagorean_triple#Elementary_properties_of_primitive_Pythagorean_triples|here]] -- namely that a and b have opposite evenness, c is always odd, and that a<sup>2</sup> + b<sup>2</sup> must be a perfect square (which [[wp:Square_number#Properties|don't ever end in 2, 3, 7, or 8]]). Notably, it doesn't use a GCD function to check for primitives (<code>BigInteger.gcd()</code> uses the binary GCD algorithm, [[wp:Computational_complexity_of_mathematical_operations#Number_theory|which is O(n<sup>2</sup>)]]). For a perimeter limit of 1000, it is about 3 times faster than [[Pythagorean triples#Java|the other brute force version]]. For a perimeter limit of 10000, it is about 5 times faster. It also does not mark the primitives.


It defines a <code>Triple</code> class which is comparable so it can be placed in a <code>Set</code> to remove duplicates. It also can scale itself by an integer factor.
It defines a <code>Triple</code> class which is comparable so it can be placed in a <code>Set</code> to remove duplicates (e.g. this algorithm finds [15, 20, 25] as a primitive candidate after it had already been added by scaling [3, 4, 5]). It also can scale itself by an integer factor.

Note: this implementation also keeps all triples in memory. Be mindful of large perimeter limits.
<lang java5>import java.math.BigInteger;
<lang java5>import java.math.BigInteger;
import java.util.Set;
import java.util.Set;
Line 25: Line 27:
public Triple scale(long k){
public Triple scale(long k){
return new Triple(a.multiply(BigInteger.valueOf(k)),
return new Triple(a.multiply(BigInteger.valueOf(k)),
b.multiply(BigInteger.valueOf(k)),
b.multiply(BigInteger.valueOf(k)),
c.multiply(BigInteger.valueOf(k)));
c.multiply(BigInteger.valueOf(k)));
}
}
Line 38: Line 40:
@Override
@Override
public int compareTo(Triple o) {
public int compareTo(Triple o) {
//sort by a, then b, then c
if(!a.equals(o.a)) return a.compareTo(o.a);
if(!a.equals(o.a)) return a.compareTo(o.a);
if(!b.equals(o.b)) return b.compareTo(o.b);
if(!b.equals(o.b)) return b.compareTo(o.b);
Line 60: Line 63:
}
}
}
}
public static void main(String[] args){
public static void main(String[] args){
long primCount = 0;
long primCount = 0;
long start = System.currentTimeMillis();
long start = System.currentTimeMillis();
Line 66: Line 69:
BigInteger peri2 = LIMIT.divide(BigInteger.valueOf(2)),
BigInteger peri2 = LIMIT.divide(BigInteger.valueOf(2)),
peri3 = LIMIT.divide(BigInteger.valueOf(3));
peri3 = LIMIT.divide(BigInteger.valueOf(3));

for(BigInteger a = ONE; a.compareTo(peri3) < 0; a = a.add(ONE)){
for(BigInteger a = ONE; a.compareTo(peri3) < 0; a = a.add(ONE)){
BigInteger aa = a.multiply(a);
BigInteger aa = a.multiply(a);

//b is the opposite evenness of a so increment by 2
//b is the opposite evenness of a so increment by 2
for(BigInteger b = a.add(ONE);
for(BigInteger b = a.add(ONE);
Line 76: Line 81:
BigInteger ab = a.add(b);
BigInteger ab = a.add(b);
BigInteger aabb = aa.add(bb);
BigInteger aabb = aa.add(bb);

for(BigInteger c = b.add(b.and(ONE).equals(ONE)? TWO:ONE);
for(BigInteger c = b.add(b.and(ONE).equals(ONE)? TWO:ONE);
c.compareTo(peri2) < 0; c = c.add(TWO)){
c.compareTo(peri2) < 0; c = c.add(TWO)){
int compare = aabb.compareTo(c.multiply(c));
//if a+b+c > periLimit
//if a+b+c > periLimit
if(ab.add(c).compareTo(LIMIT) > 0){
if(ab.add(c).compareTo(LIMIT) > 0){
break;
break;
}
}

int compare = aabb.compareTo(c.multiply(c));
//if a^2 + b^2 != c^2
//if a^2 + b^2 != c^2
if(compare < 0){
if(compare < 0){

Revision as of 17:07, 15 July 2011

Works with: Java version 1.5+

This version brute forces primitive triple candidates and then scales them to find the rest (under the perimeter limit of course). Since it only finds the primitives mathematically it can optimize its candidates based on some of the properties here -- namely that a and b have opposite evenness, c is always odd, and that a2 + b2 must be a perfect square (which don't ever end in 2, 3, 7, or 8). Notably, it doesn't use a GCD function to check for primitives (BigInteger.gcd() uses the binary GCD algorithm, which is O(n2)). For a perimeter limit of 1000, it is about 3 times faster than the other brute force version. For a perimeter limit of 10000, it is about 5 times faster. It also does not mark the primitives.

It defines a Triple class which is comparable so it can be placed in a Set to remove duplicates (e.g. this algorithm finds [15, 20, 25] as a primitive candidate after it had already been added by scaling [3, 4, 5]). It also can scale itself by an integer factor.

Note: this implementation also keeps all triples in memory. Be mindful of large perimeter limits. <lang java5>import java.math.BigInteger; import java.util.Set; import java.util.TreeSet;

import static java.math.BigInteger.ONE;

public class PythTrip2{

     public static final BigInteger TWO = BigInteger.valueOf(2);
     private static BigInteger LIMIT = BigInteger.valueOf(100);
    
     public static class Triple implements Comparable<Triple>{
           BigInteger a, b, c, peri;

           public Triple(BigInteger a, BigInteger b, BigInteger c) {
                 this.a = a;
                 this.b = b;
                 this.c = c;
                 peri = a.add(b).add(c);
           }
          
           public Triple scale(long k){
                 return new Triple(a.multiply(BigInteger.valueOf(k)),
                                   b.multiply(BigInteger.valueOf(k)),
                                   c.multiply(BigInteger.valueOf(k)));
           }

           @Override
           public boolean equals(Object obj) {
                 if(obj.getClass() != this.getClass()) return false;
                 Triple trip = (Triple)obj;
                 return a.equals(trip.a) && b.equals(trip.b) && c.equals(trip.c);
           }

           @Override
           public int compareTo(Triple o) {
                 //sort by a, then b, then c
                 if(!a.equals(o.a)) return a.compareTo(o.a);
                 if(!b.equals(o.b)) return b.compareTo(o.b);
                 if(!c.equals(o.c)) return c.compareTo(o.c);
                 return 0;
           }
          
           @Override
           public String toString(){
                 return a + ", " + b + ", " + c;
           }
     }
    
     private static Set<Triple> trips = new TreeSet<Triple>();
    
     public static void addAllScales(Triple trip){
           long k = 1;
           Triple tripCopy = new Triple(trip.a, trip.b, trip.c);
           while(tripCopy.peri.compareTo(LIMIT) < 0){
                 trips.add(tripCopy);
                 tripCopy = trip.scale(k++);
           }
     }
     public static void main(String[] args){
       long primCount = 0;
       long start = System.currentTimeMillis();
       //change this to whatever perimeter limit you want;the RAM's the limit
       BigInteger peri2 = LIMIT.divide(BigInteger.valueOf(2)),
               peri3 = LIMIT.divide(BigInteger.valueOf(3));
       for(BigInteger a = ONE; a.compareTo(peri3) < 0; a = a.add(ONE)){
           BigInteger aa = a.multiply(a);
           //b is the opposite evenness of a so increment by 2
           for(BigInteger b = a.add(ONE);
                   b.compareTo(peri2) < 0; b = b.add(TWO)){
               BigInteger bb = b.multiply(b);
               //if a^2 + b^2 is not a perfect square then don't even test for c's
               if(aa.add(bb).toString().matches(".*[2378]")) continue;
               BigInteger ab = a.add(b);
               BigInteger aabb = aa.add(bb);
               for(BigInteger c = b.add(b.and(ONE).equals(ONE)? TWO:ONE);
                       c.compareTo(peri2) < 0; c = c.add(TWO)){
                   //if a+b+c > periLimit
                   if(ab.add(c).compareTo(LIMIT) > 0){
                       break;
                   }
                   int compare = aabb.compareTo(c.multiply(c));
                   //if a^2 + b^2 != c^2
                   if(compare < 0){
                       break;
                   }else if (compare == 0){
                       Triple prim = new Triple(a, b, c);
                       if(trips.add(prim)) primCount++;
                       addAllScales(prim);
                   }
               }
           }
       }
       for(Triple trip:trips){
           System.out.println(trip);
       }
       System.out.println("Runtime: " + (System.currentTimeMillis() - start));
       System.out.println("Up to a perimeter of " + LIMIT + ", there are "
               + trips.size() + " triples, of which " + primCount + " are primitive.");
   }

}</lang> Output:

3, 4, 5
5, 12, 13
6, 8, 10
7, 24, 25
8, 15, 17
9, 12, 15
9, 40, 41
10, 24, 26
12, 16, 20
12, 35, 37
15, 20, 25
15, 36, 39
16, 30, 34
18, 24, 30
20, 21, 29
21, 28, 35
24, 32, 40
Runtime: 22
Up to a perimeter of 100, there are 17 triples, of which 7 are primitive.