행렬 곱셈을하십시오!


14

수학에서 행렬 곱셈 또는 행렬 곱은 두 행렬에서 행렬을 생성하는 이항 연산입니다. 이 정의는 응용 수학, 물리학 및 공학에 수많은 응용 프로그램이있는 벡터의 선형 방정식 및 선형 변환에 의해 동기가 부여됩니다. 보다 구체적으로, A가 n × m 행렬이고 B가 m × p 행렬 인 경우, 그들의 행렬 곱 AB는 n × p 행렬이며, 여기서 A 행의 m 항목은 m 항목과 a를 곱합니다. B의 열과 합쳐서 AB의 항목을 생성합니다. 두 개의 선형 변환이 행렬로 표시되면 행렬 곱은 두 변환의 구성을 나타냅니다.

출처 : Wikipedia

즉, 두 행렬을 곱하려면

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

첫 번째 행렬에서의 제 1 행렬의 행 번호 1, 칼럼 번호 1을하고 번성 1하여 1, 2334.

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

결과의 첫 번째 열에있는 두 번째 숫자의 경우 행 번호 1 대신 행 번호 2를 가져 와서 동일한 작업을 수행해야합니다.

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

당신의 작업

-10000에서 10000 사이의 숫자를 포함하는 두 개의 행렬 (최대 크기 200x200)이 주어지면 첫 번째 행의 열 수는 두 번째 행의 수와 같고 첫 번째 행에 두 번째 행을 곱합니다. (행렬 곱셈은 비정규 적입니다.)

입력을 받아서 배열 (또는 이에 상응하는), 행렬 (언어가 해당 형식을 가진 경우) 또는 여러 줄 문자열의 배열로 출력 할 수 있습니다.

행렬 곱셈에는 내장 함수를 사용할 수 없습니다.

테스트 사례

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

이것은 이므로 바이트 수가 가장 적은 코드가 이깁니다.


내장 된 도트 제품을 사용할 수 있습니까? 행렬이 아닌 벡터에서 작동합니다.
Dennis

1
입력 순서가 고정되어 있습니까? 아니면 ab 를 순서대로 가져 와서 b x a를 출력 할 수 있습니까?
Dennis

@Dennis 입력을 되돌릴 수 있지만 도트 제품은 없습니다
Oliver Ni

4
Y없이 X를하는 것에 대한 도전 은 권장 되지 않습니다 .
flawr

입력 행렬에 부동 소수점 숫자가 포함될 수 있습니까? 그렇다면 테스트 케이스를 추가하는 것이 좋습니다.
R. Kap

답변:


5

젤리 , 7 5 바이트

Z×þḅ1

소요 B 인수로 리턴 × 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

@Yodle 내장 æ×은입니다 (2 바이트).
Outgolfer Erik

@EriktheOutgolfer 그것은 æ.원자 를 사용한 개정 2를 참조한 것 입니다.
Dennis

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
케빈 Cruijssen

4

파이썬 2, 69 66 바이트

이것은 표준 공식을 따르지 만 간결함을위한 람다 -d :) ungolfed 코드는 매우 간단합니다!

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

3 바이트를 절약 한 Alexi Torhamo에게 감사합니다! :)

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 바이트를 절약 할 수 있습니다 . (부동 소수점으로 작동하지는 않지만 필수 사항은 아닙니다.)
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 바이트 동안 우연히 만났다
마일

@ 마일스 장관!
코너 오브라이언



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 매트릭스와 함께 외부가 어떻게 작동하는지 잘 모르겠지만이 경우 4D 배열을 생성합니다.
Billywob

아 네, 조금 문제가 있습니다. 행렬을 선형화하는 것은 여기에서 접근하는 것보다 더 많은 바이트를 필요로합니다.
rturnbull

2

Mathematica, 20 바이트

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

익명의 기능. 2 개의 순위 -2 숫자 목록을 입력으로 취하고 2 등급의 숫자 목록을 출력으로 리턴합니다. 궁금한 Inner점은 두 함수를 두 텐서에 행렬 곱셈과 같은 방식으로 적용하는 함수입니다.


나는 ... Inner[1##&,##]&과 동등 하다고 생각 Inner[1##&,##,Plus]&합니까? 그리고 1##&~Inner~##&더 나은 것입니다.
Greg Martin

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;};

1 바이트를 절약 해 주신 @Mukul Kumar에게 감사드립니다. 이번에는 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 루프를 사용하여 몇 바이트를 트리밍 할 수 있습니다.
Mukul Kumar

