Pythagorean triples: Difference between revisions

From Rosetta Code
Content added Content deleted
(→‎{{header|Python}}: Fix first method and add more efficient second.)
Line 458: Line 458:
Up to a perimeter of 19500 there are 10388 triples, of which 1373 are primitive
Up to a perimeter of 19500 there are 10388 triples, of which 1373 are primitive
Up to a perimeter of 20000 there are 10689 triples, of which 1408 are primitive</pre>
Up to a perimeter of 20000 there are 10689 triples, of which 1408 are primitive</pre>
Barebone minimum for this task:<lang Python>from sys import setrecursionlimit
setrecursionlimit(2000) # 2000 ought to be big enough for everybody

def triples(lim, a = 3, b = 4, c = 5):
l = a + b + c
if l > lim: return (0, 0)
return reduce(lambda x, y: (x[0] + y[0], x[1] + y[1]), [
(1, lim / l),
triples(lim, a - 2*b + 2*c, 2*a - b + 2*c, 2*a - 2*b + 3*c),
triples(lim, a + 2*b + 2*c, 2*a + b + 2*c, 2*a + 2*b + 3*c),
triples(lim, -a + 2*b + 2*c, -2*a + b + 2*c, -2*a + 2*b + 3*c) ])

for peri in [10 ** e for e in range(1, 8)]:
print peri, triples(peri)</lang>Output:<lang>10 (0, 0)
100 (7, 17)
1000 (70, 325)
10000 (703, 4858)
100000 (7026, 64741)
1000000 (70229, 808950)
10000000 (702309, 9706567)</lang>

Revision as of 09:08, 30 June 2011

Pythagorean triples is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

A Pythagorean triple is defined as three positive integers where , and They are called primitive triples if are coprime, that is, if their pairwise greatest common divisors . Because of their relationship through the Pythagorean theorem, a, b, and c are coprime if a and b are coprime (). Each triple forms the length of the sides of a right triangle, whose perimeter is .

Task

The task is to determine how many Pythagorean triples there are with a perimeter no larger than 100 and the number of these that are primitive.

Extra credit: Deal with large values. Can your program handle a max perimeter of 1,000,000? What about 10,000,000? 100,000,000?

Note: the extra credit is not for you to demonstrate how fast your language is compared to others; you need a proper algorithm to solve them in a timely manner.

Cf

C

Sample implemention; naive method, patentedly won't scale to larger numbers, despite the attempt to optimize it. Calculating up to 10000 is already a test of patience. <lang C>#include <stdio.h>

  1. include <stdlib.h>

typedef unsigned long long xint; typedef unsigned long ulong;

inline ulong gcd(ulong m, ulong n) { ulong t; while (n) { t = n; n = m % n; m = t; } return m; }

int main() { ulong a, b, c, pytha = 0, prim = 0, max_p = 100; xint aa, bb, cc;

for (a = 1; a <= max_p / 3; a++) { aa = (xint)a * a; printf("a = %lu\r", a); /* show that we are working */ fflush(stdout);

/* max_p/2: valid limit, because one side of triangle * must be less than the sum of the other two */ for (b = a + 1; b < max_p/2; b++) { bb = (xint)b * b; for (c = b + 1; c < max_p/2; c++) { cc = (xint)c * c; if (aa + bb < cc) break; if (a + b + c > max_p) break;

if (aa + bb == cc) { pytha++; if (gcd(a, b) == 1) prim++; } } } }

printf("Up to %lu, there are %lu triples, of which %lu are primitive\n", max_p, pytha, prim);

return 0; }</lang>output:<lang>Up to 100, there are 17 triples, of which 7 are primitive</lang> Efficient method, generating primitive triples only as described in the same WP article:<lang C>#include <stdio.h>

  1. include <stdlib.h>
  2. include <stdint.h>

/* should be 64-bit integers if going over 1 billion */ typedef unsigned long xint;

  1. define FMT "%lu"

xint total, prim, max_peri; xint U[][9] = {{ 1, -2, 2, 2, -1, 2, 2, -2, 3}, { 1, 2, 2, 2, 1, 2, 2, 2, 3}, {-1, 2, 2, -2, 1, 2, -2, 2, 3}};

