4
\$\begingroup\$

I wanna learn how to make my code more Pythonic, I'm still a newb. Advises on classes and functions I could've used are welcomed.

import random as r

n = 7   #This number defines the size of the square matrix

def print_mat(mat):
    """ This function will be used to display the matrix
    """
    for row in range(len(mat)):
        for line in range(len(mat[0])):
            print(mat[row][line],end="\t")
        print("\n")


def is_used(test_num):
    """ This function tests if the argument has already been used
    in the column of the matrix thanks to the array already_used
    """
    for used_num in already_used:
        if test_num == used_num:
            return True
    return False


A = [[0]*n for _ in range(n)] #Creates matrix of size n*n

for row in range(n):
    """ Will loop n times for each column of the matrix
    The array bellow will allow to sort out already used random numbers
    """
    already_used = [0 for _ in range(n)]

    for line in range(n):
        """ Will loop n times for each line of the matrix
        """
        rand_num = r.randint(1,n)   #Generate random number between [1;n]

        while is_used(rand_num) == True:
            rand_num = r.randint(1,n)
            """ Will loop until rand_num is a num that hasn't been used
            """
        A[line][row] = rand_num #Assignes the available rand_num to the matrix
        already_used[line] = rand_num
        
print("\nMatrice A = ")
print_mat(A)    #Prints the matrix using the function defined above

One previous output :

Matrice A =
6       4       7       4       6       5       6

3       1       6       5       1       4       2

4       3       2       3       2       1       4

2       2       1       7       3       6       5

1       5       4       2       4       7       1

5       6       5       1       5       2       3

7       7       3       6       7       3       7

Thanks a lot <3

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Hopefully someone points to the Fisher–Yates Shuffle. (Read the link, very helpful.) \$\endgroup\$
    – Peilonrayz
    Dec 17 '21 at 14:56
  • \$\begingroup\$ You were that person, and I'm reading through it rn ;) Thanks! \$\endgroup\$
    – Rúshi
    Dec 17 '21 at 17:52
3
\$\begingroup\$

As far as I understand, your objective is to create and print a square matrix, where each column must have unique values.

If that is the case, and considering you are already using random module, it is better to use random.shuffle or random.sample. Now random.shuffle modifies a list in place, so that can create reference issues, and to avoid that we will need to create a copy anyways, so random.sample would be more suitable in this case.

If you use random.sample there is no need to check whether a value has already been used while filling a cell, we can assign one row at a time. Furthermore, there is no need to build an empty matrix at all, we can directly create a matrix with random columns using zip. So first approach could be something like this:

import random

def generate_col(allowed_nums, length):
    """
    This function will generate col of provided `length`
    with random ordering out of `allowed_nums`.
    """
    return random.sample(allowed_nums, length)

def print_matrix(mat):
    """ 
    This function will be used to display the matrix
    """
    for row in mat:
        print(*row, sep='\t', end=2*'\n')

def build_matrix(n):
    """
    Creates a square matrix of size n.
    """
    allowed_nums = range(1, n+1)
    all_cols = [generate_col(allowed_nums, n) for _ in range(n)]
    return list(zip(*all_cols))

if __name__ == '__main__':
    n = 7   #This number defines the size of the square matrix
    
    A = build_matrix(n)

    print("\nMatrice A = ")
    print_matrix(A)    #Prints the matrix using the function defined above 

If you plan to work with multiple matrices, you can consider making a class. That would hide implementation details, and make the usage much cleaner:

import random

class RandomSquareMatrix:
    '''
    nxn Square Matrix filled with unique random numbers
    in range (1, n] in each column.
    '''
    def __init__(self, n):
        self.n = n
        self.allowed_vals = range(1, n+1)
        self.matrix = self.build_matrix()

    def build_matrix(self):
        """
        This function will generate col of provided `length`
        with random ordering out of `allowed_nums`.
        """
        all_cols = [self.generate_col() for _ in range(self.n)]
        return list(zip(*all_cols))

    def generate_col(self):
        return random.sample(self.allowed_vals, self.n)

    def __str__(self):
        """ 
        This function will be used to display the matrix
        when it is printed
        """
        row_sep = 2 * '\n'
        col_sep = '\t'
        return row_sep.join(
            [col_sep.join(map(str, row)) 
            for row in self.matrix]
        )

    def __repr__(self):
        """
        This function will be used to display the matrix information.
        Example:
         >>> RandomSquareMatrix(3)
         <RandomSquareMatrix: 3x3>
        """
        cls_name = type(self).__name__

if __name__ == '__main__':
    B = RandomSquareMatrix(7)
    print('Matrice B =')
    print(B)

NOTE: I think, in this case, it is better to import random and use random.sample, instead of from random import sample. Because you are using it only in one place, shouldn't be that hard to write it once, also random.sample is more clear in my view. ```

\$\endgroup\$
3
  • \$\begingroup\$ Thanks, this is very helpful. I didn't know that the function shuffle existed! I also love the way you printed the matrix in 1 line^^ \$\endgroup\$
    – Rúshi
    Dec 17 '21 at 17:32
  • \$\begingroup\$ (and many other things in your version of the code) <3 I'll take my time to assimilate that :) \$\endgroup\$
    – Rúshi
    Dec 17 '21 at 17:40
  • 1
    \$\begingroup\$ Very concise and well structured code. I also really like how elegantly you transposed the matrix with list(zip(*all_cols)). Very nice! Upvoted. \$\endgroup\$
    – Flursch
    Dec 18 '21 at 16:12
3
\$\begingroup\$

The random.shuffle method could be very useful here to get a random ordering of the integers from 1 to n without having to track what you've already used, and therefore make your code a lot more concise.

It works like this:

from random import shuffle
my_list = ['A', 'B', 'C']
shuffle(my_list)
print(my_list)

Example output: ['B', 'A', 'C']

========================================

Using it, your code might look something like:

from random import shuffle
n=7

def print_mat(mat):
    """ This function will be used to display the matrix
    """
    for row in range(len(mat)):
        for line in range(len(mat[0])):
            print(mat[row][line],end="\t")
        print("\n")

A = [[0]*n for _ in range(n)] #Creates matrix of size n*n

# List of integers from 1 to n
one_to_n = list(range(1,n+1))

for column in range(n):
    # Shuffle the list
    shuffle(one_to_n)
    # Use the shuffled list to populate the column
    for row in range(n):
        A[row][column] = one_to_n[row]

print_mat(A)
\$\endgroup\$
2
  • \$\begingroup\$ Thanks, this is very helpful. I didn't know that the function shuffle existed! \$\endgroup\$
    – Rúshi
    Dec 17 '21 at 17:33
  • 1
    \$\begingroup\$ Glad it helped! Please vote useful on the answer if you have a moment. \$\endgroup\$
    – ljdyer
    Dec 17 '21 at 18:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.