Symboliczne mnożenie macierzy

26

Istnieje wiele różnych sposobów wyjaśnienia mnożenia macierzy. Będę trzymać jedną cyfrę, ponieważ uważam, że większość ludzi tutaj jest z nią zaznajomiona (a ta liczba jest bardzo opisowa). Jeśli potrzebujesz bardziej szczegółowych informacji, sugeruję odwiedzić artykuł w Wikipedii lub wyjaśnienie na temat WolframMathWorld .

Proste wyjaśnienie:

Załóżmy, że masz dwie macierze, A i B , gdzie A to 3 na 2, a B to 2 na 3. Jeśli wykonasz mnożenie macierzy na tych macierzach, AB lub BA , otrzymasz wyniki poniżej:

wprowadź opis zdjęcia tutaj

Wyzwanie:

Zaimplementuj symboliczne mnożenie macierzy w swoim języku. Jako dane wejściowe weźmiesz dwie macierze, gdzie każdy element w macierzach jest reprezentowany przez znak ASCII niebiałej spacji (punkty kodowe 33-126). Musisz wyprowadzić iloczyn tych macierzy.

Zasady dotyczące wyników:

Iloczyn dwóch pozycji nie może zawierać żadnych symboli. To ab, nie a*b, a·b, times(a,b)lub coś podobnego. To aanie a^2.

Suma warunków powinna zawierać spację (punkt 32 kodu ASCII) pomiędzy nimi. To a b, nie a+b, plus(a,b)lub coś podobnego.

Uzasadnieniem dla tych dwóch zasad jest: Wszystkie znaki niebiałe spacje są dozwolone jako symbole w macierzach, dlatego używanie ich jako symboli matematycznych byłoby bałaganem. Więc to, co normalnie możesz napisać, a*b+c*djakie będzie ab cd.

Możesz wybrać kolejność warunków. ab cd, dc abI cd basą matematycznie rzecz biorąc takie same, więc można wybrać kolejność tu. Kolejność nie musi być spójna, o ile jest matematycznie poprawna.

Zasady dotyczące formatowania macierzy:

Macierz można wprowadzić w dowolnym formacie, z wyjątkiem pojedynczego ciągu bez ograniczników między wierszami (dzieje się tak, ponieważ dane wyjściowe zostałyby całkowicie pomieszane). Obie macierze muszą być wprowadzane w tym samym formacie. Wszystkie poniższe przykłady są prawidłowymi sposobami wprowadzania i wysyłania macierzy.

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

"a,b\nc,d"

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

[a, b]
[c, d]

Wiem, że pozwala to na wiele formatów, które będą wyglądały na bałagan, ale wyzwanie polega na pomnożeniu macierzy, a nie na sformatowaniu danych wyjściowych.

Główne zasady:

  • Możesz założyć prawidłowe dane wejściowe. Mnożenie macierzy zawsze będzie możliwe przy podanych wymiarach.
  • Będą tylko dwie matryce.
  • Możesz założyć, że macierze nie są puste
  • Wbudowane funkcje są akceptowane (ale prawdopodobnie nieco kłopotliwe ze względu na wymagania dotyczące formatowania).
  • Możesz oczywiście użyć znaków zmiany znaczenia w danych wejściowych, jeśli to konieczne ( \'zamiast ').
  • Każda standardowa metoda wejścia i wyjścia jest OK .

Przypadki testowe:

Dwie macierze wejściowe są pokazane z pustą linią pomiędzy nimi. Dane wyjściowe są wyświetlane po Output:. Kiedy są dwie macierze wyjściowe, to po prostu pokazują inne wyjściowe, które zostałyby zaakceptowane.

Przypadek testowy nr 1

Inputs:
[a]

[b]

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

Przypadek testowy nr 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]

Przypadek testowy nr 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]

Jeśli twoją odpowiedzią na zasady dotyczące wymagania ab cdzamiast a*b+c*djest: powinieneś unikać uciążliwych formatów wejścia / wyjścia , to chciałbym zauważyć, że formaty wejścia i wyjścia są bardzo elastyczne. Fakt, że nie można używać *i +dla produktów i kwot może utrudnić użyć prostego wbudowany, ale nie uważam, że nic negatywnego.

Stewie Griffin
źródło
Czy dla funkcji dopuszczalne jest pobranie dwóch tablic 2D ciągów i zwrócenie tablicy ciągów 2D?
Dennis
Tak żaden problem. Ale wymiary muszą się zgadzać, nie można transponować drugiego wejścia. Czy to ma sens?
Stewie Griffin,
Tak, dziękuję za wyjaśnienie. Ostatnie pytanie: czy mogę również wziąć tablicę znaków 2D jako dane wejściowe i zwrócić tablicę ciągów 2D?
Dennis
@Dennis napisałem: „Obie macierze muszą być wprowadzone w tym samym formacie”. Zapomniałem wspomnieć o macierzy wyjściowej, więc zachowam to w ten sposób. Dane wejściowe muszą być w tym samym formacie, ale możesz mieć inny format wyjściowy. (Nie podoba mi się to rozwiązanie, ale nie chcę teraz zmieniać rzeczy, myślę, że jest już jedna odpowiedź, która ma różne formaty wejściowe i wyjściowe)
Stewie Griffin
Jeśli chodzi ci o odpowiedź Ruby, sprawdziłem i ta działa równie dobrze z łańcuchami zamiast znaków.
Dennis

Odpowiedzi:

9

Haskell , 62 61 bajtów

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

Wypróbuj online! Przykładowe użycie:

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

Znalazłem sposób na uzyskanie transposefunkcji o jeden bajt krócej niż przy użyciu importu:

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

