Програмиране на Python / Умножение на матрици

Сложности на умножението на матрици при програмиране в Python с помощта на списъци

Подробен анализ на умножението на матрици в езика за програмиране Python.

Матрично умножение е процесът, който води до създаването на матрица, последвано от умножението на две матрици. Основното условие за да се осъществи е колоната на първата матрица да е равна на реда на втората матрица. Матричното умножение е задължителна тема за ученик от XII клас. Заданието за изчисляване на умножение на матрици с помощта на C/C+ е повсеместно за студент в колеж по компютърни науки. Независимо от това, сложността, свързана с умножението на матрици, често се пренебрегва, когато програмата е написана на Python.

Ако търсите термина „python за умножение на матрици“, Google ще ви предостави множество страници, указващи твърдо кодирани кодове за умножение на матрици. Почти всеки код, изброен в тези уебсайтове, удобно пренебрегва случая, когато възниква умножение между матрица с един ред и матрица с няколко реда. Нека се потопим в него по-нататък.

Проста твърдо кодирана програма за умножение на матрици в Python лесно ще наподобява кода по-долу:

def matrix_mul(MatrixA, MatrixB):
    """
    Function to create a matrix resulting from multiplication of two matrices
    """
    global r1, c1
    global r2, c2
    global MatrixC
    for i in range(r1):
        for j in range(c2):
            for k in range(r2):
                MatrixC[i][j] += MatrixA[i][k] * MatrixB[k][j]
MatrixA = [[1, 2], [3, 4]]
MatrixB = [[1, 2, 3, 4, 5], [5, 6, 7, 8, 9]]
r1 = 2
c1 = 2
r2 = 2
c2 = 5
MatrixC = [[0, 0, 0, 0, 0], [0, 0, 0, 0, 0]]
matrix_mul(MatrixA, MatrixB)
aster = '*'
print(aster * 68)
print('The resultant matrix is:')
print(aster * 68)
print(MatrixC)
print(aster * 68)

Когато стартирате кода, той ще даде следния резултат:

********************************************************************
The resultant matrix is:
********************************************************************
[[11, 14, 17, 20, 23], [23, 30, 37, 44, 51]]
********************************************************************

Кодът започва с инициализиране на стойностите на MatrixA (от порядък 2 x2) и MatrixB (от порядък 2x5). След това функцията matrix_mul се извиква с матрици, които трябва да бъдат умножени като аргументи. Функцията извършва умножение на матрица по точковия продукт на редовете на първата матрица към колоните на втората матрица (точковият продукт е умножението на съвпадащи елементи). Получената матрица ще има редове, равни на първата матрица и колони, равни на втората матрица.

Недостатъците на кода (подобно на повечето кодове, достъпни онлайн в различни уебсайтове) излизат на преден план, когато матрица A (от порядък 1x3) се умножи с друга матрица B (от порядък 3x1). При стартиране на кода възниква TypeError.

TypeError: обектът „int“ не може да се записва

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-26-a342be5c878d> in <module>()
     16 MatrixB = [[4], [5], [6]]
     17 MatrixC = [0]
---> 18 matrix_mul(MatrixA, MatrixB)
     19 aster = '*'
     20 print(aster * 70)
<ipython-input-26-a342be5c878d> in matrix_mul(MatrixA, MatrixB)
      9         for j in range(5):
     10             for k in range(2):
---> 11                 MatrixC[i][j] += MatrixA[i][k] * MatrixB[k][j]
     12 
     13 
TypeError: 'int' object is not subscriptable

За да разберем по-добре тази грешка, нека разгледаме списък с три елемента

list1 = [1, 2, 3]
print(list1[0][0])

Ако стартираме този код, възниква същата TypeError. Причината е, че индексът на списъка, до който кодът се опитва да осъществи достъп, е извън обхват. Ако list1 беше списък от списъци (както в кода, даден по-долу), резултатът щеше да бъде 1.

list1 = [[1], [2], [3]]
print(list1[0][0])

В програмата функцията matrix_mul(MatrixA, MatrixB) извършва умножение на матрици в следващите редове код.

for i in range(r1):
        for j in range(c2):
            for k in range(r2):
                MatrixC[i][j] += MatrixA[i][k] * MatrixB[k][j]

Основната причина за TypeError произтича точно от този ред код. Причината е невежеството на програмиста относно следните два сценария, при които е включена матрицата с един ред:

