Wyznacznik rekurencyjny 2x2

17

Wyznacznik macierzy 2 na 2

a b
c d

jest podane przez ad - bc.

Biorąc pod uwagę macierz cyfr o wymiarach 2 n na 2 n , n ≥ 1, wyprowadzaj wynik uzyskany przez rekurencyjne obliczanie wyznacznika każdego podbloku 2 na 2, aż osiągniemy pojedynczą liczbę.

Na przykład biorąc pod uwagę dane wejściowe

3 1 4 1
5 9 2 6
5 3 5 8
9 7 9 3

Po jednym kroku uzyskujemy:

(3*9 - 1*5)    (4*6 - 1*2)    =    22  22
(5*7 - 3*9)    (5*3 - 8*9)         8  -57

I powtarzając raz jeszcze, otrzymujemy:

(22*-57 - 22*8) = -1430

Stąd wynik powinien być -1430.

Zasady

  • Elementami macierzy będą zawsze jednocyfrowe liczby całkowite, tj. Od 0 do 9.
  • Możesz wprowadzać dane w dowolnym dogodnym formacie listy lub ciągu, o ile dane wstępne nie są przetwarzane. Ponieważ macierz jest zawsze kwadratowa, możesz wziąć dane wejściowe jako pojedynczą listę 1D zamiast listy 2D, jeśli chcesz.
  • Dane wejściowe mogą być wprowadzane za pomocą funkcji wejściowej, STDIN, argumentu wiersza poleceń lub najbliższej alternatywy.
  • Wyjście powinno być pojedynczą liczbą całkowitą funkcji wyjścia, STDOUT lub najbliższą alternatywą. Nie można wyprowadzać pojedynczej liczby całkowitej z listy lub macierzy.
  • Możesz użyć wbudowanych metod wyznaczania i manipulacji macierzą, jeśli Twój język je obsługuje.
  • Twój algorytm musi działać teoretycznie dla każdego ważnego wejścia.
  • Standard zasady .

Przypadki testowe

Następujące przypadki testowe są podane jako listy w stylu Python:

[[1,0],[0,1]] -> 1
[[1,9],[8,4]] -> -68
[[0,1,2,3],[4,5,6,7],[8,9,0,1],[2,3,4,5]] -> 40
[[3,1,4,1],[5,9,2,6],[5,3,5,8],[9,7,9,3]] -> -1430
[[9,0,0,9],[0,9,9,0],[9,0,9,0],[0,9,0,9]] -> 13122
[[1,0,0,0,0,0,0,0],[2,1,0,0,0,0,0,0],[3,2,1,0,0,0,0,0],[4,3,2,1,0,0,0,0],[5,4,3,2,1,0,0,0],[6,5,4,3,2,1,0,0],[7,6,5,4,3,2,1,0],[8,7,6,5,4,3,2,1]] -> 1
[[7,1,0,5,8,0,1,5],[9,9,6,6,1,2,4,8],[4,8,7,3,8,7,4,7],[4,6,1,9,7,0,1,7],[7,6,7,1,9,4,1,6],[8,0,0,8,5,5,9,9],[4,6,4,8,9,4,8,6],[9,0,8,7,6,2,1,5]] -> 2937504
[[1,2,3,4,5,6,7,8],[2,3,4,5,6,7,8,1],[3,4,5,6,7,8,1,2],[4,5,6,7,8,1,2,3],[5,6,7,8,1,2,3,4],[6,7,8,1,2,3,4,5],[7,8,1,2,3,4,5,6],[8,1,2,3,4,5,6,7]] -> -10549504
[[1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,0],[0,1,1,1,1,0,0,1,0,1,1,1,1,1,1,0],[1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,0],[0,1,1,1,1,0,0,0,0,1,1,1,1,1,0,1],[1,0,1,0,1,1,1,0,0,1,1,1,1,0,1,0],[0,0,1,1,1,0,1,1,1,1,1,1,1,0,0,0],[1,1,1,1,1,0,1,0,1,1,1,1,1,1,1,1],[1,1,0,1,1,1,1,1,1,1,1,1,0,1,0,1],[1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1],[0,1,1,1,1,1,1,1,1,0,0,1,0,1,0,1],[1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,1,0,1,1,0,1,1,1,1,1,0,0,1,1,0],[1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,0],[1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],[1,0,1,0,0,1,0,1,0,1,1,1,1,1,0,1],[1,1,1,1,1,1,1,1,1,0,0,1,1,1,0,1]] -> -8