void new_tri(xint in[]) { int i; xint t[3], p = in[0] + in[1] + in[2];

if (p > max_peri) return;

prim ++;

/* for every primitive triangle, its multiples would be right-angled too; * count them up to the max perimeter */ total += max_peri / p;

/* recursively produce next tier by multiplying the matrices */ for (i = 0; i < 3; i++) { t[0] = U[i][0] * in[0] + U[i][1] * in[1] + U[i][2] * in[2]; t[1] = U[i][3] * in[0] + U[i][4] * in[1] + U[i][5] * in[2]; t[2] = U[i][6] * in[0] + U[i][7] * in[1] + U[i][8] * in[2]; new_tri(t); } }

int main() { xint seed[3] = {3, 4, 5};

for (max_peri = 10; max_peri <= 100000000; max_peri *= 10) { total = prim = 0; new_tri(seed);

printf( "Up to "FMT": "FMT" triples, "FMT" primitives.\n", max_peri, total, prim); } return 0; }</lang>Output<lang>Up to 10: 0 triples, 0 primitives. Up to 100: 17 triples, 7 primitives. Up to 1000: 325 triples, 70 primitives. Up to 10000: 4858 triples, 703 primitives. Up to 100000: 64741 triples, 7026 primitives. Up to 1000000: 808950 triples, 70229 primitives. Up to 10000000: 9706567 triples, 702309 primitives. Up to 100000000: 113236940 triples, 7023027 primitives.</lang>

Icon and Unicon

