Зробіть матричне множення!


14

У математиці множення матриць або матричний добуток - це двійкова операція, яка виробляє матрицю з двох матриць. Визначення мотивоване лінійними рівняннями та лінійними перетвореннями на векторах, які мають численні застосування у прикладній математиці, фізиці та техніці. Більш детально, якщо A - матриця n × m, а B - матриця m × p, їх матричний продукт AB - це матриця n × p, в якій m записи в рядку A множать на m записи вниз a стовпці B і підсумовуються для створення запису AB. Коли дві лінійні перетворення представлені матрицями, то добуток матриці являє собою склад двох перетворень.

Джерело: Вікіпедія

Іншими словами, для множення двох матриць, наприклад:

1 2 3   1 4
2 3 4 × 3 1 = 
3 4 5   4 6

Спочатку візьміть рядок номер 1 у першій матриці, стовпець №1 у другій матриці та помножте 1на 1, 2на 3та 3на 4.

1 × 1 = 1
2 × 3 = 6
3 × 4 = 12

Тепер додайте їх разом, щоб отримати ваш перший предмет:

1 2 3   1 4   19
2 3 4 × 3 1 = 
3 4 5   4 6

Для другого числа в першому стовпці результату вам потрібно буде взяти рядок №2 замість номера рядка 1 і зробити те ж саме.

1 × 2 = 2
3 × 3 = 9
4 × 4 = 16
      = 27

Після того, як ви виконаєте весь перший стовпець, результат виглядає приблизно так:

1 2 3   1 4   19
2 3 4 × 3 1 = 27
3 4 5   4 6   35

Тепер знову зробіть те саме, але візьміть другий стовпчик замість першого стовпця, у результаті чого:

1 2 3   1 4   19 24
2 3 4 × 3 1 = 27 35
3 4 5   4 6   35 46

Ваше завдання

Дано дві матриці (максимальні розміри 200х200), що містять числа в діапазоні від -10000 до 10000, де число стовпців на першому дорівнює кількості рядків на другому, помножимо перше на друге. (Множення матриці є некомутативним.)

Ви можете приймати введення та давати вихід у вигляді масиву масивів (або еквівалент), матриці (якщо ваша мова має такий формат) або рядка з рядком.

Ви не можете використовувати будь-які вбудовані модулі для множення матриць.

Тестові справи

1 2   1 2 3 4 5    13 16 19 22 25
3 4 × 6 7 8 9 10 = 27 34 41 48 55
5 6                41 52 63 74 85

2 3   3 5   15 13
3 4 × 3 1 = 21 19

5 3            11    27
1 3      1 3   7     15
9 3    × 2 4 = 15    39
1 -1000        -1999 -3997

Пам'ятайте, що це , тому виграє код з найменшими байтами.


Чи можемо ми використовувати вбудовані крапкові вироби? Вони працюють на векторах, а не на матрицях.
Денніс

1
Чи фіксований порядок введення чи ми можемо взяти a і b у такому порядку і вивести b × a ?
Денніс

@Dennis Ви можете змінити вхід, але без крапкових продуктів
Oliver Ni

4
Проблеми, пов'язані з тим, що робити X без Y , не враховують .
flawr

Чи можуть вхідні матриці містити числа з плаваючою комою? Якщо так, то рекомендую додати тестовий випадок з деякими.
Р. Кап

Відповіді:


5

Желе , 7 5 байт

Z×þḅ1

Приймає B і A в якості аргументів і повертає A × B .

Спробуйте в Інтернеті!

Як це працює

Z×þḅ1  Main link. Left argument: B. Right argument: A

Z      Zip; transpose B's rows and columns.
 ×þ    Table multiplication; multiply all columns of B (rows of B's transpose) by
       all rows of A, element by element. Results are grouped by the rows of A.
   ḅ1  Unbase 1; compute the sum of all flat arrays in the result.

3
Отже, зачекайте, що вбудований і ручний спосіб множення матриць закінчуються однаковою кількістю байтів у Jelly? Це заплутано, але круто.
Йодл

@Yodle Вбудований модуль - æ×це 2 байти.
Ерік Атголфер

@EriktheOutgolfer Це було посиланням на версію 2, в якій використовувався æ.атом.
Денніс

4

05AB1E , 13 байт

vyU²øvyX*O})ˆ

Спробуйте в Інтернеті!

Пояснення

v               # for each row in the first matrix
 yU             # save the row in X
   ²øv          # for each row in the transposition of the second matrix
      yX*       # multiply the rows
         O      # sum the elements of the resulting row
          }     # end inner loop
           )    # wrap elements of the new row in a list
            ˆ   # push to global list
                # implicitly output global list