(Podziękowania dla @ MartinBüttner za pomoc w tym wyzwaniu)

Sp3000
źródło
3
Ciekawostka: przeprowadziłem na tym kilka eksperymentów i jest zaskakująco duża liczba macierzy binarnych z niezerową determinantą rekurencyjną. Na rozmiar 2x2, 4x4, 8x8, 16x16, otrzymujemy 6, 16488, 2229617029168687104, 3349795881591711813037585032680117995553655026185547430764970842694019448832 matryce z niezerową wyznacznika co odpowiada 37,5%, 25.1587% 12,0868%, 2,89294%, odpowiednio.
Martin Ender,
@ MartinBüttner: Dostaję 6, 22560, 10160459763342013440, ... co odpowiada A055165 .
Charles,
@Charles nieparzysty, sprawdzę mój kod
Martin Ender
@ MartinBüttner: Być może po prostu obliczamy dwie różne rzeczy?
Charles,
@Charles Rozważ matrycę [1,0,1,0;1,1,1,1;1,1,1,1;0,0,0,1]. Pełna wyznacznik wynosi zero, ponieważ ma dwa identyczne wiersze. Jest to zatem pojedyncza (czyli nieodwracalna) matryca 4 × 4, więc nie jest liczona przez A055165. Jednak omawiana tutaj determinantem „rekurencyjnym” jest 1*1-1*0==1. W przeciwnym kierunku macierz [0,0,0,1;1,0,0,0;0,1,0,0;0,0,1,0]ma wyznacznik „rekurencyjny” 0*0-0*0==0. Jednak jego pełny wyznacznik musi być niezerowy, ponieważ jego wiersze są tylko wierszami macierzy tożsamości w innej kolejności; i jest liczony przez A055165.
Jeppe Stig Nielsen,

Odpowiedzi:

8

J, 21 25 bajtów

0{0{(_2(_2-/ .*\|:)\])^:_

Stosowanie:

   ]input=.(3,1,4,1),(5,9,2,6),(5,3,5,8),:(9,7,9,3)
