Pascal matrix generation

From Rosetta Code
Pascal matrix generation is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

A pascal matrix is a two-dimensional square matrix holding numbers from Pascal's triangle, also known as binomial coefficients and which can be shown as nCr.

Showing truncated 5-by-5 matrices M[i, j] for i,j in range 0..4.
A Pascal upper-triangular matrix is populated with jCi

[[1, 1, 1, 1, 1],
 [0, 1, 2, 3, 4],
 [0, 0, 1, 3, 6],
 [0, 0, 0, 1, 4],
 [0, 0, 0, 0, 1]]

A Pascal lower-triangular matrix is populated with iCj (the transpose of the upper-triangular matrix).

[[1, 0, 0, 0, 0],
 [1, 1, 0, 0, 0],
 [1, 2, 1, 0, 0],
 [1, 3, 3, 1, 0],
 [1, 4, 6, 4, 1]]

A Pascal symmetric matrix is populated with i+jCi

[[1, 1, 1, 1, 1],
 [1, 2, 3, 4, 5],
 [1, 3, 6, 10, 15],
 [1, 4, 10, 20, 35],
 [1, 5, 15, 35, 70]]

The task is to write functions capable of generating each of the three forms of n-by-n matrices.<br? Use those functions to display upper, lower, and symmetric Pascal 5-by-5 matrices on this page. The output should distinguish between different matrices and the rows of each matrix (No showing a list of 25 numbers assuming the reader should split it into rows).

Note
  1. The Cholesky decomposition of a Pascal upper-triangle matrix is the Identity matrix of the same size.
  2. The Cholesky decomposition of a Pascal symmetric matrix is the Pascal lower-triangle matrix of the same size.


J

<lang J>  !/~ i. 5 1 1 1 1 1 0 1 2 3 4 0 0 1 3 6 0 0 0 1 4 0 0 0 0 1

  !~/~ i. 5

1 0 0 0 0 1 1 0 0 0 1 2 1 0 0 1 3 3 1 0 1 4 6 4 1

  (["0/!+/)~ i. 5

1 1 1 1 1 1 2 3 4 5 1 3 6 10 15 1 4 10 20 35 1 5 15 35 70</lang>

Explanation:

x!y is the number of ways of picking x balls (unordered) from a bag of y balls and x!/y for list x and list y gives a table where rows correspond to the elements of x and the columns correspond to the elements of y. Meanwhile !/~y is equivalent to y!/y (and i.y just counts the first y non-negative integers).

Also, x!~y is y!x (and the second example otherwise follows the same pattern as the first example.

For the final example we use an unadorned ! but prepare tables for its x and y values. Its right argument is a sum table, and its left argument is a left identity table. They look like this:

<lang J> (+/)~ i. 5 0 1 2 3 4 1 2 3 4 5 2 3 4 5 6 3 4 5 6 7 4 5 6 7 8

  (["0/)~ i. 5

0 0 0 0 0 1 1 1 1 1 2 2 2 2 2 3 3 3 3 3 4 4 4 4 4</lang>

The parenthesis in these last two examples are redundant - they could have been omitted without changing the result, but were left in place for emphasis.

Python

Python: Summing adjacent values

<lang python>from pprint import pprint as pp

def pascal_upp(n):

   s = [[0] * n for _ in range(n)]
   s[0] = [1] * n
   for i in range(1, n):
       for j in range(i, n):
           s[i][j] = s[i-1][j-1] + s[i][j-1]
   return s

def pascal_low(n):

   # transpose of pascal_upp(n)
   return [list(x) for x in zip(*pascal_upp(n))]

def pascal_sym(n):

   s = [[1] * n for _ in range(n)]
   for i in range(1, n):
       for j in range(1, n):
           s[i][j] = s[i-1][j] + s[i][j-1]
   return s
   

if __name__ == "__main__":

   n = 5
   print("\nUpper:")
   pp(pascal_upp(n))
   print("\nLower:")
   pp(pascal_low(n))
   print("\nSymmetric:")
   pp(pascal_sym(n))</lang>
Output:
Upper:
[[1, 1, 1, 1, 1],
 [0, 1, 2, 3, 4],
 [0, 0, 1, 3, 6],
 [0, 0, 0, 1, 4],
 [0, 0, 0, 0, 1]]

Lower:
[[1, 0, 0, 0, 0],
 [1, 1, 0, 0, 0],
 [1, 2, 1, 0, 0],
 [1, 3, 3, 1, 0],
 [1, 4, 6, 4, 1]]

Symmetric:
[[1, 1, 1, 1, 1],
 [1, 2, 3, 4, 5],
 [1, 3, 6, 10, 15],
 [1, 4, 10, 20, 35],
 [1, 5, 15, 35, 70]]

Python: Using a binomial coefficient generator function

<lang python>def binomialCoeff(n, k):

   result = 1
   for i in range(1, k+1):
       result = result * (n-i+1) // i
   return result

def pascal_upp(n):

   return [[binomialCoeff(j, i) for j in range(n)] for i in range(n)]

def pascal_low(n):

   return [[binomialCoeff(i, j) for j in range(n)] for i in range(n)]

def pascal_sym(n):

   return [[binomialCoeff(i+j, i) for j in range(n)] for i in range(n)]</lang>
Output:

(As above)