符号矩阵乘法


26

有很多不同的方法来解释矩阵乘法。我会坚持一个数字,因为我相信这里的大多数人都熟悉它(这个数字非常具有描述性)。如果您需要更多详细信息,建议您访问Wikipedia文章WolframMathWorld上的说明。

简单说明:

假设您有两个矩阵AB,其中A是3×2,而B是2×3。如果对矩阵ABBA进行矩阵乘法,将得到以下结果:

在此处输入图片说明

挑战:

用您的语言实现符号矩阵乘法。您将采用两个矩阵作为输入,其中矩阵中的每个元素都由一个非空白ASCII字符(代码点33-126)表示。您必须输出这些矩阵的乘积。

有关输出的规则:

两个条目的乘积之间不应有任何符号。这aba*ba·btimes(a,b)或者类似的东西。这aa,不是a^2

项的总和之间应有一个空格(ASCII码点32)。这a ba+bplus(a,b)或者类似的东西。

这两个规则的基本原理是:矩阵中所有非空白字符都可以用作符号,因此将它们用作数学符号会很麻烦。所以,你可以正常写成a*b+c*dab cd

您可以选择条款的顺序。ab cddc abcd ba在数学上来说是相同的,所以你可以在这里选择的顺序了。只要在数学上正确,顺序就不必一致。

有关矩阵格式的规则:

矩阵可以以任何您喜欢的格式输入,除了在行之间没有定界符的单个字符串(这是因为输出将被完全弄乱)。两个矩阵必须以相同的格式输入。以下所有示例都是输入和输出矩阵的有效方法。

"ab;cd"     <- This will look awful, but it's still accepted.

"a,b\nc,d"

[[a,b],[c,d]] 

[a, b]
[c, d]

我知道这允许许多看起来很杂乱的格式,但是挑战在于要乘以矩阵而不是格式化输出。

一般规则:

  • 您可以假设输入有效。在给定尺寸下,矩阵乘法始终是可能的。
  • 只有两个矩阵。
  • 您可以假设矩阵是非空的
  • 接受内置函数(但由于格式要求,可能有点麻烦)。
  • 当然,如果需要,您当然可以在输入中使用转义符(\'而不是')。
  • 任何标准的输入和输出方法都可以

测试用例:

所示的两个输入矩阵之间有一条空线。输出显示在之后Output:。当有两个输出矩阵时,这只是显示其他可以接受的输出。

测试用例#1

Inputs:
[a]

[b]

Output:
[ab]
[ba]      <- Also OK

测试案例#2