3 1 4 1
5 9 2 6
5 3 5 8
9 7 9 3
   (0{0{(_2(_2-/ .*\|:)\])^:_) input
_1430

Na każdym kroku przycinamy macierz do 2 na 2 i obliczamy wyznaczniki każdego z nich, uzyskując macierz wejściową następnego kroku. Powtarzamy ten proces, aż wynik się nie zmieni (ostatnim elementem jest sama determinanta). Końcowy wynik przekształcamy na skalar za pomocą 0{0{.

Wypróbuj online tutaj.

randomra
źródło
Próbowałem użyć do tego celu funkcji kafelkowania Cut, ale nie mogłem grać w golfa aż do twojej wersji. 29 bajtów: (2 2$2)&(-/ .*;._3^:(2^.#@])) Wypróbuj online!
Jonasz
4

Mathematica, 52 40 bajtów

Podziękowania dla Martina Büttnera za oszczędność 12 bajtów.

Tr[#//.l:{_,__}:>BlockMap[Det,l,{2,2}]]&

Wyjaśnienie

BlockMap[f,expr,n]podzielone exprna podlisty wielkości ni mapy fna każdej podlisty. BlockMap[Det,#,{2,2}]&podziel tablicę wejściową na 2 * 2 bloki i oblicz ich wyznaczniki.


Przypadek testowy

%[{{3,1,4,1},{5,9,2,6},{5,3,5,8},{9,7,9,3}}]
(* -1430 *)
njpipeorgan
źródło
1
Napisałem referencyjną implementację w Mathematica, omawiając pomysł wyzwania ze Sp3000 i jego 40 bajtami. Jest dość podobny do twojego, więc dam ci trochę czasu, abyś sam go znalazł, jeśli chcesz. :)
Martin Ender,
@ MartinBüttner Nie udało mi się. :(
njpipeorgan
1
Możesz uniknąć za [[1,1]]pomocą Tri za Nestpomocą //.:Tr[#//.l:{_,__}:>BlockMap[Det,l,{2,2}]]&
Martin Ender
@ MartinBüttner Właściwie wpadłem na //. pomysł, czytając odpowiedź w J, ale utknąłem w znalezieniu dobrego sposobu dopasowania tablicy. : P
njpipeorgan
skorzystaj z mojego rozwiązania, aby zaktualizować swoją odpowiedź
Martin Ender,
3

Galaretka, 20 17 bajtów

s€2s2U×¥/€ḅ-µL¡SS

Wypróbuj online! lub zweryfikuj wszystkie przypadki testowe jednocześnie .

Jak to działa

s€2s2U×¥/€ḅ-µL¡SS  Main link. Input: M (matrix)

s€2                Split each row of M into pairs.
   s2              Split the result into pairs of rows.
        /€         Reduce each pair...
       ¥             by applying the following, dyadic chain:
     U                 Reverse each pair of the left argument (1st row).
      ×                Multiply element-wise with the right argument (2nd row).
          ḅ-       Convert each resulting pair from base -1 to integer.
                   This maps [a, b] -> b - a.
            µ      Turn the previous links into a monadic chain. Begin a new one.
             L     Yield the length of the input.
              ¡    Execute the previous chain L times.
                   log2(L) times would do, but who's counting?
               SS  Sum twice to turn the resulting 1x1 matrix into a scalar.
Dennis
źródło
2

Haskell , 93 86 bajtów

EDYCJA: Dzięki @Laikoni za skrócenie tego całego 7 bajtów!

f[[a,b],[c,d]]=a*d-b*c
f m|let l=[take,drop]<*>[div(length m)2]=f[f.($b<$>m)<$>l|b<-l]

Nie wiedziałem, że możesz umieścić instrukcję let przed = i nigdy nie przyzwyczaiłem się do tych operatorów monad. Jeszcze raz dziękuję @Laikoni

Stara wersja:

f[[a,b],[c,d]]=a*d-b*c
f m=f[[f$a$map b m|a<-l]|b<-l]where h=length m`div`2;l=[take h,drop h]

Wypróbuj online!

Jest to funkcja, która powtarza się na dwa różne sposoby. Najpierw dopasowanie wzoru łapie przypadek podstawowy: macierz 2x2 i wykonuje obliczenia. Używam tego, aby wykonać obliczenia w przypadku rekurencyjnym, wywołując funkcję za pomocą generowanej przeze mnie macierzy 2x2, która zawiera rozwiązania rekurencyjne. Macierz ta jest generowana przez dwukrotne iterowanie tablicy funkcji, z których każda przecina listę na pół. Stosuję go do wierszy za pomocą prostego wywołania i stosuję do kolumn za pomocą map.

użytkownik1472751
źródło
Zamiast tego where h=length m`div`2;l=[take h,drop h]możesz użyć f m|let l=[take,drop]<*>[length m`div`2]=. map b mmoże być b<$>mi [f$a$b<$>m|a<-l]może być dalej skrócony f.($b<$>m)<$>l. W sumie 86 bajtów: [ tio.run/… Wypróbuj online!]
Laikoni
1

Python, 166 bajtów

def f(m):l=len(m)/2;g=lambda x,y:[(s[:l],s[l:])[x]for s in(m[:l],m[l:])[y]];return f(g(0,0))*f(g(1,1))-f(g(0,1))*f(g(1,0)) if l>1 else m[0][0]*m[1][1]-m[1][0]*m[0][1]

To przechodzi wszystkie dostarczone przypadki testowe. m musi być tablicą 2D (jak w przypadkach testowych), g wybiera macierz podrzędną.

basile-henry
źródło
1

Pyth, 26

M-F*VG_HhhumgMCcR2dcG2llQQ

Pakiet testowy

Składa się z dwóch części: M-F*VG_Hprzedefiniowuje funkcjęg obliczania wyznacznika macierzy dwa na dwa. To oszczędza bajty, nawet jeśli używamy go tylko raz, ponieważ rozpakowuje dwa rzędy.

Druga część to duże ograniczenie, które nazywamy log_2( len( input() ) )czasami. Niestety wykonanie kroku redukcji na macierzy 1 na 1 powoduje zawinięcie wyniku w listę, więc nie otrzymujemy stałego punktu. Redukcja polega głównie na podzieleniu macierzy, aby uzyskać macierze 2 na 2, a następnie nałożeniu g.

FryAmTheEggman
źródło
1

MATL , 26 30 bajtów

`tnX^teHHhZC2Ih2#Y)pwp-tnq

Dane wejściowe to tablica 2D z wierszami oddzielonymi ;, to znaczy

[3 1 4 1; 5 9 2 6; 5 3 5 8; 9 7 9 3]

Wypróbuj online!

`             % do...while loop
  tnX^te      %   reshape into square matrix. Implicitly asks for input the first time
  HHhZC       %   transform each 2x2 block into a column
  2Ih2#Y)     %   push matrix with rows 2,3, and also matrix with remaining rows (1,4)
  pwp-        %   multiplications and subtraction to compute the 2x2 determinants
  tnq         %   condition of do...while loop: is number of elements greater than 1?
              % implicitly end loop
              % implicitly display
Luis Mendo
źródło
1

Perl 5 , 120 + 1 ( -a) = 121 bajtów

while($#F){@r=();for$i(@o=0..($l=sqrt@F)/2-1){push@r,$F[$k=$i*$l*2+2*$_]*$F[$k+$l+1]-$F[$k+$l]*$F[$k+1]for@o}@F=@r}say@F

Wypróbuj online!

Pobiera dane wejściowe jako listę liczb oddzielonych spacjami.

Xcali
źródło
0

ES6, 91 bajtów

(a,x=0,y=0,w=a.length)=>(w>>=1)?f(a,x,y,w)*f(a,x+w,y+w,w)-f(a,x,y+w,w)*f(a,x+w,y,w):a[x][y]

Proste rozwiązanie rekurencyjne.

Neil
źródło
0

R , 111 bajtów

f=function(m)"if"((n=nrow(m))-2,f(matrix(c(f(m[x<-1:(n/2),x]),f(m[y<-x+n/2,x]),f(m[x,y]),f(m[y,y])),2)),det(m))

Wypróbuj online!

Pobiera dane wejściowe jako macierz R (która jest konwertowana przez funkcję w nagłówku).

Giuseppe
źródło
0

Groovy, 221 189 bajtów (w tym momencie mogłem użyć Javy)

f={x->b=x.size();c=b/2-1;a=(0..c).collect{i->(0..c).collect{j->z=x.toList().subList(i*2,i*2+2).collect{it.toList().subList(j*2,j*2+2)};z[0][0]*z[1][1]-z[0][1]*z[1][0];}};a.size()==1?a:f(a)}

Stara kiepska wersja, którą równie dobrze może być Java (221 bajtów):

f={x->b=x.size();a=new int[b/2][b/2];for(i=0;i<b-1;i+=2){for(j=0;j<b-1;j+=2){z=x.toList().subList(i,i+2).collect{it.toList().subList(j,j+2)};a[(int)(i/2)][(int)(j/2)]=z[0][0]*z[1][1]-z[0][1]*z[1][0];}};a.size()==1?a:f(a)}

Nieskluczony kod:

f=
{x->
  b=x.size();
  int[][]a=new int[b/2][b/2];
  for(i=0;i<b-1;i+=2) {
    for(j=0;j<b-1;j+=2) {
      z=x.toList().subList(i,i+2).collect{
        it.toList().subList(j,j+2)
      };
      a[(int)(i/2)][(int)(j/2)]=z[0][0]*z[1][1]-z[0][1]*z[1][0];
    }
  }
  a.size()==1
    ?
      a:f(a)
}
Urna Magicznej Ośmiornicy
źródło