@MukulKumar 잠깐만, 난 그렇게 생각하지 않습니다. 기껏해야 그들은 심지어 바르게? for(;i<n;)-> while(i<n)는 모두 10 바이트입니다.
Yodle

1
for (;i <n;i++) -> while (i++<n)1 바이트 절약
Mukul Kumar

내가 상당히 다른 답변을 받았을 때의 에티켓을 확신하지 못했지만 내 대안은 분명히 이것에서 영감을 얻었습니다.
커크 브로드 허스트

2

MATL , 12 11 바이트

7L&!*Xs6Be!

행렬은 ;행 구분자로 사용 됩니다.

온라인으로 사용해보십시오!

내장되지 않은 행렬 곱셈은 Showcase of languages에 대한 나의 대답의 일부였습니다 . 그러나이 답변에 원래 코드를 재사용하려고하면 버그가 있음을 알았습니다 (행 벡터 출력이 열 벡터로 잘못 변환되었습니다). 이것은 이제 여기저기서 수정되었습니다. 코드 작동 방식에 대한 설명은 참조 된 게시물 (길이 -11 스 니펫)을 참조하십시오.


2

C ++ 14 173 168 156 146 바이트

  • 참조 매개 변수를 통해 리턴하는 경우 -5 바이트
  • foreach를 사용하고 C.back()대신 사용하기위한 -12 바이트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()않겠습니까?
G. Sliepen

2

껍질 , 7 6 바이트

mMδṁ*T

인수 순서를 적어두고 온라인으로 시도하십시오!

@Zgarb 덕분에 -1 바이트!

설명

기본적으로 행렬 곱셈의 정의가하는 것을 수행하십시오.

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

자바 스크립트 (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를 사용하여보다 효율적으로 작성할 수 있다고 가정하여 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;
    }

}

Nice : P Linq를 그렇게 많이 사용하지는 않았기 때문에 Linq의 모든 기능을 완전히 인식하지 못했기 때문에 표준 루프와 항목 만 사용하는 경향이 있습니다. 그러나 사용하는 System.Linq를 포함시켜야한다고 생각합니다. 바이트 수의 줄에 영향을 미치는지 확실하지 않습니다.
Yodle

@ Yodle 예 포함해야합니다 using System.Linq; 여기 솔루션은 보일러 등을 포함 할 필요가있어 확인하는 경우 using Systemstatic void Main()
커크 Broadhurst

나는 지금 조금 대답하고 있으며, 내가 본 것에서, 기본적으로 당신의 바이트 수에 포함 된 당신의 대답은 프로그램에 붙여 넣으면 작동해야합니다. 특히 C #의 경우 함수 만 작성하는 경우 클래스 정의 또는 정적 void Main () 항목을 포함 할 필요가 없지만 솔루션에서 Console.WriteLine ()과 같은 라이브러리 항목을 사용하는 경우 수행해야합니다 System.Console.WriteLine () 또는 시스템 사용; 하나가 더 짧을 수 있기 때문입니다.
Yodle

1

하스켈 , 49 바이트

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

온라인으로 사용해보십시오!

입력 및 출력은 열 목록입니다. 두 번째 행렬의 각 열을 해당 행에 매핑하고 첫 번째 행렬의 열로 압축하고 각각의 축척을 벡터로 합산합니다.

나는이 포인트를 자유롭게 만들고 소수의 바이트를 절약 할 수있는 좋은 방법이 있어야한다고 생각하지만 아직 보지 못했습니다.


0

자바 스크립트, 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;}

11 개의 배열을위한 3 개의 루프. 이것은 매우 직설적이지만 ... 골프는별로 없습니다.


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 2 차원 정수 배열로 문자 배열 입력을 변환하는 가장 바이트를 차지한다는 사실에 여기에서 가장 긴 제출, 또한 나는 긴 시간에 C에서 golfed하지 않았기 때문에. 나는 이것을 가능한 한 짧게하기 위해 노력하고 있으며, 그렇게하는 데 도움이되는 팁은 대단히 높이 평가됩니다.

이제이 방법을 사용하면 명령 행을 통해 두 개의 문자열로 표시되는 두 개의 행렬이 입력됩니다. 각 행은 쉼표로 구분 된 행과 공백으로 구분 된 정수로 표시되는 각 행을 포함합니다. 예를 들어, 행렬 :

   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

클로저, 60 바이트

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

두 번째 인수를 바꾸는 데 많은 바이트가 사용되었습니다.


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.