Multiplicação de matriz simbólica

26

Existem muitas maneiras diferentes de explicar a multiplicação de matrizes. Vou ficar com uma única figura, pois acredito que a maioria das pessoas aqui está familiarizada com ela (e a figura é muito descritiva). Se você quiser informações mais detalhadas, sugiro que visite o artigo da Wikipedia ou a explicação sobre o WolframMathWorld .

Explicação simples:

Suponha que você tenha duas matrizes, A e B , onde A é 3 por 2 e B é 2 por 3. Se você executar a multiplicação de matrizes nessas matrizes, AB ou BA, obterá os resultados abaixo:

insira a descrição da imagem aqui

Desafio:

Implemente a multiplicação matricial simbólica no seu idioma. Você deve usar duas matrizes como entrada, em que cada elemento nas matrizes é representado por um caractere ASCII que não é um espaço em branco (pontos de código 33-126). Você deve produzir o produto dessas matrizes.

Regras relativas à saída:

Um produto de duas entradas não deve ter nenhum símbolo no meio. É ab, não a*b, a·b, times(a,b)ou algo similar. É aa, não a^2.

A soma dos termos deve ter um espaço (ponto 32 do código ASCII) no meio. É a b, não a+b, plus(a,b)ou algo semelhante.

A lógica para essas duas regras é: todos os caracteres de espaço não em branco são permitidos como símbolos nas matrizes, portanto, usá-los como símbolos matemáticos seria uma bagunça. Então, o que você normalmente poderia escrever como a*b+c*dserá ab cd.

Você pode escolher a ordem dos termos. ab cd, dc abe cd bamatematicamente estão falando o mesmo, então você também pode escolher o pedido aqui. O pedido não precisa ser consistente, desde que seja matematicamente correto.

Regras relativas à formatação da matriz:

Uma matriz pode ser inserida no formato que você quiser, exceto uma única sequência sem delimitadores entre linhas (isso ocorre porque a saída seria completamente confusa). Ambas as matrizes devem ser inseridas no mesmo formato. Todos os exemplos abaixo são formas válidas de inserir e produzir uma matriz.

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

"a,b\nc,d"

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

[a, b]
[c, d]

Estou ciente de que isso permite muitos formatos que parecerão confusos, mas o desafio é multiplicar matrizes, não formatar a saída.

Regras gerais:

  • Você pode assumir uma entrada válida. A multiplicação de matrizes sempre será possível com as dimensões especificadas.
  • Haverá apenas duas matrizes.
  • Você pode assumir que as matrizes não estão vazias
  • Funções internas são aceitas (mas provavelmente um pouco complicadas devido aos requisitos de formatação).
  • Obviamente, você pode usar caracteres de escape na entrada, se necessário (em \'vez de ').
  • Qualquer método padrão de entrada e saída está OK .

Casos de teste:

As duas matrizes de entrada são mostradas com uma linha vazia no meio. A saída é mostrada depois Output:. Quando existem duas matrizes de saída, é apenas para mostrar outras saídas que seriam aceitas.

Caso de teste nº 1

Inputs:
[a]

[b]

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

Caso de teste nº 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]

Caso de teste nº 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]

Se a sua resposta às regras sobre exigir em ab cdvez de a*b+c*dfor: você deve evitar formatos de entrada / saída complicados , gostaria de observar que os formatos de entrada e saída são muito flexíveis. O fato de você não poder usar *e +para produtos e somas pode dificultar o uso de um simples embutido, mas não considero isso negativo.

Stewie Griffin
fonte
Para uma função, aceitar duas matrizes 2D de strings e retornar um array 2D de strings é aceitável?
Dennis
Sim não tem problema. Mas as dimensões devem corresponder, não é possível que a segunda entrada seja transposta. Aquilo fez sentido?
Stewie Griffin
Sim, obrigado por esclarecer. Uma última pergunta: eu também poderia pegar uma matriz 2D de caracteres como entrada e retornar uma matriz 2D de seqüências de caracteres?
Dennis
@ Dennis, escrevi: "Ambas as matrizes devem ser inseridas no mesmo formato". Eu esqueci de mencionar a matriz de saída lá, então eu continuarei assim. As entradas devem estar no mesmo formato, mas você pode ter um formato de saída diferente. (Eu realmente não gosto dessa solução, mas eu não quero coisas mudança agora, eu acho que há uma resposta já que tem diferentes formatos de entrada e saída)
Stewie Griffin
Se você quer dizer a resposta Ruby, eu verifiquei e essa funciona tão bem com strings em vez de caracteres.
Dennis

Respostas:

9

Haskell , 62 61 bytes

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

Experimente online! Exemplo de uso:

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

Eu encontrei uma maneira de obter uma transposefunção em um byte menor do que usando a importação:

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

Versão antiga com importação: (62 bytes)

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

Experimente online!

