0

So I'm trying to implement a pascal's triangle that produces the following in python:

pascal_triangle(5) prints:
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1

The problem is I'm trying to do it without using any type of loops but can't figure out how to do that. Any help would be appreciated. Than you.

This is what I have so far:

   def factorial(x):
            if x == 0:
                    return 1
            else: 
                    x * factorial(x - 1)

    def pascal_triangle(n):`

UPDATED:

print_pascal_line(r):
    if r == 0:
        return 1
    else:
        R = print_pascal_line(r-1)
        return 1 +
1

7 Answers 7

2

Each element of Pascal's triangle is evaluated using the binomial coefficient. This value, often referred to as nCr, asks "given n items how many ways can you Choose r things?"

Take, for example, the items a, b, and c. How many ways can we create combinations of the following sizes?

  1. There's only 1 way to choose 0 items: {}
  2. There are 3 possible combinations: {a}, {b}, or {c}
  3. Again, 3 ways: {a, b}, {a, c}, or {b, c}
  4. Only {a, b, c}

And what would you know, that just so happens to be level 3* of Pascal's triangle: 1 3 3 1! As it turns out, we can use this on every level.

0: nCr(0, 0)
1: nCr(1, 0) nCr(1, 1)
2: nCr(2, 0) nCr(2, 1) nCr(2, 2)
3: nCr(3, 0) nCr(3, 1) nCr(3, 2) nCr(3, 3)
etc
etc

So, how can we code for this? Looking at this answer we get our nCr function

In [454]: import functools as ft

In [455]: import operator as op

In [456]: def nCr(n, r):
     ...:     r = min(r, n-r)
     ...:     numer = ft.reduce(op.mul, range(n, n - r, -1), 1)
     ...:     denom = ft.reduce(op.mul, range(1, r + 1), 1)
     ...:     return numer // denom
     ...:

Finally, let's make a recursive function to tie it all together.

In [457]: def pascal(n):
     ...:     if n >= 1:
     ...:         pascal(n - 1)
     ...:         print(' '.join(str(nCr(n - 1, r)) for r in range(n)))
     ...:

In [463]: pascal(5)
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1

Technically, this is should be pascal(4) as Pascal's triangle is zero-indexed*, but I'm just going according to the OPs request. If we wanted to change this we would alter our pascal function to

In [468]: def pascal(n):
     ...:     if n >= 0:
     ...:         pascal(n - 1)
     ...:         print(' '.join(str(nCr(n, r)) for r in range(n + 1)))
     ...:
Sign up to request clarification or add additional context in comments.

2 Comments

Very nice answer. I learned something new about Pascal. The In [###]: prompts are distracting and make it difficult to copy/paste the code however.
The OP explicitly stated multiple times, "without using any loops". The expression, (str(nCr(n, r)) for r in range(n + 1)) is a loop!
1

A pure recursive solution (without loop, without assignment, without external modules, only python function used is sum which can be avoided as well). This code can be easily translated to LISP family language.

def pascal_line(n):
    def nextline(thisline):
        if thisline == []:
            return []
        else:
            return [sum(thisline[:2])] + nextline(thisline[1:])
    if n == 1:
        return [1]
    elif n == 2:
        return [1, 1]
    else:
        return [1]+nextline(pascal_line(n-1))

def pascal_triangle(n):
    def printline(m):
        if m <= n:
            print(*pascal_line(m))
            printline(m+1)
    return printline(1)

pascal_triangle(6)
# output =>
# 1
# 1 1
# 1 2 1
# 1 3 3 1
# 1 4 6 4 1
# 1 5 10 10 5 1

Inner function nextline derives the next line (without leading 1) in pascal triangle based on current line recursively.

Function pascal_line derives the nth line in a pascal triangle, by calling nextline recursively with (n-1)th line (its own previous solution).

Function pascal_triangle prints out lines in pascal triangle by calling pascal_line recursively.

Three recursive functions all together nicely illustrated the typical divide and conquer nature of recursive approach.

Comments

1

First create a function that prints the Nth line of the pascal triangle, and I advise you to use combinations instead of manually computing the values in each line using factorials, it would be much more efficient. Let's say this function is called print_pascal_line and receives an integer, the line number.

Then you just have:

def pascal_triangle(n):
    aux(0, n)

def aux(current_line, n):
    if current_line < n:
        print_pascal_line(current_line)
        aux(current_line + 1, n)

Or you can use default arguments to have this in one function only:

def pascal_triangle(n, current_line = 0):
    if current_line < n:
        print_pascal_line(current_line)
        pascal_triangle(n, current_line + 1)

2 Comments

thank you for the reply. here is my shot for the print_pascal_line: (and based on your explanation I believe this is only supposed to print that Nth line so for example if n=6, then it prints the 6th row) .
Yes, that is exactly what I mean. And by the way, here is something that will help you with your print_pascal_line function: scipy.special.comb
0

How about this?

def pascal_triangle(n, line=None):
    if n == 0: return
    if line is None: 
        line = [1]
    print(" ".join(map(str, line)))
    pascal_line(line)
    pascal_triangle(n-1, line)

def pascal_line(line, i=0, add=0):
    if i >= len(line):
        line.append(add)
        return
    add, line[i] = line[i], line[i] + add
    pascal_line(line, i+1, add)

1 Comment

this works aswell, however is there an alternate way of defining the parameters like line=None, i=0, and add=0, because I personally never learnt to do it this way.
0

I answered this question once before here. Follow the link for an explanation of how to design recursive functions like this one.

def pairs (xs):
  if 2 > len(xs):
    return []
  else:
    return [xs[0:2]] + pairs(xs[1:])

def pascal (n):
  def compute (prev):
    return [1] + [x + y for (x,y) in pairs(prev)] + [1]
  def aux (m, prev):
    if (m > n):
      return []
    else:
      return [prev] + aux(m + 1, compute(prev))
  return aux(1, [1])

for line in pascal(5):
  print(line)
# [1]
# [1, 1]
# [1, 2, 1]
# [1, 3, 3, 1]
# [1, 4, 6, 4, 1]

Above we create a new [1] singleton in three places; two of which are part of the compute loop. We should create it once and reuse it instead.

def pascal (n):
  one = [1]
  def compute (prev):
    return one + [x + y for (x,y) in pairs(prev)] + one
  def aux (m, prev):
    if (m > n):
      return []
    else:
      return [prev] + aux(m + 1, compute(prev))
  return aux(1, one)

A final improvement I might suggest is to use a generator instead of returning all of the rows eagerly

def pascal (n):
  one = [1]
  def compute (prev):
    return one + [x + y for (x,y) in pairs(prev)] + one
  def aux (m, prev):
    if (m > n):
      return
    else:
      yield prev
      yield from aux(m + 1, compute(prev))
  yield from aux(1, one)

Now you can compute the output lazily, as it is consumed. If you want it all at once however, you can use list.

list(pascal(5))
# [[1], [1, 1], [1, 2, 1], [1, 3, 3, 1], [1, 4, 6, 4, 1]]

2 Comments

very nice solution. we end up with almost same solution. I suggest to let pairs function return [sum(xs[0:2])] + pairs(xs[1:]). Then you save the list comprehension later in compute by returning one + pairs(prev) + one , which makes whole program more readable and concise and avoid any type of loop as requested by OP.
The OP explicitly stated multiple times, "without using any loops". The expression, [x + y for (x,y) in pairs(prev)] is a loop!
0

A simpler recursive solution that uses math to construct the triangle without any hidden loops:

def pascal(n, row=0):

    def pascal_row(numerator, denominator=1, number=1):
        if numerator > 0:
            number = number * numerator // denominator
            return [number, *pascal_row(numerator - 1, denominator + 1, number)]

        return []

    if row < n:
        return [[1, *pascal_row(row)], *pascal(n, row + 1)]

    return []

print(*pascal(10), sep='\n')

OUTPUT

% python3 test.py
[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
%

Comments

0

Here is one without loops:

def getRow(r,k=1,row=[]):
  if k==1: row = [1] # first k  
  if k==r+1: return row # stop
  row += [row[-1]*(r-k+1)//k] # add next element
  return getRow(r,k+1,row) # Recurse 

def pascal(n):
  row = getRow(n) 
  if n == 0: return [row] # Stop condition 
  return pascal(n-1) + [row] # Recurse 
  
print(*pascal(12), sep='\n') 

Outputs:

[1]
[1, 1]
[1, 2, 1]
[1, 3, 3, 1]
[1, 4, 6, 4, 1]
[1, 5, 10, 10, 5, 1]
[1, 6, 15, 20, 15, 6, 1]
[1, 7, 21, 35, 35, 21, 7, 1]
[1, 8, 28, 56, 70, 56, 28, 8, 1]
[1, 9, 36, 84, 126, 126, 84, 36, 9, 1]
[1, 10, 45, 120, 210, 252, 210, 120, 45, 10, 1]
[1, 11, 55, 165, 330, 462, 462, 330, 165, 55, 11, 1]
[1, 12, 66, 220, 495, 792, 924, 792, 495, 220, 66, 12, 1]

If you don't mind a loop on the print side:

tr = pascal(12)
max_len = len(" ".join(map(str,tr[-1])))
for row in tr:
  print(" ".join(map(str,row)).center(max_len))

Output:

                     1                     
                    1 1                    
                   1 2 1                   
                  1 3 3 1                  
                 1 4 6 4 1                 
               1 5 10 10 5 1               
              1 6 15 20 15 6 1             
            1 7 21 35 35 21 7 1            
           1 8 28 56 70 56 28 8 1          
        1 9 36 84 126 126 84 36 9 1        
    1 10 45 120 210 252 210 120 45 10 1    
  1 11 55 165 330 462 462 330 165 55 11 1  
1 12 66 220 495 792 924 792 495 220 66 12 1

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.