3
def mot (n) :
  if n==0 : 
    yield n
  else :
    for m in mot(n-1) :
      yield [m]
    for k in range(0,n-1) :    
      for l in mot(k) :
        for r in mot(n-2-k) :        
          yield (l,r)

def countFor(f,n) :
  for i in range(n) :
    count = 0
    for t in f(i) : 
      count+=1
    yield count

def countsFor(mes,f,n) :
  print(mes)
  print([c for c in countFor(f,n)])
  print("")

def showFor(mes,f,n) :
  print(mes)
  for t in f(n) :
    print(t)
  print("")


showFor('Motzkin trees',mot,4)
countsFor('Motzkin trees',mot,12)
print("done")

def test() :
  for n in range(6) :
    print(n,list(mot(n)))  

I have the following code that outputs the motzkin numbers, I want to change the yield expressions to another, more simple expressions or functions, how can I do that , what can I do? thanks

Paul Rooney
  • 20,879
  • 9
  • 40
  • 61
  • I am getting dizzy from the code, you want to simplify it, i can't to hard fro me, haha. – U13-Forward Sep 12 '18 at 01:34
  • You are recalculating the multiple sub-branches multiple times, which isn't very efficient this looks like a good candidate for dynamic programming. – AChampion Sep 12 '18 at 01:48

3 Answers3

1

Getting rid of yield from a generator function that generates a finite sequence is as easy as appending the yielded values to a list for return instead.

For example, your mot function can be revised without yield as:

def mot(n) :
  output = []
  if n==0 :
    output.append(n)
  else :
    for m in mot(n-1) :
      output.append([m])
    for k in range(0,n-1) :
      for l in mot(k) :
        for r in mot(n-2-k) :
          output.append((l,r))
  return output

But unless the caller needs to perform index-based operations with the returning list, there's no need to convert the function so it returns a list, as generators are both faster and more memory-efficient.

blhsing
  • 91,368
  • 6
  • 71
  • 106
0

According to wikipedia, the Moptzkin numbers satisfy this recurrence relation:

M_n = ((2n + 1)/(n + 2)) M_(n-1) + ((3n - 3)/(n+2)) M_(n-2)

That's easy enough to translate into code:

from itertools import count

def mot():
    M_n1 = 1
    M_n2 = 1
    yield 1
    yield 1
    for n in count(2):
        M = ((2*n + 1)/(n + 2))*M_n1 + ((3*n - 3)/(n+2))*M_n2
        M = int(M)
        yield M
        M_n1, M_n2 = M, M_n1

Now we can loop through the terms of the sequence until the numbers get too big for memory, or just slice a few from the front of the list:

from itertools import islice

print(list(islice(mot(), 10)))
# [1, 1, 2, 4, 9, 21, 51, 127, 323, 835]
Patrick Haugh
  • 59,226
  • 13
  • 88
  • 96
0

As a bit of dynamic programming:

def mot(t):
    M = [1, 1]
    for n in range(2, t+1):
        M.append(((2*n + 1)*M[n-1] + (3*n - 3)*M[n-2]) // (n + 2))
    return M

In []:    
mot(4)

Out[]:
[1, 1, 2, 4, 9]

In []:
mot(10)

Out[]:
[1, 1, 2, 4, 9, 21, 51, 127, 323, 835, 2188]
AChampion
  • 29,683
  • 4
  • 59
  • 75