views:

1091

answers:

11

hi, I want to calaculate pythagorean triplets(code below) and I want to calculate infinitly how do I do it without using the three for loops? Could I use a for loop in some way? thanks.

import math

def main():
    for x in range (10000, 1000):
     for y in range (10000, 1000):
      for z in range(10000, 1000):
       if x*x == y*y + z*z:
        print y, z, x
        print '-'*50

if __name__ == '__main__':
    main()
+7  A: 

Generally, you can't. Three variables, three loops.

But this is a special case, as nobody pointed out. You can solve this problem with two loops.

Also, there's no point in checking y, z and z, y.

Oh, and range(10000, 1000) = [].

import math

for x in range(1, 1000):
  for y in range(x, 1000):
      z = math.sqrt(x**2 + y**2)
      if int(z) == z:
        print x, y, int(z)
        print '-'*50
Can Berk Güder
+1: for must use some kind of iterable; sequence or generator.
S.Lott
Yeah, that's what I was getting at, and what I would have written if I knew more Python.
Kyle Cronin
A: 

Besides what has already been posted, people would expect three loops for three collections. Anything else may get very confusing and provide no added benefit.

Ed Swangren
+3  A: 

You would only need two loops - just check to see if math.sqrt(x*x+y*y) is an integer. If it is, you've discovered a pythagorean triple.

I'm new to Python, so I don't know what range(10000, 1000) does - where does it start and stop? I ask because you can halve your runtime by having the range for y start at x instead of fixing it, due to the fact that addition is commutative.

edit: This answer is what I was getting at, and what I would have written if I knew more Python.

Kyle Cronin
+4  A: 

You can arrange your code in a single main loop like this:

MIN = 10000
MAX = 10010
a = [MIN, MIN, MIN]
while True:
    print a
    for i in range(len(a)):
        a[i] = a[i] + 1
        if a[i] < MAX:
            break
        a[i] = MIN
        i += 1
    else:
        break

Instead of the print a, you can do your Pythagorean triplet test there. This will work for an arbitrary number of dimensions.

If you really want to do this infinitely, you will have to use a different iteration technique such as diagonalization.

Greg Hewgill
+1 diagonalization---I wanted to be the one to answer that :(
zweiterlinde
+1  A: 

Not the most efficient (Python will build an array with a billion tuples), but this is a single loop:

for x, y, z in [(x, y, z) for x in range(10000, 11000) for y in range(10000, 11000) for z in range(10000, 11000)]:
    if x*x == y*y + z*z:
        print y, z, x
        print '-'*50

Or, as suggested by Christian Witts,

for x, y, z in ((x, y, z) for x in xrange(10000, 11000) for y in xrange(10000, 11000) for z in xrange(10000, 11000)):
    if x*x == y*y + z*z:
        print y, z, x
        print '-'*50

(assuming Python >= 2.4) uses generators instead of building a billion-tuple array.

Either way, you shouldn't code like this... Your initial code with nested loops is clearer.

ephemient
change that to "for x, y, z in ((x, y, z) for x in xrange(10000, 11000) for y in xrange(10000, 11000) for z in xrange(10000, 11000)):"for a generator expression instead and change range to xrange unless you're already using Py3.0
Christian Witts
+2  A: 

Using xrange instead of range should use less memory, especially if you want to try large ranges.

Jacob Gabrielson
In recent versions of Python, they're the same
DNS
True, although AFAICT that's in Python 3.0 which I assume most folks aren't using yet.
Jacob Gabrielson
But one shouldn't optimize if it's not necessary.
Georg
Well, the original poster seemed like he wanted to do larger ranges if he could, hence my suggestion. Take it for what it's worth :-)
Jacob Gabrielson
+1  A: 

This is the same as Can Berk Guder's answer, but done as a generator, just for fun. It's not really useful with the nested loops here, but it can often be a cleaner solution. Your function produces results; you worry later about how many to retrieve.

import math

def triplets(limit):
    for x in range(1, limit):
        for y in range(x, limit):
            z = math.sqrt(x**2 + y**2)
            if int(z) == z:
                yield x, y, int(z)

for x, y, z in triplets(10):
    print x, y, z
    print "-" * 50
DNS
A: 

If you want to count to infinity ..

Create a generator function that counts from zero and never stops, and use the for loop on it

def inf():
   i = 0
   while True:
     yield i
     i = i + 1

for i in inf():
    print i  # or do whatever you want!

I don't know if there's already such a builtin function

hasen j
Yes, it's itertools.count().
Kiv
+1  A: 

Using the same algorithm (see the other answers for better approaches), you can use itertools.count to get a loop that runs forever.

import itertools

for x in itertools.count(1):
    for y in xrange(1, x):
         for z in xrange(1, y):
              if x*x == y*y + z*z:
                  print x, y, z
cthulahoops
+2  A: 

Here's an efficient version, using iterators, that generates all such triples, in order. The trick here is to iterate up through the sets of (x,y) pairs that sum to N, for all N.

import math
import itertools

def all_int_pairs():
    "generate all pairs of positive integers"
    for n in itertools.count(1):
        for x in xrange(1,n/2+1):
            yield x,n-x

for x,y in all_int_pairs():
    z = math.sqrt(x**2 + y**2)
    if int(z) == z:
        print x, y, int(z)
        print '-'*50
A: 

At least three loops are needed for infinity. To make something flexible takes a ton of loops. This example is a solution to Project Euler Problem 9 and more.

#!/usr/bin/env python

def fcount(start=1):
    n = float(start)
    while True:
        yield n
        n += 1

def find_triples():
    for c in fcount():
        for b in fcount():
            if b > c:
                break
            for a in fcount():
                if a > b:
                    break
                if a ** 2 + b ** 2 == c ** 2:
                    yield (a, b, c)

def triples_by_sum(targetsum):
    for a, b, c in find_triples():
        if a + b + c == targetsum:
            yield a, b, c
        if c > targetsum:
            break

if __name__ == '__main__':
    # Finds multiple triples
    for a, b, c in triples_by_sum(252):
        print a, b, c
    # Finds single triples
    a, b, c = triples_by_sum(1000).next()
    print a, b, c
    # Goes forever
    for a, b, c in find_triples():
        print a, b, c