Stara wersja z importem: (62 bajty)

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

Wypróbuj online!

Jest bardzo podobna do mojej odpowiedzi na nie-symbolicznym mnożenia macierzy : a!b=[sum.zipWith(*)r<$>transpose b|r<-a], zastępując mnożenie (*)sznurkiem konkatenacji (++)i sumz unwordsktórych skleja listę ciągów z przestrzeni pomiędzy. Import jest potrzebny dla tej transposefunkcji, więc w sumie transpozycja drugiej macierzy zużywa połowę bajtów ...


Stara wersja bez importu: (64 bajty)

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

Wypróbuj online!

Ponieważ import i transposefunkcja zajmują tak dużo bajtów, próbowałem rozwiązać zadanie bez importu. Jak dotąd podejście to okazało się dwa bajty dłuższe, ale może być bardziej grywalne. Edycja: inne podejście u góry wygrywa z importem!

Zrozumienie listy [s:t|_:s:t<-b]pobiera niepuste ogony z list b, użycie tylko, [t|_:t<-b]aby uzyskać ogony, byłoby o 4 bajty krótsze (nawet w porównaniu z wersją importu), ale dołącza pusty wiersz, podobnie jak ["","",""]macierz, która, jak sądzę, jest niedozwolona.

Laikoni
źródło
6

Mathematica, 36 bajtów

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

Innerjest uogólnieniem Mathematiki Dot(tj. zwykłym produktem macierzy / wektora). Uogólnia iloczyn kropkowy, umożliwiając zapewnienie dwóch funkcji fi g, które będą używane zamiast zwykłego mnożenia i dodawania, odpowiednio. Zamieniamy mnożenie na #<>#2&(które łączy dwa znaki w jeden ciąg) i dodawanie na StringRiffle@*List, które najpierw otacza wszystkie sumy na liście, a następnie StringRifflełączy je spacjami.

Można potencjalnie użyć Dotoperatora, .a następnie przekształcić wynik, ale problem polega na tym, że takie rzeczy "a"*"a"natychmiast przekształcą się w "a"^2(to samo w przypadku sum), co byłoby denerwujące, aby rozdzielić je ponownie.

Martin Ender
źródło
6

Rubin, 61 bajtów

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

Przykładowy przebieg:

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
}}}
Klamka
źródło
4

Clojure, 53 bajty

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

Uruchamianie z argumentami [["a" "b"]["c" "e"]]i [["f" "g"]["h" "i"]]zwrotami ((("af" "bh") ("ag" "bi")) (("cf" "eh") ("cg" "ei"))). Jest to w rzeczywistości krótsza wersja numeryczna .

NikoNyrh
źródło
3

Dyalog APL , 10 bajtów

Pobiera macierze znaków jako argumenty lewy i prawy. Zwraca macierz list znaków. (APL reprezentuje ciągi jako listy znaków.)

{∊⍺' '⍵}.,

Wypróbuj APL online!

Normalny produkt wewnętrzny znajduje się w APL +.×, ale dodawanie i mnożenie może być dowolnymi funkcjami, w szczególności:

Dodawanie zostało zastąpione przez
{ anonimową funkcję:
 spłaszczona
⍺ ' ' ⍵ lista składająca się z lewego argumentu, spacji i prawego argumentu
}

Mnożenie zostało zastąpione konkatenacją, ,

Adám
źródło
2

Galaretka , 7 bajtów

Z;"þK€€

To diademiczne łącze, które przyjmuje B i A jako argumenty (w tej kolejności) i zwraca AB . Dane wejściowe i wyjściowe mają postać tablic 2D ciągów, które w rzeczywistości są tablicami znaków 3D. Kolejny bajt można zapisać, przyjmując tablice znaków 2D jako dane wejściowe. Nie jestem pewien, czy to dozwolone.

Wypróbuj online!

Trudno jest ustalić, co Jelly robi pod maską, gdy w grę wchodzą sznurki, ponieważ przed drukowaniem robi się dużo rozpryskiwania. W ten sposób Jelly reprezentuje dane wejściowe i wyjściowe wewnętrznie.

Jak to działa

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
źródło
2

Prolog,> 256 bajtów

Używam {_ | _}, który jest findall / 3, _ [_, _], który jest arg / 3 i suma (_), która jest agregacją. Wszystkie mogą być używane w środku to / 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) }.

Wraz z dodatkowymi definicjami dla wyżej wymienionych predykatów i niestandardowym jest / 2, który może zwrócić więcej niż liczby, jego pewność> 256 bajtów.

Liczby Transfinite
źródło
1

JavaScript (ES6), 65 bajtów

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

Pobiera dane wejściowe jako dwie tablice znaków 2D i zwraca tablicę ciągów 2D. Dodaj 10 bajtów do obsługi danych wejściowych jako dwie tablice 1D łańcuchów.

Neil
źródło
1

Pyth, 14 bajtów

clQmj;sMCd*QCE

Program, który pobiera dwie dwuwymiarowe listy znaków oddzielone znakiem nowej linii i drukuje dwuwymiarową listę ciągów znaków.

Zestaw testowy

Jak to działa

[Wyjaśnienie nastąpi później]

TheBikingViking
źródło
1

Pip , 17 bajtów

{Y Zb{a._JsMy}Ma}

Jest to funkcja, która pobiera dwie zagnieżdżone listy ciągów (jednoznakowych) i zwraca zagnieżdżoną listę ciągów. Wypróbuj online! (z dwoma testami).

Wyjaśnienie

Argumenty {}funkcji -delimitowanej są przypisywane do zmiennych lokalnych ado e. Pierwszy argument funkcji lambda jest reprezentowany przez _.

{               }  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
źródło