Тепер може бути 7 байт з точно таким же підходом:εUøεX*O
Кевін Кройсейсен

4

Пітон 2, 69 66 байт

Це якраз і відповідає стандартній формулі, але лямбда-d для стисності :) Код, який не перебуває у гольфі, надзвичайно простий!

lambda x,y:[[sum(map(int.__mul__,r,c))for c in zip(*y)]for r in x]

Дякуємо Алексі Торхамо за збереження 3 байтів! :)

Невикористаний код:

x = [[1,2],[3,4],[5,6]]
y = [[1,2,3,4,5],[6,7,8,9,10]]

output = []
for row in x:
    nrow = []
    for col in zip(*y):                             # zip(*[]) transposes a matrix
        nrow += [sum(a*b for a,b in zip(row,col))]  # multiplication for each pair summed
    output += [nrow]

print output

Ви можете використати sum(map(int.__mul__,r,c))для збереження 3 байти. (Не буде працювати з плаваючою точкою, але це теж не потрібно)
Алексі Торхамо

3

J, 13 9 байт

Збережено 4 байти завдяки милі!

[:+/*"#:~

Це вилка з кришкою:

[: +/ *"#:~

Що еквівалентно:

[: +/ (*"#:)~
[: +/ (*"_ 1 0)~

Який виконує бажане множення; вони підсумовуються.

З вбудованим точковим продуктом, 5 байт: +/ .*

Тестові справи

   f =: [: +/ *"#:~
   (3 3$1 2 3 2 3 4 3 4 5)f(3 2$1 4 3 1 4 6)
19 24
27 35
35 46
   (3 3$1 2 3 2 3 4 3 4 5);(3 2$1 4 3 1 4 6)
+-----+---+
|1 2 3|1 4|
|2 3 4|3 1|
|3 4 5|4 6|
+-----+---+
   (2 2$2 3 3 4)f(2 2$3 5 3 1)
15 13
21 19
   (2 2$2 3 3 4);(2 2$3 5 3 1)
+---+---+
|2 3|3 5|
|3 4|3 1|
+---+---+

Я просто наткнувся [:+/*"#:~на 9 байт
милі

@miles ефектно!
Conor O'Brien



2

R, 66 байт

function(A,B)apply(B,2,function(i)apply(A,1,function(j)sum(j*i)))

Безіменна функція, яка приймає дві R-матриці як вхідні дані та повертає продукт. Він використовує, applyякий використовується для застосування функцій через поля масивів. У forцьому випадку він працює як подвійний цикл: для кожного стовпця Bта для кожного ряду Aповертайте суму (векторизованих) продуктів.

Порівняйте з чистим для циклу підхід ( 101байти):

function(A,B){M=matrix(NA,m<-nrow(A),n<-ncol(B));for(i in 1:n)for(j in 1:m)M[j,i]=sum(A[j,]*B[,i]);M}

Наразі не на моєму робочому столі, але ви не могли зробити щось подібне, outer(A,B,`*`)а не вбудовані applyдзвінки?
rturnbull

@rturnbull Я не впевнений, як працює зовнішнє у поєднанні з матрицями, але в цьому випадку він мав би створити 4-D масив.
Billywob

Ага так, це трохи проблематично. Лінеаризація матриць, швидше за все, займе більше байтів, ніж ваш підхід тут
rturnbull

2

Математика, 20 байт

Inner[1##&,##,Plus]&

Анонімна функція. В якості введення бере два списки чисел-2 і повертає список номерів рангів 2 як вихід. Для тих, хто цікавиться, Innerце функція, яка робить матричне множення, як застосування двох функцій до двох тензорів.


Я вважаю Inner[1##&,##]&, еквівалентно Inner[1##&,##,Plus]&...? І так 1##&~Inner~##&було б ще краще.
Грег Мартін

2

C #, 168 167 байт

(A,B)=>{int n=A.Length,p=B[0].Length,i=0,j=0,k=0,s;var R=new int[n,p];while(i++<n)for(j=0;j<p;){s=0;for(k=0;k<A[0].Length;)s+=A[i][k]*B[k++][j];R[i,j++]=s;}return R;};

Дякую @Mukul Kumar, що врятував 1 байт, а цикл в той час був справді коротшим: P

Повна програма з тестовими кейсами:

using System;
class Matrix
{
    static void Main()
    {
        Func<int[][], int[][], int[,]> a = null;

        a = (A,B)=>
        {
            int n=A.Length,p=B[0].Length,i=0,j=0,k=0,s;
            var R=new int[n,p];
            while(i++<n)
                for(j=0;j<p;)
                {
                    s=0;
                    for(k=0;k<A[0].Length;)
                        s+=A[i][k]*B[k++][j];
                    R[i,j++]=s;
                }
            return R;
        };

        int[,] t1 = a(new int[][] { new int[] { 1, 2 }, new int[] { 3, 4 }, new int[] { 5, 6 } },
            new int[][] { new int[] { 1, 2, 3, 4, 5 }, new int[] { 6, 7, 8, 9, 10 } } );
        int[,] t2 = a(new int[][] { new int[] { 2, 3 }, new int[] { 3, 4 } },
            new int[][] { new int[] { 3, 5 }, new int[] { 3, 1 } });
        int[,] t3 = a(new int[][] { new int[] { 5, 3 }, new int[] { 1, 3 }, new int[] { 9, 3 }, new int[] { 1, -1000 } },
            new int[][] { new int[] { 1, 3 }, new int[] { 2, 4 } });

        Console.WriteLine(IsCorrect(t1, new int[,] { { 13, 16, 19, 22, 25 }, { 27, 34, 41, 48, 55 }, { 41, 52, 63, 74, 85 } } ));
        Console.WriteLine(IsCorrect(t2, new int[,] { { 15, 13 }, { 21, 19 } } ));
        Console.WriteLine(IsCorrect(t3, new int[,] { { 11, 27 }, { 7, 15 }, { 15, 39 }, { -1999, -3997 } } ));

        Console.Read();
    }

    static bool IsCorrect(int[,] answer, int[,] valid)
    {
        if (answer.Length != valid.Length)
            return false;
        for (int i = 0; i < answer.GetLength(0); i++)
            for (int j = 0; j < answer.GetLength(1); j++)
                if (answer[i, j] != valid[i, j])
                    return false;
        return true;
    }
}

Ви можете обрізати кілька байтів, використовуючи петлі ....
Mukul Kumar

@MukulKumar Зачекайте, я не думаю. Принаймні, вони зламаються навіть правильно? for(;i<n;)-> while(i<n)обидва є 10 байтами.
Йодл

1
for (;i <n;i++) -> while (i++<n)економить 1 байт
Мукул Кумар

Я не впевнений у етикеті, коли у мене є досить інша відповідь, але моя альтернатива напевно була натхненна цим.
Кірк Бродхерст

2

MATL , 12 11 байт

7L&!*Xs6Be!

Матриці вводяться, використовуючи ;як роздільник рядків.

Спробуйте в Інтернеті!

Матричне множення без вбудованого було частиною моєї відповіді на Вітрина мов . Однак, намагаючись повторно використовувати оригінальний код для цієї відповіді, я зрозумів, що у нього помилка (вихідний вектор рядка був неправильно перетворений у вектор стовпця). Це зараз виправлено і тут, і там. Для пояснення того, як працює код, див. Згаданий пост (фрагмент довжини-11).


2

C ++ 14, 173 168 156 146 байт

  • -5 байт для повернення через опорний параметр
  • -12 байт для використання foreach і C.back()замість цього розраховуватиi
  • -10 байт для випадання C.clear()і вимагає Cбути пустим на початку

Як неназвана лямбда:

[](auto A,auto B,auto&C){int j,k,s=B[0].size();for(auto a:A){C.emplace_back(s);for(j=-1;++j<s;)for(k=-1;++k<B.size();C.back()[j]+=a[k]*B[k][j]);}}

Потрібні введення та виведення, оскільки vector<vector<int>>вихід повинен бути попередньо порожнім.

Безголівки:

auto f=
[](auto A, auto B, auto&C){
 int j,k,s=B[0].size();
 for (auto a:A){
  C.emplace_back(s);
  for (j=-1;++j<s;)
   for (k=-1;++k<B.size();
    C.back()[j]+=a[k]*B[k][j]
   );
 }
}
;

Зразок:

int main() {
 using M=std::vector<std::vector<int>>;
 M a = {
  {1,2,3},
  {2,3,4},
  {3,4,5},
 };
 M b = {
  {1,4},
  {3,1},
  {4,6},
 };
 M c;
 f(a,b,c);
 for (auto&r:c){
  for (auto&i:r) std::cout << i << ", ";
  std::cout << "\n";
 }
}

Чому б не використовувати push_back()замість emplace_back()?
Г. Сліпен

2

Лушпиння , 7 6 байт

mMδṁ*T

Зверніть увагу на порядок аргументів, спробуйте його в Інтернеті!

-1 байт завдяки @Zgarb!

Пояснення

По суті, саме те, що визначає матричне множення:

mMδṁ*T  -- takes arguments in reverse order, eg: [[1],[0],[-1]] [[1,2,3],[4,5,6]]
     T  -- transpose the first argument: [[1,0,-1]] [[1,2,3],[4,5,6]]
m       -- map the following function (example element [1,0,-1])
 M      --   map the following function applied to [1,0,-1] (example element [1,2,3])
  δṁ    --     accumulate a sum of element-wise..
    *    --    ..multiplication: -2
          -- [[-2],[-2]]

1
oΣzможнаδṁ
Zgarb

1

JavaScript (ES6), 66 байт

(a,b)=>a.map(c=>b[0].map((_,i)=>b.reduce((s,d,j)=>s+d[i]*c[j],0)))

1

C #, 131 байт

(A,B)=>new List<List<int>>(A.Select(x=>new List<int>
    (B[0].Select((f,i)=>B.Select(r=>r[i])).Select(y=>x.Zip(y,(p,q)=>p*q).Sum()))));

Я вкрав рішення Yodle з припущенням, що я можу це написати більш ефективно, використовуючи LINQ (на відміну від циклів). Взяв кілька спроб, але трохи розчавив її.

Тут вона дещо розбита:

a = (A, B) => new List<List<int>>(
            from x in A
            select new List<int>(
                from y in B.First().Select((f, i) => B.Select(r => r.ElementAt(i)))
                select x.Zip(y, (p, q) => p * q).Sum()));

Єдиний реальний «трюк» тут транспонована матриця, B.First().Select((f, i) => B.Select(r => r.ElementAt(i))). Після того, як ми перенесемо другу матрицю, ми маємо два масиви A[i,x]і B[j,x]. Візьміть декартовий добуток ( i*j) і зафіксуйте кожен xмасив довжини разом.

Код тесту:

using System;
class Matrix
{
    static void Main()
    {
        Func<int[][], int[][], List<List<int>>> a = null;
        a = (A, B) => new List<List<int>>(A.Select(x => new List<int>(B[0].Select((f, i) => B.Select(r => r[i])).Select(y => x.Zip(y, (p, q) => p * q).Sum()))));

        List<List<int>> t1 = a(new int[][] { new int[] { 1, 2 }, new int[] { 3, 4 }, new int[] { 5, 6 } },
            new int[][] { new int[] { 1, 2, 3, 4, 5 }, new int[] { 6, 7, 8, 9, 10 } });
        List<List<int>> t2 = a(new int[][] { new int[] { 2, 3 }, new int[] { 3, 4 } },
            new int[][] { new int[] { 3, 5 }, new int[] { 3, 1 } });
        List<List<int>> t3 = a(new int[][] { new int[] { 5, 3 }, new int[] { 1, 3 }, new int[] { 9, 3 }, new int[] { 1, -1000 } },
            new int[][] { new int[] { 1, 3 }, new int[] { 2, 4 } });

        Console.WriteLine(IsCorrect(t1, new int[,] { { 13, 16, 19, 22, 25 }, { 27, 34, 41, 48, 55 }, { 41, 52, 63, 74, 85 } }));
        Console.WriteLine(IsCorrect(t2, new int[,] { { 15, 13 }, { 21, 19 } }));
        Console.WriteLine(IsCorrect(t3, new int[,] { { 11, 27 }, { 7, 15 }, { 15, 39 }, { -1999, -3997 } }));

        Console.Read();
    }

    static bool IsCorrect(List<List<int>> answer, int[,] valid)
    {
        if (answer.Count*answer[0].Count != valid.Length)
            return false;
        for (int i = 0; i < answer.Count; i++)
            for (int j = 0; j < answer[0].Count; j++)
                if (answer[i][j] != valid[i, j])
                    return false;
        return true;
    }

}

Ніцца: P Я ніколи не використовував Linq все так багато, тому я не знаю повністю всіх можливостей цього, тому я схильний просто використовувати стандартні петлі та інше. Однак, я думаю, ви повинні включити використовуючи System.Linq; рядок у вашому рахунку байтів, не знаючи, наскільки це впливає.
Йодл

@Yodle так, я повинен був би включити його using System.Linq; Я не впевнений, чи потрібно тут вирішувати включення котлоподібного типу using Systemтаstatic void Main()
Кірк Бродхерст

Я відповідаю трохи зараз, і з того, що я бачив, в основному ваша відповідь (все, що ви включаєте в свій байт) має спрацювати, якщо ви просто вставили її в програму. Зокрема, для C #, якщо ви пишете лише функцію, вам не потрібно включати визначення класів або статичні параметри Main (), але якщо ваше рішення використовує будь-які речі бібліотеки, такі як Console.WriteLine (), тоді вам потрібно зробити System.Console.WriteLine () або за допомогою системи; оскільки одна може бути коротшою.
Йодл

1

Хаскелл , 49 байт

z=zipWith
m a=map(\x->foldr1(z(+))$z(map.(*))x a)

Спробуйте в Інтернеті!

Введення та вихід - це списки стовпців. Звертає кожен стовпець другої матриці до цього рядка, блискавки зі стовпцями першої матриці та масштабування кожного, підсумовується як вектор.

Я відчуваю, що повинен бути приємний спосіб зробити цю точку вільного та зберегти жменю байтів, але я цього ще не бачу.


0

Javascript, 128 байт

m=(a,b)=>{$=[];q=0;for(x in b){c=[];j=0;for(y in a[0]){_=i=0;for(z in b[0]){_+=a[i][j]*b[q][i];i++}c.push(_);j++}$.push(c);q++}}

Ви отримуєте результат, просто перевіривши $ - це свого роду обман, але ей, це заощадило кілька байт.


0

PHP, 110 байт

function f($a,$b){foreach($a as$n=>$x)foreach($b as$m=>$y)foreach($y as$p=>$v)$z[$n][$p]+=$v*$x[$m];return$z;}

Три петлі для ельфійських масивів. Це так прямо вперед ... але гольфу не так вже й багато.


0

Власне , 14 байт

Пропозиції з гольфу вітаються! Спробуйте в Інтернеті!

┬@;l)∙`i♀*Σ`M╡

Ungolfing

         Implicit input A, then B.
┬        Transpose B's rows and columns. Call it B_T.
@        Swap A to TOS.
;l)      Get len(A) and move to BOS for later.
∙        Push the Cartesian product of A and B_T. Call it cart_prod.
`...`M   Map the following function over cart_prod. Variable xs.
  i        Flatten xs onto the stack, getting a row of A and column of B.
  ♀*       Multiply each element of A_row by each element of B_column.
  Σ        Sum the resulting list to get an element of A*B.
         The result of the map returns every element of A*B, but in one flat list.
╡        Push a list containing len(A) non-overlapping sublists of A*B.
         This separates A*B into rows.
         Implicit return.

0

C, 618 байт

M(char*a,char*b){char*P[2];P[0]=malloc(strlen(a));P[1]=malloc(strlen(b));for(int A=0;A<strlen(a);A++){P[0][A]=a[A];};for(int B=0;B<strlen(b);B++){P[1][B]=b[B];};int H[200][200],B[200][200];int O,N,m,J;for(int Y=0;Y<2;Y++){int y=0,z=0,r=0;char j[7];int p=strlen(P[Y]);for(int i=0;i<=p;i++){if(P[Y][i]==' '||P[Y][i]==','||i==p){(Y<1)?H[y][z]=atoi(j):(B[y][z]=atoi(j));memset(j,'\0',4);(P[Y][i]==' ')?z++:y++;z=(P[Y][i]==',')?0:z;r=0;}else{j[r]=P[Y][i];r++;};};(Y<1)?O=z+1,N=y:(m=y,J=z+1);};for(int U=0;U<N;U++){for(int F=0;F<J;F++){int T=0;for(int d=0;d<O;d++){T+=H[U][d]*B[d][F];};printf("%d ",T);T=0;};printf("\n");};}

Названа функція і, безумовно, найдовша подача тут, частково через те, що перетворення вхідних масивів символів у двовимірні цілочисельні масиви займає найбільше байтів, а також тому, що я довгий час не займався гольфуванням в C. Я все ще працюю над тим, щоб скоротити це, наскільки я можу, і будь-які поради щодо цього дуже вдячні.

Тепер, якщо це не виходить, це вимагає введення через командний рядок з двома матрицями, представленими двома рядками, причому кожна з них містить рядки, розділені комами, а кожен рядок представлений пробілами, розділеними пробілами. Наприклад, матриці:

   1 2 3     44 52
A= 4 5 6  B= 67 -79
   7 8 9     83 90

буде введено як:

./a.out "1 2 3,4 5 6,7 8 9" "44 52,67 -79,83 90"

Отримана матриця виводиться в STDOUT як багаторядковий рядок. Наприклад, вихід для вищевказаного вводу буде:

 427 164 
1009 353 
1591 542 

TIO 539 байт
girobuz

0

Clojure, 60 байт

#(for[a %](for[b(apply map vector %2)](apply +(map * a b))))

Багато байт витратили на перенесення 2-го аргументу.


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.