Делай Матричное Умножение!


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

Твое задание

Учитывая две матрицы (максимальные размеры 200x200), содержащие числа в диапазоне от -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 ?
Денис

@ Денис Вы можете изменить вход, но без точечных произведений
Оливер 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 Встроенный is æ×, который составляет 2 байта.
Эрик Outgolfer

@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

Python 2, 69 66 байт

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

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

Спасибо Алекси Торхамо за сохранение 3 байта! :)

Код Ungolfed:

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 байтов
миль

@ Мили впечатляющие!
Конор О'Брайен

3

Haskell , 57 56 54 байта

e=[]:e
z=zipWith
a!b=[sum.z(*)r<$>foldr(z(:))e b|r<-a]

Попробуйте онлайн!

Использование:

Prelude> [[1,2],[3,4],[5,6]] ! [[1,2,3,4,5],[6,7,8,9,10]]
[[13,16,19,22,25],[27,34,41,48,55],[41,52,63,74,85]]

foldr(zipWith(:))eс e=[]:eявляется более короткой формой transpose.



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

Mathematica, 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 байта, на этот раз цикл while был на самом деле короче: 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;
    }
}

Вы можете обрезать несколько байтов, используя циклы while ....
Мукул Кумар

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

1
for (;i <n;i++) -> while (i++<n)сохраняет 1 байт
Mukul Kumar

Не уверен в этикете, когда у меня есть совершенно другой ответ, но моя альтернатива была определенно вдохновлена ​​этим.
Кирк Бродхерст

2

MATL , 12 11 байт

7L&!*Xs6Be!

Матрицы вводятся с использованием в ;качестве разделителя строк.

Попробуйте онлайн!

Умножение матриц без встроенного было частью моего ответа на Showcase языков . Однако, пытаясь повторно использовать исходный код для этого ответа, я понял, что в нем есть ошибка (вывод вектора строки был неправильно преобразован в вектор столбца). Это теперь исправлено, и здесь, и там. Для объяснения того, как работает код, см. Упомянутый пост (фрагмент длины 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>>вывод должен быть пустым заранее.

Ungolfed:

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()))));

Я украл решение Йодля с предположением, что я мог бы написать это более эффективно, используя LINQ (в отличие от циклов for). Сделал несколько попыток, но несколько сломал его.

Здесь это несколько разбито:

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) и Zip каждый из этих 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; строка в вашем числе байтов, не уверен, насколько это влияет.
Йодль

@ Йодль да, я должен был бы включить using System.Linq; Я не уверен, что решения должны включать шаблон, как using Systemиstatic void Main()
Кирк Бродхерст

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

1

Haskell , 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 2-мерные целочисленные массивы занимает больше байтов, а также потому, что я не играл в гольф в 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 байт
гиробуз

0

Clojure, 60 байт

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

Много байтов потрачено на транспонирование второго аргумента.


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.