Isso é bastante semelhante à minha resposta à multiplicação matricial não simbólica : a!b=[sum.zipWith(*)r<$>transpose b|r<-a]substituindo a multiplicação (*)pela concatenação de strings (++)e sumcom a unwordsqual concatenamos uma lista de strings com um espaço no meio. A importação é necessária para a transposefunção, portanto, em toda a transposição da segunda matriz, consome metade dos bytes ...


Versão antiga sem importação: (64 bytes)

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

Experimente online!

Com a importação e a transposefunção ocupando muitos bytes, tentei resolver a tarefa sem importar. Até agora, essa abordagem acabou sendo dois bytes mais longa, mas poderia ser mais fácil de jogar. Edit: A outra abordagem no topo agora supera a importação!

A compreensão da lista [s:t|_:s:t<-b]obtém as caudas não vazias das listas b, usar apenas [t|_:t<-b]para obter as caudas seria 4 bytes mais curto (até superando a versão de importação), mas acrescenta uma linha vazia como ["","",""]a matriz que, suponho, não é permitida.

Laikoni
fonte
6

Mathematica, 36 bytes

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

Inneré uma generalização do Mathematica Dot(isto é, o produto matriz / vetor usual). Ele generaliza o produto escalar, permitindo que você forneça duas funções fe g, que serão usadas no lugar da multiplicação e adição usuais, respectivamente. Estamos substituindo a multiplicação por #<>#2&(que une os dois caracteres em uma única sequência de caracteres) e a adição por StringRiffle@*List, que primeiro agrupa todos os summands em uma lista e os StringRiffleune a espaços.

Pode-se potencialmente usar o Dotoperador .e depois transformar o resultado, mas o problema é que coisas como essas "a"*"a"seriam imediatamente transformadas em "a"^2(o mesmo para somas), o que seria irritante para separar novamente.

Martin Ender
fonte
6

Ruby, 61 bytes

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

Exemplo de execução:

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
}}}
Maçaneta da porta
fonte
4

Clojure, 53 bytes

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

Correndo com argumentos [["a" "b"]["c" "e"]]e [["f" "g"]["h" "i"]]retornos ((("af" "bh") ("ag" "bi")) (("cf" "eh") ("cg" "ei"))). Na verdade, é mais curto que a versão numérica .

NikoNyrh
fonte
3

Dyalog APL , 10 bytes

Toma matrizes de caracteres como argumentos esquerdo e direito. Retorna a matriz de listas de caracteres. (APL representa cadeias de caracteres como listas de caracteres.)

{∊⍺' '⍵}.,

TryAPL online!

O produto interno normal está no APL +.×, mas a adição e multiplicação podem ter quaisquer funções, em particular:

A adição foi substituída por
{ uma função anônima:
 a
⍺ ' ' ⍵ lista nivelada que consiste no argumento esquerdo, um espaço e o argumento correto
}

A multiplicação foi substituída pela concatenação, ,

Adão
fonte
2

Geléia , 7 bytes

Z;"þK€€

Este é um link diádico que recebe B e A como argumentos (nessa ordem) e retorna AB . Entrada e saída estão na forma de matrizes 2D de seqüências de caracteres, que são na verdade matrizes 3D de caracteres. Um byte adicional pode ser salvo usando matrizes 2D de caracteres como entrada. Não tenho certeza se isso é permitido.

Experimente online!

É um pouco difícil determinar o que a Jelly faz sob o capô quando as cordas estão envolvidas, pois faz muito barulho antes da impressão. É assim que o Jelly representa entrada e saída internamente.

Como funciona

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.
Dennis
fonte
2

Prolog,> 256 bytes

Eu estou usando {_ | _} que é um findall / 3, _ [_, _] que é um pouco de arg / 3 e soma (_) que é uma agregação. Todos eles podem ser usados ​​dentro de / 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) }.

Juntamente com as definições extras para os predicados mencionados acima e o não padrão é / 2 que pode retornar mais que números, é certo que> 256 bytes.

Números Transfinitos
fonte
1

JavaScript (ES6), 65 bytes

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

Recebe a entrada como duas matrizes 2D de caracteres e retorna uma matriz 2D de seqüências de caracteres. Adicione 10 bytes para suportar a entrada como duas matrizes 1D de cadeias.

Neil
fonte
1

Pitão, 14 bytes

clQmj;sMCd*QCE

Um programa que recebe entrada de duas listas bidimensionais de caracteres separadas por nova linha e imprime uma lista bidimensional de seqüências de caracteres.

Suíte de teste

Como funciona

[Explicação que vem depois]

TheBikingViking
fonte
1

Pip , 17 bytes

{Y Zb{a._JsMy}Ma}

Essa é uma função que pega duas listas aninhadas de seqüências de caracteres (um caractere) e retorna uma lista aninhada de seqüências de caracteres. Experimente online! (com dois casos de teste).

Explicação

Argumentos para uma {}função delimitada são atribuídos às variáveis ​​locais apara e. O primeiro argumento de uma função lambda é representado por _.

{               }  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
DLosc
fonte