做矩阵乘法!


14

在数学中,矩阵乘法或矩阵乘积是一种从两个矩阵生成矩阵的二进制运算。该定义受矢量上的线性方程式和线性变换的启发,在应用数学,物理学和工程学中有许多应用。更详细地讲,如果A是n×m矩阵,而B是m×p矩阵,则它们的矩阵乘积AB是n×p矩阵,其中A行中的m个条目与m个条目在a处相乘。 B列并求和以产生AB条目。当两个线性变换由矩阵表示时,则矩阵乘积代表两个变换的组成。

资料来源:维基百科

换句话说,将两个矩阵相乘,例如:

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

首先,采取在第一矩阵的行号1,列号1中的第二矩阵,和乘法1通过12通过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
输入顺序是否固定,或者我们可以按该顺序取ab并输出b×a吗?
丹尼斯

@Dennis您可以撤消输入,但不能使用点积
Oliver Ni

4
鼓励在没有Y情况下执行X的挑战。
瑕疵的

输入矩阵可以包含浮点数吗?如果是这样,我建议添加一些测试用例。
卡普

Answers:


5

果冻7 5 字节

Z×þḅ1

BA作为参数并返回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
因此,等等,在矩阵果冻中,内置和手动相乘矩阵的方式最终得到的字节数相同吗?令人困惑,但很酷。
2016年

@Yodle内置为æ×,即2个字节。
暴民埃里克(Erik the 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
Kevin Cruijssen

4

Python 2,69 66字节

这只是遵循标准公式,但为简洁起见,使用lambda -d :)非高尔夫代码非常简单!

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

感谢Alexi Torhamo节省了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个字节。(不适用于浮点数,但这也不是必需的)
Aleksi Torhamo

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

3

Haskell57 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(:))ee=[]: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,返回(向量化)乘积的总和。

与纯for循环方法(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维数组。
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个字节。
Yodle

1
for (;i <n;i++) - > while (i++<n)保存1个字节
穆库尔库马尔

当我给出完全不同的答案时,我不确定礼节,但是我的选择无疑是受此启发的。
Kirk Broadhurst

2

MATL12 11字节

7L&!*Xs6Be!

使用;行分隔符输入矩阵。

在线尝试!

没有内置函数的矩阵乘法是对语言展示的回答的一部分。但是,当尝试将原始代码重用于此答案时,我意识到它存在错误(行向量输出被错误地转换为列向量)。现在,无论在这里还是在这里,都已得到纠正。有关代码的工作原理的解释,请参见引用的文章(长度为11的代码段)。


2

C ++ 14,173个 168 156 146字节

  • -5字节用于通过参考参数返回
  • -12个字节,用于使用foreach而C.back()不是依靠i
  • -10字节用于删除,C.clear()并且要求C在开始时为空

作为未命名的lambda:

[](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()
G. Sliepen

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循环)更有效地编写此代码为前提,偷了Yodle的解决方案。进行了几次尝试,但有所降低。

在此将其分解:

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

@Yodle是的,我将不得不包括在内using System.Linq;我不确定此处的解决方案是否需要包含样板,using System以及static void Main()
Kirk Broadhurst

我已经回答了一段时间,从我所看到的情况来看,如果您只是将其粘贴到程序中,则基本上您的答案(无论您要包括在字节数中)都必须起作用。特别是对于C#,如果只编写一个函数,则不需要包含类定义或静态void Main()东西,但是,如果您的解决方案使用诸如Console.WriteLine()之类的任何库东西,则必须这样做System.Console.WriteLine()或使用System; 因为一个可能会更短。
Yodle

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╡

开球

         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二维整数数组占用的字节最多,而且还因为我没有在最长时间内使用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))))

许多字节用于转置第二个参数。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.