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 +
aydow
  • 3,673
  • 2
  • 23
  • 40
Cutie Pie
  • 29
  • 2
  • 8
  • Possible duplicate of [Pascals triangle with recursion](https://stackoverflow.com/questions/43687857/pascals-triangle-with-recursion) – Mulan Nov 06 '18 at 15:21

6 Answers6

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)))
     ...:
aydow
  • 3,673
  • 2
  • 23
  • 40
  • Very nice answer. I learned something new about Pascal. The `In [###]: ` prompts are distracting and make it difficult to copy/paste the code however. – Mulan Nov 06 '18 at 15:31
  • 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! – cdlane Oct 31 '19 at 22:25
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)
eat chocolate
  • 160
  • 2
  • 10
  • 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) . – Cutie Pie Oct 26 '18 at 04:02
  • 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](https://docs.scipy.org/doc/scipy/reference/generated/scipy.special.comb.html#scipy.special.comb) – eat chocolate Oct 26 '18 at 04:10
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.

englealuze
  • 1,445
  • 12
  • 19
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)
Silver
  • 1,327
  • 12
  • 24
  • 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. – Cutie Pie Oct 26 '18 at 05:20
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]]
Mulan
  • 129,518
  • 31
  • 228
  • 259
  • 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. – englealuze Nov 07 '18 at 09:25
  • The OP explicitly stated multiple times, "without using any loops". The expression, `[x + y for (x,y) in pairs(prev)]` is a loop! – cdlane Oct 31 '19 at 22:24
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]
%
cdlane
  • 40,441
  • 5
  • 32
  • 81