This uses the elegant formula (#IV) from Formulas for generating Pythagorean triples

<lang Icon> link numbers link printf

procedure main(A) # P-triples

  plimit := (0 < integer(\A[1])) | 100 # get perimiter limit
  
  nonprimitiveS := set()  # record unique non-primitives triples
  primitiveS := set()     # record unique primitive triples
  
  u :=  0
  while (g := (u +:= 1)^2) + 3 * u + 2 < plimit / 2 do {
     every v := seq(1) do {
        a := g + (i := 2*u*v)
        b := (h := 2*v^2) + i
        c := g + h + i
        if (p := a + b + c) > plimit then break 
        
        insert( (gcd(u,v)=1 & u%2=1, primitiveS) | nonprimitiveS, memo(a,b,c)) 
        every k := seq(2) do {      # k is for larger non-primitives          
           if k*p > plimit then break      
           insert(nonprimitiveS,memo(a*k,b*k,c*k) )
           }
        }
     }
     

printf("Under perimiter=%d: Pythagorean Triples=%d including primitives=%d\n",

      plimit,*nonprimitiveS+*primitiveS,*primitiveS) 
      

every put(gcol := [] , &collections) printf("Time=%d, Collections: total=%d string=%d block=%d",&time,gcol[1],gcol[3],gcol[4]) end


procedure memo(x[]) #: return a csv string of arguments in sorted order every (s := "") ||:= !sort(x) do s ||:= "," return s[1:-1] end</lang>

numbers.icn provides gcd printf.icn provides printf

The output from some sample runs with BLKSIZE=500000000 and STRSIZE=50000000 is below. It starts getting very slow after 10M at about 9 minutes (times are shown in ms. I suspect there may be faster gcd algorithms that would speed this up.

Output:

Under perimiter=10: Pythagorean Triples=0 including primitives=0
Time=3, Collections: total=0 string=0 block=0

Under perimiter=100: Pythagorean Triples=17 including primitives=7
Time=3, Collections: total=0 string=0 block=0

Under perimiter=1000: Pythagorean Triples=325 including primitives=70
Time=6, Collections: total=0 string=0 block=0

Under perimiter=10000: Pythagorean Triples=4858 including primitives=703
Time=57, Collections: total=0 string=0 block=0

Under perimiter=100000: Pythagorean Triples=64741 including primitives=7026
Time=738, Collections: total=0 string=0 block=0

Under perimiter=1000000: Pythagorean Triples=808950 including primitives=70229
Time=12454, Collections: total=0 string=0 block=0

Under perimiter=10000000: Pythagorean Triples=9706567 including primitives=702309
Time=560625, Collections: total=16 string=8 block=8

J

Brute force approach:

<lang j>pytr=: 3 :0

 r=. i. 0 3
 for_a. 1 + i. <.(y-1)%3 do.
   b=. 1 + a + i. <.(y%2)-3*a%2
   c=. a +&.*: b
   keep=. (c = <.c) *. y >: a+b+c
   if. 1 e. keep do.
     r=. r, a,.b ,.&(keep&#) c 
   end.
 end.
 (,.~ prim"1)r

)

prim=: 1 = 2 +./@{. |:</lang>

Example use:

First column indicates whether the triple is primitive, and the remaining three columns are a, b and c.

<lang j> pytr 100 1 3 4 5 1 5 12 13 0 6 8 10 1 7 24 25 1 8 15 17 0 9 12 15 1 9 40 41 0 10 24 26 0 12 16 20 1 12 35 37 0 15 20 25 0 15 36 39 0 16 30 34 0 18 24 30 1 20 21 29 0 21 28 35 0 24 32 40

  (# , [: {. +/) pytr 10

0 0

  (# , [: {. +/) pytr 100

17 7

  (# , [: {. +/) pytr 1000

325 70

  (# , [: {. +/) pytr 10000

4858 703</lang>

pytr 10000 takes 4 seconds on this laptop, and time to complete grows with square of perimeter, so pytr 1e6 should take something like 11 hours using this algorithm on this machine.

A slightly smarter approach:

<lang j>trips=:3 :0

 'm n'=. |:(#~ 1 = 2 | +/"1)(#~ >/"1) ,/ ,"0/~ }. i. <. %: y
 prim=. (#~ 1 = 2 +./@{. |:) (#~ y >: +/"1)m (-&*: ,. +:@* ,. +&*:) n
 /:~ ; <@(,.~ # {. 1:)@(*/~ 1 + y i.@<.@% +/)"1 prim

)</lang>

usage for trips is the same as for pytr. Thus:

<lang j> (# , 1 {. +/) trips 10 0 0

  (# , 1 {. +/) trips 100

17 7

  (# , 1 {. +/) trips 1000

325 70

  (# , 1 {. +/) trips 10000

4858 703

  (# , 1 {. +/) trips 100000

64741 7026

  (# , 1 {. +/) trips 1000000

808950 70229

  (# , 1 {. +/) trips 10000000

9706567 702309</lang>

The last line took about 16 seconds.

Java

Theoretically, this can go "forever", but it takes a while, so only the minimum is shown. Luckily, BigInteger has a GCD method built in.

<lang java> import java.math.BigInteger; import static java.math.BigInteger.ONE;

public class PythTrip{

   public static void main(String[] args){
       long tripCount = 0, primCount = 0;
       //change this to whatever perimeter limit you want;the RAM's the limit
       BigInteger periLimit = BigInteger.valueOf(100),
               peri2 = periLimit.divide(BigInteger.valueOf(2)),
               peri3 = periLimit.divide(BigInteger.valueOf(3));
       for(BigInteger a = ONE; a.compareTo(peri3) < 0; a = a.add(ONE)){
           BigInteger aa = a.multiply(a);
           
           for(BigInteger b = a.add(ONE);
                   b.compareTo(peri2) < 0; b = b.add(ONE)){
               BigInteger bb = b.multiply(b);
               BigInteger ab = a.add(b);
               BigInteger aabb = aa.add(bb);
               
               for(BigInteger c = b.add(ONE);
                       c.compareTo(peri2) < 0; c = c.add(ONE)){
                   int compare = aabb.compareTo(c.multiply(c));
                   //if a+b+c > periLimit
                   if(ab.add(c).compareTo(periLimit) > 0){
                       break;
                   }
                   //if a^2 + b^2 != c^2
                   if(compare < 0){
                       break;
                   }else if (compare == 0){
                       tripCount++;
                       System.out.print(a + ", " + b + ", " + c);
                       //does binary GCD under the hood
                       if(a.gcd(b).equals(ONE)){
                           System.out.print(" primitive");
                           primCount++;
                       }
                       System.out.println();
                   }
               }
           }
       }
       System.out.println("Up to a perimeter of " + periLimit + ", there are "
               + tripCount + " triples, of which " + primCount + " are primitive.");
   }

}</lang> Output:

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

Python

Two methods, the second of which is much faster <lang python>from fractions import gcd


def pt1(maxperimeter=100):

   
  1. Naive method
   
   trips = []
   for a in range(1, maxperimeter):
       aa = a*a
       for b in range(a, maxperimeter-a+1):
           bb = b*b
           for c in range(b, maxperimeter-b-a+1):
               cc = c*c
               if a+b+c > maxperimeter or cc > aa + bb: break
               if aa + bb == cc:
                   trips.append((a,b,c, gcd(a, b) == 1))
   return trips

def pytrip(trip=(3,4,5),perim=100, prim=1):

   a0, b0, c0 = a, b, c = sorted(trip)
   t, firstprim = set(), prim>0
   while a + b + c <= perim:
       t.add((a, b, c, firstprim>0))
       a, b, c, firstprim = a+a0, b+b0, c+c0, False
   #
   t2 = set()
   for a, b, c, firstprim in t:
       a2, a5, b2, b5, c2, c3, c7 = a*2, a*5, b*2, b*5, c*2, c*3, c*7
       if  a5 - b5 + c7 <= perim:
           t2 |= pytrip(( a - b2 + c2,  a2 - b + c2,  a2 - b2 + c3), perim, firstprim)
       if  a5 + b5 + c7 <= perim:
           t2 |= pytrip(( a + b2 + c2,  a2 + b + c2,  a2 + b2 + c3), perim, firstprim)
       if -a5 + b5 + c7 <= perim:
           t2 |= pytrip((-a + b2 + c2, -a2 + b + c2, -a2 + b2 + c3), perim, firstprim)
   return t | t2

def pt2(maxperimeter=100):

   
  1. Parent/child relationship method:
  2. http://en.wikipedia.org/wiki/Formulas_for_generating_Pythagorean_triples#XI.
   
   trips = pytrip((3,4,5), maxperimeter, 1)
   return trips

def printit(maxperimeter=100, pt=pt1):

   trips = pt(maxperimeter)
   print("  Up to a perimeter of %i there are %i triples, of which %i are primitive"
         % (maxperimeter,
            len(trips),
            len([prim for a,b,c,prim in trips if prim])))
 

for algo, mn, mx in ((pt1, 250, 2500), (pt2, 500, 20000)):

   print(algo.__doc__)
   for maxperimeter in range(mn, mx+1, mn):
       printit(maxperimeter, algo)

</lang>

Output
# Naive method
    
  Up to a perimeter of 250 there are 56 triples, of which 18 are primitive
  Up to a perimeter of 500 there are 137 triples, of which 35 are primitive
  Up to a perimeter of 750 there are 227 triples, of which 52 are primitive
  Up to a perimeter of 1000 there are 325 triples, of which 70 are primitive
  Up to a perimeter of 1250 there are 425 triples, of which 88 are primitive
  Up to a perimeter of 1500 there are 527 triples, of which 104 are primitive
  Up to a perimeter of 1750 there are 637 triples, of which 123 are primitive
  Up to a perimeter of 2000 there are 744 triples, of which 140 are primitive
  Up to a perimeter of 2250 there are 858 triples, of which 156 are primitive
  Up to a perimeter of 2500 there are 969 triples, of which 175 are primitive

# Parent/child relationship method:
# http://en.wikipedia.org/wiki/Formulas_for_generating_Pythagorean_triples#XI.
    
  Up to a perimeter of 500 there are 137 triples, of which 35 are primitive
  Up to a perimeter of 1000 there are 325 triples, of which 70 are primitive
  Up to a perimeter of 1500 there are 527 triples, of which 104 are primitive
  Up to a perimeter of 2000 there are 744 triples, of which 140 are primitive
  Up to a perimeter of 2500 there are 969 triples, of which 175 are primitive
  Up to a perimeter of 3000 there are 1204 triples, of which 211 are primitive
  Up to a perimeter of 3500 there are 1443 triples, of which 245 are primitive
  Up to a perimeter of 4000 there are 1687 triples, of which 280 are primitive
  Up to a perimeter of 4500 there are 1931 triples, of which 314 are primitive
  Up to a perimeter of 5000 there are 2184 triples, of which 349 are primitive
  Up to a perimeter of 5500 there are 2442 triples, of which 385 are primitive
  Up to a perimeter of 6000 there are 2701 triples, of which 422 are primitive
  Up to a perimeter of 6500 there are 2963 triples, of which 457 are primitive
  Up to a perimeter of 7000 there are 3224 triples, of which 492 are primitive
  Up to a perimeter of 7500 there are 3491 triples, of which 527 are primitive
  Up to a perimeter of 8000 there are 3763 triples, of which 560 are primitive
  Up to a perimeter of 8500 there are 4029 triples, of which 597 are primitive
  Up to a perimeter of 9000 there are 4304 triples, of which 631 are primitive
  Up to a perimeter of 9500 there are 4577 triples, of which 667 are primitive
  Up to a perimeter of 10000 there are 4858 triples, of which 703 are primitive
  Up to a perimeter of 10500 there are 5138 triples, of which 736 are primitive
  Up to a perimeter of 11000 there are 5414 triples, of which 770 are primitive
  Up to a perimeter of 11500 there are 5699 triples, of which 804 are primitive
  Up to a perimeter of 12000 there are 5980 triples, of which 839 are primitive
  Up to a perimeter of 12500 there are 6263 triples, of which 877 are primitive
  Up to a perimeter of 13000 there are 6559 triples, of which 913 are primitive
  Up to a perimeter of 13500 there are 6843 triples, of which 949 are primitive
  Up to a perimeter of 14000 there are 7129 triples, of which 983 are primitive
  Up to a perimeter of 14500 there are 7420 triples, of which 1019 are primitive
  Up to a perimeter of 15000 there are 7714 triples, of which 1055 are primitive
  Up to a perimeter of 15500 there are 8004 triples, of which 1089 are primitive
  Up to a perimeter of 16000 there are 8304 triples, of which 1127 are primitive
  Up to a perimeter of 16500 there are 8595 triples, of which 1159 are primitive
  Up to a perimeter of 17000 there are 8884 triples, of which 1192 are primitive
  Up to a perimeter of 17500 there are 9189 triples, of which 1228 are primitive
  Up to a perimeter of 18000 there are 9484 triples, of which 1264 are primitive
  Up to a perimeter of 18500 there are 9791 triples, of which 1301 are primitive
  Up to a perimeter of 19000 there are 10088 triples, of which 1336 are primitive
  Up to a perimeter of 19500 there are 10388 triples, of which 1373 are primitive
  Up to a perimeter of 20000 there are 10689 triples, of which 1408 are primitive

Barebone minimum for this task:<lang Python>from sys import setrecursionlimit setrecursionlimit(2000) # 2000 ought to be big enough for everybody

def triples(lim, a = 3, b = 4, c = 5): l = a + b + c if l > lim: return (0, 0) return reduce(lambda x, y: (x[0] + y[0], x[1] + y[1]), [ (1, lim / l), triples(lim, a - 2*b + 2*c, 2*a - b + 2*c, 2*a - 2*b + 3*c), triples(lim, a + 2*b + 2*c, 2*a + b + 2*c, 2*a + 2*b + 3*c), triples(lim, -a + 2*b + 2*c, -2*a + b + 2*c, -2*a + 2*b + 3*c) ])

for peri in [10 ** e for e in range(1, 8)]: print peri, triples(peri)</lang>Output:<lang>10 (0, 0) 100 (7, 17) 1000 (70, 325) 10000 (703, 4858) 100000 (7026, 64741) 1000000 (70229, 808950) 10000000 (702309, 9706567)</lang>