'Python: generate a random 2d array of integers from given sums of its rows and columns

Is there any way to generate a 2d array with a fixed sum of each row and each column?

for example

TargetArray = 3 rows and 4 columns

Sum of Each row = [30, 20, 30]

Sum of Each column = [23, 10, 27, 20]

I was trying to solve this a lot of days with no result 😢



Solution 1:[1]

(Temporary note until they hopefully unvandalize their question: They added the "random" and "positive" requirements later, after this answer.)

Simple solution:

     23 10 27 20

30  -27 10 27 20
20   20  0  0  0
30   30  0  0  0

Code to build it:

R = [30, 20, 30]
C = [23, 10, 27, 20]

assert sum(R) == sum(C)
matrix = [[(0 if j else r) if i else (c if j else r+c-sum(R))
           for j, c in enumerate(C)]
          for i, r in enumerate(R)]

Or:

matrix = [[0 if i and j else
           r if i else
           c if j else
           r + c - sum(R)
           for j, c in enumerate(C)]
          for i, r in enumerate(R)]

Or a somewhat cute way, always building a mini-matrix of the four possibilities and picking the right one (assumes a non-empty matrix):

x = R[0] + C[0] - sum(R)
matrix = [[[[x, c],
            [r, 0]][i > 0][j > 0]
           for j, c in enumerate(C)]
          for i, r in enumerate(R)]

Solution 2:[2]

You can achieve this with an algorithm called Iterative Proportional Fitting: https://en.wikipedia.org/wiki/Iterative_proportional_fitting. IPF allows it to reconstruct a non-negative matrix from row an column sum vectors.

There is a Python implementation for this, called ipfn.

from ipfn import ipfn
import numpy as np

seed = np.ones((3,4))
row_sum = [30, 20, 30]
column_sum = [23, 10, 27, 20]    
aggregates = [row_sum, column_sum]
dimensions = [[0], [1]]
IPF = ipfn.ipfn(seed, aggregates, dimensions, convergence_rate=1e-6)
X = IPF.iteration()

X = array([[ 8.625,  3.75 , 10.125,  7.5  ],
   [ 5.75 ,  2.5  ,  6.75 ,  5.   ],
   [ 8.625,  3.75 , 10.125,  7.5  ]])

By changing, the seed, you will get different results. If you require X to contain integers only, then you need to resort to an integerized version of IPF. However, as far as I know, there is no Python implementation for this.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 c-wizz