nested for loop

Collapse
This topic is closed.
X
X
 
  • Time
  • Show
Clear All
new posts
  • Wolfgang Buechel

    nested for loop

    Hi,

    I want to iterate over all 2x2 matrices with elements in range 0..25
    (crypto-stuff).

    To produce them, first I wrote a fourfold nested for loop:

    M=26
    for a in range(M):
    for b in range (M):
    for c in range (M):
    for d in range (M):
    matr = [[a,b],[c,d]]
    (dosomething)

    Then I had a look in comp.lang.pytho n and found:


    for (a,b,c,d) in [(x,y,z,t) for x in range(M)
    for y in range(M)
    for z in range(M)
    for t in range(M)] :
    matr = [[a,b],[c,d]]

    Is there a shorter (and probably, with respect to exec time, faster)
    way to write such a 4for loop?
    (I want to scan 3x3, 4x4 matrices too (;-)

    -- Wolfgang
  • Tor Iver Wilhelmsen

    #2
    Re: nested for loop

    wjb131@web.de (Wolfgang Buechel) writes:
    [color=blue]
    > for (a,b,c,d) in [(x,y,z,t) for x in range(M)
    > for y in range(M)
    > for z in range(M)
    > for t in range(M)] :
    > matr = [[a,b],[c,d]][/color]

    Try assigning range(M) to a variable instead of creating four copies
    of it. For larger ranges, experiment using xrange instead.

    Comment

    • Peter Hansen

      #3
      Re: nested for loop

      Wolfgang Buechel wrote:
      [color=blue]
      > I want to iterate over all 2x2 matrices with elements in range 0..25
      > (crypto-stuff).
      >
      > To produce them, first I wrote a fourfold nested for loop:
      >
      > M=26
      > for a in range(M):
      > for b in range (M):
      > for c in range (M):
      > for d in range (M):
      > matr = [[a,b],[c,d]]
      > (dosomething)
      >
      > Then I had a look in comp.lang.pytho n and found:
      >
      >
      > for (a,b,c,d) in [(x,y,z,t) for x in range(M)
      > for y in range(M)
      > for z in range(M)
      > for t in range(M)] :
      > matr = [[a,b],[c,d]]
      >
      > Is there a shorter (and probably, with respect to exec time, faster)
      > way to write such a 4for loop?[/color]

      Hmm... why would you want something shorter, as it would
      probably be less readable? Also, how fast do you need it
      to run, or how many times faster than the above would you
      like it to run?

      (The second one is a facetious question... the first is
      more serious. In effect: what's wrong with what you have?)

      -Peter

      Comment

      • Terry Reedy

        #4
        Re: nested for loop


        "Wolfgang Buechel" <wjb131@web.d e> wrote in message
        news:4629559b.0 405101330.286dd b32@posting.goo gle.com...[color=blue]
        > Hi,
        >
        > I want to iterate over all 2x2 matrices with elements in range 0..25
        > (crypto-stuff).
        >
        > To produce them, first I wrote a fourfold nested for loop:
        >
        > M=26
        > for a in range(M):
        > for b in range (M):
        > for c in range (M):
        > for d in range (M):
        > matr = [[a,b],[c,d]]
        > (dosomething)[/color]

        This is completely clear and space efficient.
        [color=blue]
        > Then I had a look in comp.lang.pytho n and found:[/color]

        Oh dear...
        [color=blue]
        > for (a,b,c,d) in [(x,y,z,t) for x in range(M)
        > for y in range(M)
        > for z in range(M)
        > for t in range(M)] :
        > matr = [[a,b],[c,d]][/color]

        This is less clear. It took me about 10 seconds (versus 1) to see as
        equivalent. It produces a list with M**4 elements that you don't actually
        need and soon throw away.
        [color=blue]
        > Is there a shorter (and probably, with respect to exec time, faster)
        > way to write such a 4for loop?[/color]

        Write a C extension, maybe with Pyrex. However, an hour of your time is
        worth lots of hours of PC time. But for a million runs, it might be worth
        it.

        Terry J. Reedy




        Comment

        • Sean Ross

          #5
          Re: nested for loop

          "Wolfgang Buechel" <wjb131@web.d e> wrote in message
          news:4629559b.0 405101330.286dd b32@posting.goo gle.com...[color=blue]
          > Hi,
          >
          > I want to iterate over all 2x2 matrices with elements in range 0..25
          > (crypto-stuff).[/color]
          [snip][color=blue]
          > Is there a shorter (and probably, with respect to exec time, faster)
          > way to write such a 4for loop?
          > (I want to scan 3x3, 4x4 matrices too (;-)
          >
          > -- Wolfgang[/color]

          Hi.

          The following code isn't necessarily shorter or faster (or more readable),
          but it's a bit more general:

          # slightly modified code from

          def sequences(n, things):
          "generates sequences of n items from a set of things"
          if n == 0:
          yield []
          else:
          for x in things:
          for y in sequences(n-1, things):
          yield [x] + y

          def nXn_matrices(n, elements):
          "generates nXn matrices from elements"
          for s in sequences(n*n, elements):
          yield [s[i*n:(i+1)*n] for i in xrange(n)]



          # we'll try it over a small range ...
          M = 3
          for m in nXn_matrices(2, range(M)):
          print m

          Output:
          [[0, 0], [0, 0]]
          [[0, 0], [0, 1]]
          [[0, 0], [0, 2]]
          [[0, 0], [1, 0]]
          [[0, 0], [1, 1]]
          [[0, 0], [1, 2]]
          [[0, 0], [2, 0]]
          ....
          ....
          [[2, 2], [2, 0]]
          [[2, 2], [2, 1]]
          [[2, 2], [2, 2]]



          # now 3X3 ... this takes a _l_o_n_g_ time ...
          M = 3
          for m in nXn_matrices(3, range(M)):
          print m

          Output:
          [[0, 0, 0], [0, 0, 0], [0, 0, 0]]
          [[0, 0, 0], [0, 0, 0], [0, 0, 1]]
          [[0, 0, 0], [0, 0, 0], [0, 0, 2]]
          [[0, 0, 0], [0, 0, 0], [0, 1, 0]]
          [[0, 0, 0], [0, 0, 0], [0, 1, 1]]
          [[0, 0, 0], [0, 0, 0], [0, 1, 2]]
          ....
          ....
          [[2, 2, 2], [2, 2, 2], [2, 1, 0]]
          [[2, 2, 2], [2, 2, 2], [2, 1, 1]]
          [[2, 2, 2], [2, 2, 2], [2, 1, 2]]
          [[2, 2, 2], [2, 2, 2], [2, 2, 0]]
          [[2, 2, 2], [2, 2, 2], [2, 2, 1]]
          [[2, 2, 2], [2, 2, 2], [2, 2, 2]]



          I'm believe there are several opportunities for optimization both in the
          code and in the algorithm (for instance, it may be possible to take
          advantage of repetition in the sub-matrices), but I won't be trying that
          now.

          Good luck with what you're doing,
          Sean


          Comment

          • Sean Ross

            #6
            Re: nested for loop


            "Sean Ross" <sross@connectm ail.carleton.ca > wrote in message
            news:VDWnc.2167 9$FH5.581779@ne ws20.bellglobal .com...
            [snip][color=blue]
            > # slightly modified code from
            > http://twistedmatrix.com/wiki/python/PostYourCode
            > def sequences(n, things):
            > "generates sequences of n items from a set of things"
            > if n == 0:
            > yield []
            > else:
            > for x in things:
            > for y in sequences(n-1, things):
            > yield [x] + y
            >
            > def nXn_matrices(n, elements):
            > "generates nXn matrices from elements"
            > for s in sequences(n*n, elements):
            > yield [s[i*n:(i+1)*n] for i in xrange(n)][/color]
            [snip][color=blue]
            > [I] believe there are several opportunities for optimization both in the
            > code and in the algorithm (for instance, it may be possible to take
            > advantage of repetition in the sub-matrices), but I won't be trying that
            > now.[/color]

            Looks like I'll be trying it now after all:

            def nXn_matrices2(n , elements):
            for m in sequences(n, list(sequences( n, elements))):
            yield m

            This is slightly faster than the first version, but it has more overhead
            since it builds a list of the submatrices. That could be problem. It can
            probably be avoided, but I haven't figured out how to do it just yet. We'll
            see what happens.

            Sean



            Comment

            • Anton Vredegoor

              #7
              Re: nested for loop

              wjb131@web.de (Wolfgang Buechel) wrote:
              [color=blue]
              >I want to iterate over all 2x2 matrices with elements in range 0..25
              >(crypto-stuff).
              >
              >To produce them, first I wrote a fourfold nested for loop:
              >
              >M=26
              > for a in range(M):
              > for b in range (M):
              > for c in range (M):
              > for d in range (M):
              > matr = [[a,b],[c,d]]
              > (dosomething)[/color]

              [snip]
              [color=blue]
              >Is there a shorter (and probably, with respect to exec time, faster)
              >way to write such a 4for loop?
              >(I want to scan 3x3, 4x4 matrices too (;-)[/color]

              This question is very much related to the one in a thread below here
              (titled "Inverse of int(s, base)?"). In fact with a small adaptation
              that code can produce this kind of matrices:

              from itertools import islice

              def tobase(i,base,d igits):
              R = []
              for j in xrange(digits):
              i,k = divmod(i,base)
              R.append(k)
              R.reverse()
              return R

              def as_matrix(R,row s,cols):
              it = iter(R)
              return[list(islice(it, cols)) for i in xrange(rows)]

              def test():
              for i in range(10):
              R = tobase(i,25,6)
              print as_matrix(R,3,2 )

              if __name__=='__ma in__':
              test()

              Anton

              Comment

              • Wolfgang Buechel

                #8
                Re: nested for loop

                wjb131@web.de (Wolfgang Buechel) wrote in message news:<4629559b. 0405101330.286d db32@posting.go ogle.com>...[color=blue]
                > Hi,
                >
                > I want to iterate over all 2x2 matrices with elements in range 0..25
                > (crypto-stuff).[/color]

                Try generators (new in Python 2.3.3).
                There is an example you can/must adapt:
                Look at your Python installation:

                (PythonPath)/Lib/test/test_generators .py
                def conjoin()

                and in the whatsnew-documentation:



                --W.

                Comment

                • Peter Otten

                  #9
                  Re: nested for loop

                  Dan Bishop wrote:
                  [color=blue]
                  > What you can do to make your code faster (if you don't change matr
                  > once it's created) is to precompute the 676 possible matrix rows.
                  >
                  > ELEMENT_RANGE = range(26)
                  > MATRIX_ROWS = [[x, y] for x in ELEMENT_RANGE
                  > for y in ELEMENT_RANGE]
                  > for row1 in MATRIX_ROWS:
                  > for row2 in MATRIX_ROWS:
                  > matr = [row1, row2]
                  >
                  > That takes only 532 ms -- almost 3 times faster than the original.[/color]

                  Nice. Another speed gain (from 435 to 246ms on my machine) is in for you if
                  you use tuples instead of lists. And if you allow for somewhat less elegant
                  code that builds on your recipe,

                  from itertools import izip, repeat
                  ELEMENT_RANGE = range(26)
                  MATRIX_ROWS = [(x, y) for x in ELEMENT_RANGE
                  for y in ELEMENT_RANGE]
                  for row in MATRIX_ROWS:
                  for matr in izip(repeat(row ), MATRIX_ROWS):
                  pass

                  you can bring that down to 138ms.

                  For the record: the straightforward solution (the original with tuples and
                  range() factored out)

                  r = range(26)
                  for a in r:
                  for b in r:
                  for c in r:
                  for d in r:
                  matr = ((a,b),(c,d))

                  takes 478ms. The "improved" variant is evil performance-wise (1598ms):

                  r = range(26)
                  for (a,b,c,d) in [(x,y,z,t) for x in r
                  for y in r
                  for z in r
                  for t in r] :
                  matr = ((a,b),(c,d))

                  It might be interesting how much this can be improved with 2.4's generator
                  expressions.

                  Peter

                  Comment

                  Working...