Сценарий 1: Когато редът на първата матрица и колоната на втората матрица са едно. MatrixA от порядък 1x3 и MatrixB от порядък 3x1 е типичният пример. В този случай MatrixA и MatrixC (нулевата матрица) са списъци (вместо списък със списъци). Следователно, когато се изчислява MatrixC[i][j] += MatrixA[i][k] * MatrixB[k][j], индексът е извън диапазона за MatrixC и MatrixA, тъй като те са матрици с един ред. Следователно обектът TypeError: ‘int’ не може да се записва.

Сценарий 2: Когато редът на първата матрица е единица и колоната на втората матрица не е равна на единица. MatrixA от порядък 1x2 и MatrixB от порядък 2x2 е конкретният случай. Точно както в предишния случай, MatrixA и MatrixC са матрици с един ред. Следователно възниква TypeError.

Нека да коригираме тези проблеми и да се стремим да създадем програма, която включва всички аспекти на матричното умножение. Едновременно с това, нека да дадем възможност на крайния потребител (човекът, изпълняващ кода) и да направим кода по-гъвкав, като му/ѝ дадем възможност да определя реда на матриците и да въвежда съответните стойности по време на изпълнение.

Кодът по-горе получава въвеждане от потребителя по време на изпълнение и проверява дали потребителят е въвел правилните данни. Кодът проверява за всички възможни грешки, които потребителят може да направи по време на въвеждане. Първо, програмата проверява дали получените входни данни за реда на матрицата са цяло число. За тази обработка на изключение на Python (опитайте, освен) се използва за повишаване на ValueError.

Enter the no. of rows of Matrix A: 2.5
Please enter a valid integer greater than zero

Появява се съобщение за грешка, ако колоната на първата матрица не е равна на реда на втората матрица.

Enter the no. of rows of Matrix A: 2
Enter the no. of columns of Matrix A: 3
Enter the no. of rows of Matrix B: 4
Enter the np. of columns of Matrix B: 2
Kindly change the order of matrices. For multiplication of matrices, the no. of columns of first matrix must be equal to no. of rows of second matrix.

Ако редът на първата матрица е 2x2 и потребителят въведе четири елемента за първия ред, програмата показва съобщение за грешка „Моля, въведете елементи, равни на 2 — размера на колоната на Матрица A“.

Програмата взема под внимание двата сценария на матрицата с един ред, разгледани по-рано. Нека разгледаме Сценарий 1, когато броят на редовете на първата матрица и броят на колоните на втората матрица е едно (например матрица A е от порядък 1x3, а MatrixB е от порядък 3x1), matrixC ще да бъде матрица 1x1. Подобно на matrixC, MatrixA ще бъде матрица с един ред. В този случай умножението на матрицата се извършва с помощта на следния ред код:

if(r1 == 1)&(c2 == 1):  #(1x3)*(3x1)=(1x1)
        for i in range(r1):
            for j in range(c2):
                for k in range(r2):
                    matrixC[i] += matrixA[k] * matrixB[k][j]

Сценарий 2се занимава със ситуация, когато броят на редовете на първата матрица е едно, а броят на колоните на втората матрица не е едно (Например матрица A е от порядък 1x2, а матрицаB е от порядък 2x2). Точно като Сценарий 1, matrixA и matrixC са матрици с един ред. Разликата е в начина, по който се изчислява стойността на умножението на матрицата. В Сценарий 1 matrixC се итерира върху стойности на i (ред на матрица A), докато в Сценарий 2 matrixC се итерира върху стойности на j (колона на matrixB).

elif(r1 == 1)&(c2 != 1): #(1x2)*(2x2)=(1x2)
        for i in range(r1):
            for j in range(c2):
                for k in range(r2):
                    matrixC[j] += matrixA[k] * matrixB[k][j]

Както в сценарий 1, така и в сценарий 2, matrixC се изчислява по начин, по който ще се превърне в матрица с един ред.

if(r1 == 1)|(r1 == c2 == 1): 
        for i in range(r1):
            matrixC = []
            for j in range(c2):
                matrixC.append(0)

Във всички останали случаи умножението на матрицата се извършва чрез обичайния код:

else: #all other cases
        for i in range(r1):
            for j in range(c2):
                for k in range(r2):
                    matrixC[i][j] += matrixA[i][k] * matrixB[k][j]

matrixC като многоредова матрица се изчислява чрез:

for i in range(r1):
            row_matrixC = []
            for j in range(c2):
                row_matrixC.append(0)            
            matrixC.append(row_matrixC)

Всички тези случаи могат да се разглеждат като единичен случай в C++ (един подход за всички). Умножението на матрицата, направено в C++:

Github връзка



Честито кодиране!!!