Inputs:
[a, b]
[1, 4] 
[y, {]

[%, 4, 1] 
[a, b, c]

Output:    
[a% ba, a4 bb, a1 bc] 
[1% 4a, 14 4b, 11 4c] 
[y% {a, y4 {b, y1 {c]

测试案例#3:

Inputs:
[1, 2, 3, 4]
[5, 6, 7, 8]
[9, 1, 2, 3]
[4, 5, 6, 7]

[a]
[b]
[c]
[d]

Output:
[1a 2b 3c 4d]
[5a 6b 7c 8d]
[9a 1b 2c 3d]
[4a 5b 6c 7d]

[d4 c3 b2 a1]      <-- Also OK
[d8 c7 b6 a5]
[1b 9a c2 3d]
[a4 b5 d7 6c]

如果您对要求ab cd而不是的规则的回应a*b+c*d是:应该避免使用繁琐的输入/输出格式,那么我想指出一下,输入和输出格式非常灵活。您不能使用*+用于乘积和总和的事实可能会使使用简单的内置函数变得更加困难,但是我不认为这是消极的事情。


对于一个函数,是否可以接受两个2D字符串数组并返回一个2D字符串数组?
丹尼斯,

是没有问题。但是尺寸必须匹配,您不能转换第二个输入。这有意义吗?
Stewie Griffin

确实如此,感谢您的澄清。最后一个问题:能否将2D 字符数组作为输入并返回2D字符串数组?
丹尼斯,

@Dennis,我写道:“两个矩阵都必须以相同的格式输入。” 我忘了在那儿提到输出矩阵,所以我就这样保留它。输入必须使用相同的格式,但是您可以使用不同的输出格式。(我不太喜欢这种解决方案,但是我现在不想更改任何东西,我认为已经有一个答案具有不同的输入和输出格式)
Stewie Griffin

如果您是用Ruby回答的话,我检查了一下,发现它对于字符串而不是字符也同样适用。
丹尼斯,

Answers:


9

Haskell62 61字节

e=[]:e
a!b=[unwords.zipWith(++)r<$>foldr(zipWith(:))e b|r<-a]

在线尝试!用法示例:

Prelude> [["a","b"],["c","e"]] ! [["f","g"],["h","i"]]
[["af bh","ag bi"],["cf eh","cg ei"]]

我找到了一种transpose比使用导入短一个字节的函数的方法:

import Data.List;transpose
e=[]:e;foldr(zipWith(:))e

带有导入的旧版本:(62个字节)

import Data.List
a!b=[unwords.zipWith(++)r<$>transpose b|r<-a]

在线尝试!

这与我对非符号矩阵乘法的回答非常相似:a!b=[sum.zipWith(*)r<$>transpose b|r<-a](*)用字符串串联代替乘法,(++)sumunwords字符串之间的空格串联起来。该transpose功能需要导入,因此第二个矩阵的所有转置全部占用了一半的字节...


没有导入的旧版本:(64字节)

a![]=[];a!b=(unwords.zipWith(++)[h|h:_<-b]<$>a):a![s:t|_:s:t<-b]

在线尝试!

由于import和transposefunction占用了很多字节,因此我尝试不导入就解决任务。到目前为止,这种方法原来要长两个字节,但它可能更适用于高尔夫。编辑:顶部的另一种方法现在击败了进口!

列表推导[s:t|_:s:t<-b]会获取中的列表的非空尾部b,仅[t|_:t<-b]用于获取尾部将短4个字节(甚至击败导入版本),但["","",""]在矩阵中添加空行,这是我认为不允许的。


6

Mathematica,36个字节

Inner[#<>#2&,##,StringRiffle@*List]&

Inner是Mathematica的推广Dot(即常用的矩阵/矢量乘积)。通过让您提供两个函数f和来概括点积g,将分别使用它们代替通常的乘法和加法。我们将用替换乘号#<>#2&(将两个字符连接成一个字符串),用替换加法StringRiffle@*List,其首先将所有求和式包装在列表中,然后StringRiffle将它们与空格连接在一起。

一个人可能会使用Dot运算符.,然后转换结果,但是麻烦的是,类似的东西"a"*"a"会立即转换成"a"^2(和总和),这很烦人。


6

Ruby,61个字节

->a,b{a.map{|x|b.transpose.map{|y|x.zip(y).map(&:join)*' '}}}

样品运行:

main(0):007> ->a,b{a.map{|x|b.transpose.map{|y|x.zip(y).map(&:join)*' '}}}[[[?a, ?b], [?1, ?4], [?y, ?{]], [[?%, ?4, ?1], [?a, ?b, ?c]]]
=> [["a% ba", "a4 bb", "a1 bc"], ["1% 4a", "14 4b", "11 4c"], ["y% {a", "y4 {b", "y1 {c"]]
->a,b{
a.map{|x|            # for each row of a
b.transpose.map{|y|  # and for each column of b
x.zip(y)             # match up corresponding elements
.map(&:join)         # join each pair together
*' '                 # join the entire thing on space
}}}

4

Clojure,53个字节

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

用参数[["a" "b"]["c" "e"]][["f" "g"]["h" "i"]]返回运行((("af" "bh") ("ag" "bi")) (("cf" "eh") ("cg" "ei")))。实际上比数字版本短。


3

Dyalog APL,10 个字节

将字符矩阵作为左右参数。返回字符列表的矩阵。(APL将字符串表示为字符列表。)

{∊⍺' '⍵}.,

在线尝试APL!

普通的内积在APL中+.×,但是加法和乘法可以是任何函数,尤其是:

加法已被
{ 匿名函数代替: 由左参数,空格和右参数组成
 的扁平化
⍺ ' ' ⍵列表
}

乘法已被串联取代, ,


2

果冻,7个字节

Z;"þK€€

这是一条二进位链接,将BA作为参数(按此顺序)并返回AB。输入和输出采用2D字符串数组的形式,实际上是3D字符数组。通过将2D 字符数组作为输入可以节省更多的字节。我不确定是否允许。

在线尝试!

当涉及到字符串时,很难确定Jelly在引擎盖下的作用,因为在打印之前它会进行很多喷溅。这就是Jelly在内部表示输入和输出的方式。

怎么运行的

Z;"þK€€  Dyadic link. Left argument: B. Right argument: A

Z        Zip/transpose B.
 ;"þ     Table vectorized concatenation; for each row in B' and each row in A,
         concatenate the corresponding strings.
    K€€  Join the arrays that correspond to the rows of A by spaces.

2

Prolog,> 256字节

我正在使用{_ | _}是findall / 3,_ [_,_]是一些arg / 3,sum(_)是一些聚合。它们都可以在is / 2内部使用:

*(X, Y, Z) :- functor(Y, matrice, _), L is len(X[1]), L =:= len(Y), !,
   M is len(X), N is len(Y[1]),
   Z is { { sum({ X[J,K] * Y[K,I] | between(1,L,K) })
                                  | between(1,N,I) }
                                  | between(1,M,J) }.

再加上上述谓词的额外定义和可以返回不只数字的非标准is / 2,其肯定大于256字节。


1

JavaScript(ES6),65个字节

(a,b)=>a.map(c=>b[0].map((_,j)=>c.map((e,i)=>e+b[i][j]).join` `))

将输入作为两个2D字符数组并返回一个2D字符串数组。添加10个字节以支持输入为两个一维字符串数组。


1

Pyth,14个字节

clQmj;sMCd*QCE

一个程序,它输入两个以换行符分隔的二维字符列表,并打印一个二维字符串列表。

测试套件

怎么运行的

[稍后再讲]


1

,17字节

{Y Zb{a._JsMy}Ma}

此函数接受两个(单个字符)字符串的嵌套列表并返回一个嵌套的字符串列表。在线尝试!(带有两个测试用例)。

说明

参数的{}-delimited功能分配给本地变量ae。Lambda函数的第一个参数由表示_

{               }  Define a function:
   Zb              Zip rows of b, transposing it
 Y                 Yank into global variable y for access in nested function
     {       }Ma   To the rows of a, map this function:
           My       To the rows of y (i.e. columns of b), map this function:
      a._           Concatenate, itemwise, the current row of a and row of y
         Js         Join the resulting list on space
                   The result of the outer map operation is returned
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.