Determinante di una matrice di numeri interi


34

Data una matrice intera quadrata come input, genera il determinante della matrice.

Regole

  • Puoi presumere che tutti gli elementi nella matrice, il determinante della matrice e il numero totale di elementi nella matrice siano compresi nell'intervallo rappresentabile di numeri interi per la tua lingua.
  • È consentito l'output di un valore decimale / float con una parte frazionaria di 0 (ad es. 42.0Anziché 42).
  • I built-in sono consentiti, ma si consiglia di includere una soluzione che non utilizza i built-in.

Casi test

[[42]] -> 42
[[2, 3], [1, 4]] -> 5
[[1, 2, 3], [4, 5, 6], [7, 8, 9]] -> 0
[[13, 17, 24], [19, 1, 3], [-5, 4, 0]] -> 1533
[[372, -152, 244], [-97, -191, 185], [-53, -397, -126]] -> 46548380
[[100, -200, 58, 4], [1, -90, -55, -165], [-67, -83, 239, 182], [238, -283, 384, 392]] -> 571026450
[[432, 45, 330, 284, 276], [-492, 497, 133, -289, -28], [-443, -400, 56, 150, -316], [-344, 316, 92, 205, 104], [277, 307, -464, 244, -422]] -> -51446016699154
[[416, 66, 340, 250, -436, -146], [-464, 68, 104, 471, -335, -442], [159, -407, 310, -489, -248, 370], [62, 277, 446, -325, 47, -193], [460, 460, -418, -28, 234, -374], [249, 375, 489, 172, -423, 125]] -> 39153009069988024
[[-246, -142, 378, -156, -373, 444], [186, 186, -23, 50, 349, -413], [216, 1, -418, 38, 47, -192], [109, 345, -356, -296, -47, -498], [-283, 91, 258, 66, -127, 79], [218, 465, -420, -326, -445, 19]] -> -925012040475554
[[-192, 141, -349, 447, -403, -21, 34], [260, -307, -333, -373, -324, 144, -190], [301, 277, 25, 8, -177, 180, 405], [-406, -9, -318, 337, -118, 44, -123], [-207, 33, -189, -229, -196, 58, -491], [-426, 48, -24, 72, -250, 160, 359], [-208, 120, -385, 251, 322, -349, -448]] -> -4248003140052269106
[[80, 159, 362, -30, -24, -493, 410, 249, -11, -109], [-110, -123, -461, -34, -266, 199, -437, 445, 498, 96], [175, -405, 432, -7, 157, 169, 336, -276, 337, -200], [-106, -379, -157, -199, 123, -172, 141, 329, 158, 309], [-316, -239, 327, -29, -482, 294, -86, -326, 490, -295], [64, -201, -155, 238, 131, 182, -487, -462, -312, 196], [-297, -75, -206, 471, -94, -46, -378, 334, 407, -97], [-140, -137, 297, -372, 228, 318, 251, -93, 117, 286], [-95, -300, -419, 41, -140, -205, 29, -481, -372, -49], [-140, -281, -88, -13, -128, -264, 165, 261, -469, -62]] -> 297434936630444226910432057


esiste una dimensione massima di matrice che deve essere supportata o è arbitraria?
Taylor Scott,

1
@TaylorScott Prima regola elencata:You may assume that all elements in the matrix, the determinant of the matrix, and the total number of elements in the matrix are within the representable range of integers for your language.
Mego

4
Sai di avere una sfida interessante quando hai 4 risposte Jelly che si
battono in successione a

Risposte:


25

Gelatina , 15 byte

LŒ!ðŒcIṠ;ị"Pð€S

Provalo online!

Come funziona

LŒ!ðŒcIṠ;ị"Pð€S   input
L                 length
 Œ!               all_permutations
   ð        ð€    for each permutation:
    Œc                take all unordered pairs
      I               calculate the difference between
                      the two integers of each pair
       Ṡ              signum of each difference
                      (positive -> 1, negative -> -1)
        ;             append:
         ị"             the list of elements generated by taking
                        each row according to the index specified
                        by each entry of the permutation
           P          product of everything
              S   sum

Perché funziona - versione mathy

L'operatore det prende una matrice e restituisce uno scalare. Un n -by- n matrice può essere pensato come un insieme di n vettori di lunghezza n , così det è una funzione che prende n vettori da ℤ n e ritorna uno scalare.

Pertanto, scrivo det ( v 1 , v 2 , v 3 , ..., v n ) per det [ v 1 v 2 v 3 ... v n ].

Si noti che det è lineare in ogni argomento, ovvero det ( v 1 + λ w 1 , v 2 , v 3 , ..., v n ) = det ( v 1 , v 2 , v 3 , ..., v n ) + λ det ( w 1 , v 2 , v 3 , ..., v n ). Pertanto, è una mappa lineare da (ℤ n ) n a ℤ.

È sufficiente determinare l'immagine della base sotto la mappa lineare. La base di (ℤ n ) n è costituita da n- toldor prodotti degli elementi base di ℤ n , ieeg e 5 ⊗ e 3 ⊗ e 1 ⊗ e 5 ⊗ e 1 . I prodotti tensore che includono tensori identici devono essere inviati a zero, poiché il determinante di una matrice in cui due colonne sono identiche è zero. Resta da verificare a cosa vengono inviati i prodotti tensore di elementi base distinti. Gli indici dei vettori nel prodotto tensore formano una biiezione, cioè una permutazione, in cui le permutazioni pari vengono inviate a 1 e le permutazioni dispari vengono inviate a -1.

Ad esempio, per trovare il determinante di [[1, 2], [3, 4]]: nota che le colonne sono [1, 3] e [2, 4]. Decomponiamo [1, 3] per dare (1 e 1 + 3 e 2 ) e (2 e 1 + 4 e 2 ). L'elemento corrispondente nel prodotto tensore è (1 e 1 ⊗ 2 e 1 + 1 e 1 ⊗ 4 e 2 + 3 e 2 ⊗ 2 e 1 + 3 e 2 ⊗ 4 e 2 ), che semplifichiamo in (2 e 1 ⊗ e 1 + 4 e 1 ⊗ e 2 + 6 e 2 ⊗ e 1 + 12 e 2 ⊗ e 2). Perciò:

det [[1, 2], [3, 4]]
= det (1 e 1 + 3 e 2 , 2 e 1 + 4 e 2 )
= det (2 e 1 ⊗ e 1 + 4 e 1 ⊗ e 2 + 6 e 2 ⊗ e 1 + 12 e 2 ⊗ e 2 )
= det (2 e 1 ⊗ e 1 ) + det (4 e 1 ⊗ e 2 ) + det (6 e 2 ⊗ e 1 ) + det (12 e2 ⊗ e 2 )
= 2 det (e 1 ⊗ e 1 ) + 4 det (e 1 ⊗ e 2 ) + 6 det (e 2 ⊗ e 1 ) + 12 det (e 2 ⊗ e 2 )
= 2 (0) + 4 (1) + 6 (-1) + 12 (0)
= 4 - 6
= -2

Ora resta da dimostrare che la formula per trovare la parità della permutazione è valida. Quello che fa il mio codice è essenzialmente trovare il numero di inversioni, cioè i luoghi in cui un elemento a sinistra è più grande di un elemento a destra (non necessariamente consecutivamente).

Ad esempio, nella permutazione 3614572, ci sono 9 inversioni (31, 32, 61, 64, 65, 62, 42, 52, 72), quindi la permutazione è dispari.

La giustificazione è che ogni trasposizione (scambiando due elementi) aggiunge una inversione o toglie un'inversione, scambiando la parità del numero di inversioni e la parità della permutazione è la parità del numero di trasposizioni necessarie per ottenere la permutazione.

Pertanto, in conclusione, la nostra formula è data da:

Perché funziona: versione non matematica

dove σ è una permutazione di 𝕊 n il gruppo di tutte le permutazioni su n lettere e sgn è il segno della permutazione, AKA (-1) elevato alla parità della permutazione e a ij è la ( ij ) th entry in la matrice ( i giù, j attraverso).


17
Quella "versione non mathica" è una dannata maledizione.
MD XF

6
Le formule e i simboli e i numeri di @MDXF difficilmente costituiscono matematica. La matematica è l'astrazione, la generalizzazione e la logica dietro le manipolazioni formali dei simboli.
Leaky Nun,

7
@JAB Jelly implementa una propria tabella codici personalizzata . (Uno di questi giorni, TIO includerà un link alla
tabella

1
@Mego i "prodotti della somma dei diagonali" funzionano solo per le matrici 1x1, 2x2 e 3x3. Per le matrici più grandi, è necessario considerare tutte le permutazioni e la loro parità.
Leaky Nun,

3
+1 per effettivamente includere la prova nel post invece di dire "perché questa formula è elencata nella pagina abcxyz deve essere vera".
user202729

11

R , 3 byte

Soluzione banale

det

Provalo online!

R , 94 92 byte

soluzione reimplementata

superato da Jarko Dubbeldam

d=function(m)"if"(x<-nrow(m),m[,1]%*%sapply(1:x,function(y)(-1)^(y-1)*d(m[-y,-1,drop=F])),1)

Provalo online!

Utilizza ricorsivamente l'espansione dei minori nella prima colonna della matrice.

f <- function(m){
 x <- nrow(m)                 # number of rows of the matrix
 if(sum(x) > 1){              # when the recursion reaches a 1x1, it has 0 rows
                              # b/c [] drops attributes
  minor <- function(y){
   m[y] * (-1)^(y-1) *
   d(m[-y,-1])                # recurse with the yth row and first column dropped
   }
  minors <- sapply(1:x,minor) # call on each row
  sum(minors)                 # return the sum
 } else {
  m                           # return the 1x1 matrix
 }
}



9

Gelatina , 16 15 12 10 byte

Ḣ×Zß-Ƥ$Ṛḅ-

Utilizza l'espansione di Laplace . Grazie a @miles per giocare a golf da 3 5 byte!

Provalo online!

Come funziona

Ḣ×Zß-Ƥ$Ṛḅ-  Main link. Argument: M (matrix / 2D array)

Ḣ           Head; pop and yield the first row of M.
      $     Combine the two links to the left into a monadic chain.
  Z         Zip/transpose the matrix (M without its first row).
   ß-Ƥ      Recursively map the main link over all outfixes of length 1, i.e., over
            the transpose without each of its rows.
            This yields an empty array if M = [[x]].
 ×          Take the elementwise product of the first row and the result on the
            right hand. Due to Jelly's vectorizer, [x] × [] yields [x].
       Ṛ    Reverse the order of the products.
        ḅ-  Convert from base -1 to integer.
                [a]          -> (-1)**0*a
                [a, b]       -> (-1)**1*a + (-1)**0*b = b - a
                [a, b, c]    -> (-1)**2*a + (-1)**1*b + (-1)**0*c = c - b + a
                etc.

8

Wolfram Language (Mathematica) , tra 14 e 42 byte

Abbiamo un built-in a 3 byte e un soluzione a 53 byte che evita completamente i built-in, quindi ecco alcune soluzioni più strane nel mezzo.

Il Wolfram Language ha molte funzioni molto intense per decomporre le matrici in prodotti di altre matrici con una struttura più semplice. Uno dei più semplici (il che significa che ne ho sentito parlare prima) è la decomposizione della Giordania. Ogni matrice è simile a una matrice triangolare superiore (possibilmente di valore complesso) fatta di blocchi diagonali con una struttura specifica, chiamata decomposizione di Jordan di quella matrice. La somiglianza conserva i determinanti e il determinante di una matrice triangolare è il prodotto degli elementi diagonali, quindi possiamo calcolare il determinante con i seguenti 42 byte :

1##&@@Diagonal@Last@JordanDecomposition@#&

Il determinante è anche uguale al prodotto degli autovalori di una matrice, con molteplicità. Fortunatamente, la funzione di autovalore di Wolfram tiene traccia della molteplicità (anche per matrici non diagonali), quindi otteniamo la seguente soluzione di 20 byte :

1##&@@Eigenvalues@#&

La prossima soluzione è una specie di imbroglio e non sono davvero sicuro del perché funzioni. Il Wronskian di un elenco di n funzioni è il determinante della matrice dei primi n -1 derivati ​​delle funzioni. Se diamo alla Wronskianfunzione una matrice di numeri interi e diciamo che la variabile di differenziazione è 1, in qualche modo sputa il determinante della matrice. È strano, ma non coinvolge le lettere " Det" ed è solo 14 byte ...

#~Wronskian~1&

(Il determinante Casoratian funziona così, per 1 altro Byte: #~Casoratian~1&)

Nel regno di algebra astratta, il determinante di un n  x  n matrice (pensato come mappa k → k cioè moltiplicazione per il determinante) è la n esima potenza esterno della matrice (dopo aver ricevuto un isomorfismo k → ⋀ n k n ). Nel linguaggio Wolfram, possiamo farlo con i seguenti 26 byte :

HodgeDual[TensorWedge@@#]&

Ed ecco una soluzione che funziona solo per determinanti positivi. Se prendiamo un ipercubo unitario n- dimensionale e applichiamo ad esso una trasformazione lineare, il "volume" n- dimensionale della regione risultante è il valore assoluto del determinante della trasformazione. L'applicazione di una trasformazione lineare a un cubo fornisce un parallelepipedo e possiamo prendere il suo volume con i seguenti 39 byte di codice:

RegionMeasure@Parallelepiped[Last@#,#]&

1
La soluzione che ho avuto in questo senso è stata Exp@*Tr@*MatrixLog, ma sfortunatamente questo non funziona per le singole matrici.
Misha Lavrov,

1
@MishaLavrov Ooh, è intelligente! Penso che puoi risolverlo con Check[E^Tr@MatrixLog@#,0]&.
Non un albero il

È fantastico! Non ne ero a conoscenza Checkprima.
Misha Lavrov,

1
Ho creato una sfida per la decomposizione della Giordania qualche tempo fa. Potresti essere interessato anche a questo. Che grande risposta!
Mego

8

Haskell , 71 byte

-3 byte grazie a Lynn. Un altro bytes la polvere grazie a Craig Roy.

f[]=1
f(h:t)=foldr1(-)[v*f[take i l++drop(i+1)l|l<-t]|(i,v)<-zip[0..]h]

Provalo online! Aggiunto -Oflag a fini di ottimizzazione. Non è necessario.

Spiegazione (obsoleta)

f implementa ricorsivamente l'espansione del cofattore.

f[[x]]=x

Questa linea copre il caso base di una matrice 1 × 1 , nel qual caso il determinante è mat[0, 0].

f(h:t)=

Questo utilizza la corrispondenza del modello di Haskell per spezzare la matrice in una testa (la prima fila) e una coda (il resto della matrice).

          [                                     |(i,v)<-zip[0..]h]

Enumera la testa della matrice (comprimendo l'elenco infinito di numeri interi e la testa) e itera su di essa.

           (-1)*i*v

Annulla il risultato in base al fatto che il suo indice sia anche dal momento che il calcolo del determinante comporta alternanza di addizione e sottrazione.

                     [take i l++drop(i+1)l|l<-t]

Questo essenzialmente rimuove l' ith colonna della coda prendendo gli elementi i e concatenandola con la riga con i primi (i + 1) elementi rilasciati per ogni riga nella coda.

                   *f

Calcola il determinante del risultato sopra e moltiplicalo per il risultato di (-1)*i*v.

       sum

Somma il risultato dell'elenco sopra e restituiscilo.


2
potresti risparmiare 1 byte se sostituisci sum[(-1)^i*...confoldr(-)0[...
Craig Roy il

6

Proton , 99 byte

f=m=>(l=len(m))==1?m[0][0]:sum((-1)**i*m[0][i]*f([[m[k][j]for k:1..l]for j:0..l if j-i])for i:0..l)

Provalo online!

-3 byte grazie a Mr. Xcoder
-3 byte grazie a Erik the Outgolfer

Espansione sulla prima riga


Solo perché Proton non ha incorporato per determinante.
user202729,

103 byte . ((~i%2)*2-1)->((-i%2)|1)
Mr. Xcoder,

Anche 102 byte sostituendo j!=icon j-io i-j.
Mr. Xcoder,


@EriktheOutgolfer Ah sì, grazie!
HyperNeutrino,

5

Ottava , 28 byte

@(x)round(prod(diag(qr(x))))

Provalo online!

Questo utilizza il QR decomposizione di una matrice X in una matrice orthgonal Q ed una tomaia matrice triangolare R . Il determinante di X è il prodotto di quelle di Q e R . Una matrice ortogonale ha un determinante unitario e per una matrice triangolare il determinante è il prodotto delle sue entrate diagonali. Di Octave qrfunzione chiamata con una singola uscita dà R .

Il risultato viene arrotondato al numero intero più vicino. Per matrici di input di grandi dimensioni, le imprecisioni in virgola mobile possono produrre un errore superiore 0.5e quindi produrre un risultato errato.


1
Questo è un modo interessante per eludere il detbuiltin. ;)
tomsmeding il

1
@tomsmeding :-) Also, it had already been "used" in Stewie's answer
Luis Mendo


5

C,  176  125 bytes

Thanks to @ceilingcat for golfing 42 bytes, and thanks to both @Lynn and @Jonathan Frech for saving a byte each!

d(M,n)int*M;{int i=n--,s=*M*!n,c,T[n*n];for(;i--;s+=M[i]*(1-i%2*2)*d(T,n))for(c=n*n;c--;T[c]=M[n-~c+c/n+(c%n>=i)]);return s;}

Calculates the determinant using the Laplace expansion along the first row.

Try it online!

Unrolled:

d(M, n)int*M;
{
    int i=n--, s=*M*!n, c, T[n*n];
    for (; i--; s+=M[i]*(1-i%2*2)*d(T,n))
        for (c=n*n; c--;)
            T[c] = M[n-~c+c/n+(c%n>=i)];
    return s;
}

(i%2*-2+1)(1-i%2*2) saves one more byte.
Lynn

n+1+c can be n-~c.
Jonathan Frech

Suggest i=s instead of return s
ceilingcat

5

Jelly, 43 bytes

Finally I've done writing my non-builtin solution in a golfing language!

ḣ⁹’’¤;ṫḊ€Ç×⁸ị@⁹’¤¤ḷ/¤
çЀ⁸J‘¤µJ-*×NS
ÇḢḢ$Ṗ?

Thanks to HyperNeutrino for saving a byte!

Try it online! (spaced code for clarity)

terribly long way to remove n'th elements from a list, will improve later


This answer had been outgolfed by answers of HyperNeutrino, Dennis and Leaky Nun. Jelly is very popular as a golfing language.

Quick explanation:

ÇḢḢ$Ṗ?    Main link.
     ?    If
    Ṗ     after remove the last element, the value is not empty (truthy)
Ç         then execute the last link
 ḢḢ$      else get the element at index [1, 1].

çЀ⁸J‘¤µJ-*×NS     Helper link 1, take input as a matrix.
çЀ                Apply the previous link, thread right argument to
   ⁸J‘¤            the range [2, 3, ..., n+1]
       µ           With the result,
        J-*        generate the range [-1, 1, -1, 1, ...] with that length
           ×N      Multiply by negative
             S     Sum

ḣ⁹’’¤;ṫḊ€Ç×⁸ị@⁹’¤¤ḷ/¤    Helper link 2, take left input as a matrix, right input as a number in range [2..n+1]
ḣ
 ⁹’’¤                    Take head ρ-2 of the matrix
     ;                   concatenate with 
      ṫ                  tail ρ (that is, remove item ρ-1)
       Ḋ€                Remove first column
         Ç               Calculate determinant of remaining matrix
          ×         ¤    multiply by
                  ḷ/     the first column,
            ị@           row #
              ⁹’¤        ρ-1 (just removed in determinant calculation routine) of
           ⁸     ¤       the matrix.

4

Jelly, 24 bytes

œcL’$ṚÑ€
J-*×Ḣ€×ÇSµḢḢ$Ṗ?

Try it online!

Explanation

œcL’$ṚÑ€         Helper Link; get the next level of subdeterminants (for Laplace Expansion)
œc               Combinations without replacement of length:
  L’$            Length of input - 1 (this will get all submatrices, except it's reversed)
     Ṛ           Reverse the whole thing
      р         Get the determinant of each of these
J-*×Ḣ€×ÇSµḢḢ$Ṗ?  Main Link
              ?  If the next value is truthy
             Ṗ   Remove the last element (truthy if the matrix was at least size 2)
J-*×Ḣ€×ÇSµ       Then expand
          ḢḢ$    Otherwise, get the first element of the first element (m[0][0] in Python)
J                [1, 2, ..., len(z)]
 -*              (-1 ** z) for each z in the length range
   ×             Vectorizing multiply with
    Ḣ€           The first element of each (this gets the first column); modifies each row (makes it golfier yay)
      ×Ç         Vectorizing multiply with the subdeterminants
        S        Sum

-2 bytes thanks to user202729's solution


4

MATL, 3 bytes / 5 bytes

With built-in function

&0|

Try it online!

Without built-in

Thanks to Misha Lavrov for pointing out a mistake, now corrected

YvpYo

Try it online!

This computes the determinant as the product of the eigenvalues, rounded to the closest integer to avoid floating-point inaccuracies.

Yv       % Implicit input. Push vector containing the eigenvalues
p        % Product
Yo       % Round. Implicit display

Wouldn't the product of the singular values only tell you the absolute value of the determinant?
Misha Lavrov

@MishaLavrov You're totally right! Thanks for noticing. I corrected it by using eigenvalues instead of singular values... and that saved 4 bytes \o/
Luis Mendo

4

R, 32 bytes

function(m)Re(prod(eigen(m)$va))

Uses Not a Tree's algorithm of taking the eigenvalues of the matrix and taking the real part of their product.

Try it online!


Very elegant! +1.
Giuseppe

3

Octave, 30 bytes

@(x)-prod(diag([~,l,~]=lu(x)))

Try it online!

or, the boring 4 byte solution (saved 6 bytes thanks to Luis Mendo (forgot the rules regaring builtin functions)):

@det

Explanation:

Coming up! :)


3

TI-Basic, 2 bytes

det(Ans

Ah, well.

Please don't upvote trivial answers.

As a high school student (who's forced to own one of these calculators), this function is hella useful so...


8
It's still hella useful in college - linear algebra does not go away
Taylor Scott

5
@TaylorScott In fact, it comes back with a vengeance in differential equations.
Mego

@Mego - you are right on that; though for some reason they let me take all of calc and that before linear :/
Taylor Scott

1
@TaylorScott Due to an oversight by my university's math department, linalg was not a prerequisite for diffeq when I took it. When my professor realized that, he quickly gave us a 3-day crash course in linalg.
Mego

3

Haskell, 62 bytes

a#((b:c):r)=b*d(a++map tail r)-(a++[c])#r
_#_=0
d[]=1
d l=[]#l

Try it online! (Footer with test cases taken from @totallyhuman's solution.)

d computes the determinant using a Laplace expansion along the first column. Needs three bytes more than the permanent.


3

Python 2, 95 bytes

-12 bytes thanks to Lynn.

Port of my Haskell answer.

f=lambda m:sum((-1)**i*v*f([j[:i]+j[i+1:]for j in m[1:]])for i,v in enumerate(m[0]))if m else 1

Try it online!


1
Here, too, you can use [] as a base case: f=lambda m:sum((-1)**i*v*f([j[:i]+j[i+1:]for j in m[1:]])for i,v in enumerate(m[0]))if m else 1 for 95 bytes!
Lynn

m==[]or sum(...) gives 92 bytes.
Bubbler

3

Wolfram Language (Mathematica), 53 52 bytes

1##&@@@(t=Tuples)@#.Signature/@t[Range@Tr[1^#]&/@#]&

Try it online!

Unfortunately, computing the determinant of an n by n matrix this way uses O(nn) memory, which puts large test cases out of reach.

How it works

The first part, 1##&@@@(t=Tuples)@#, computes all possible products of a term from each row of the given matrix. t[Range@Tr[1^#]&/@#] gives a list of the same length whose elements are things like {3,2,1} or {2,2,3} saying which entry of each row we picked out for the corresponding product.

We apply Signature to the second list, which maps even permutations to 1, odd permutations to -1, and non-permutations to 0. This is precisely the coefficient with which the corresponding product appears in the determinant.

Finally, we take the dot product of the two lists.


If even Signature is too much of a built-in, at 73 bytes we can take

1##&@@@(t=Tuples)@#.(1##&@@Order@@@#~Subsets~{2}&/@t[Range@Tr[1^#]&/@#])&

replacing it by 1##&@@Order@@@#~Subsets~{2}&. This computes Signature of a possibly-permutation by taking the product of Order applied to all pairs of elements of the permutation. Order will give 1 if the pair is in ascending order, -1 if it's in descending order, and 0 if they're equal.


-1 byte thanks to @user202729


1
52 bytes (in case you didn't know this Mathematica golfing tip)
user202729

I did, but somehow forgot about it here. Thanks!
Misha Lavrov

3

Python 3, 238 bytes, 227 bytes, 224 bytes, 216 bytes

from functools import*
from itertools import*
r=range;n=len;s=sum
f=lambda l:s(reduce(lambda p,m:p*m,[l[a][b]for a,b in zip(r(n(l)),j)])*(-1)**s(s(y<j[x]for y in j[x:])for x in r(n(l)))for j in permutations(r(n(l))))

Try it online!

My solution uses the definition of a determinant for calculations. Unfortunately, the complexity of this algorithm is n! and I can not show the passage of the last test, but in theory this is possible.


3

CJam (50 45 bytes)

{:A_,{1$_,,.=1b\)/:CAff*A@zf{\f.*1fb}..-}/;C}

This is an anonymous block (function) which takes a 2D array on the stack and leaves an integer on the stack.

Online test suite

Dissection

This implements the Faddeev-LeVerrier algorithm, and I think it's the first answer to take that approach.

The objective is to calculate the coefficients ck of the characteristic polynomial of the n×n matrix A,

p(λ)det(λInA)=k=0nckλk
where, evidently, cn=1 and c0=(1)ndetA.

The coefficients are determined recursively from the top down, by dint of the auxiliary matrices M,

M00cn=1(k=0)MkAMk1+cnk+1Icnk=1ktr(AMk)k=1,,n .

The code never works directly with cnk and Mk, but always with (1)kcnk and (1)k+1AMk, so the recurrence is

(1)kcnk=1ktr((1)k+1AMk)(1)k+2AMk+1=(1)kcnkAA((1)k+1AMk)

{               e# Define a block
  :A            e#   Store the input matrix in A
  _,            e#   Take the length of a copy
  {             e#     for i = 0 to n-1
                e#       Stack: (-1)^{i+2}AM_{i+1} i
    1$_,,.=1b   e#       Calculate tr((-1)^{i+2}AM_{i+1})
    \)/:C       e#       Divide by (i+1) and store in C
    Aff*        e#       Multiply by A
    A@          e#       Push a copy of A, bring (-1)^{i+2}AM_{i+1} to the top
    zf{\f.*1fb} e#       Matrix multiplication
    ..-         e#       Matrix subtraction
  }/
  ;             e#   Pop (-1)^{n+2}AM_{n+1} (which incidentally is 0)
  C             e#   Fetch the last stored value of C
}



2

SageMath, various

Here are a bunch of methods for computing the determinant that I found interesting, all programmed in SageMath. They can all be tried here.

Builtin, 3 bytes

det

This one isn't too interesting. Sage provides global-level aliases to many common operations that would normally be object methods, so this is shorter than lambda m:m.det().


Real Part of Product of Eigenvalues, 36 bytes

lambda m:real(prod(m.eigenvalues()))

Unfortunately, eigenvalues is not one of those global-level aliases. That, combined with the fact that Sage doesn't have a neat way to compose functions, means we're stuck with a costly lambda. This function symbolic values which are automatically converted to numeric values when printed, so some floating point inaccuracy may be present in some outputs.


Product of Diagonal in Jordan Normal Form, 60 bytes

lambda m:prod(m.jordan_form()[x,x]for x in range(m.nrows()))

In Jordan Normal form, an NxN matrix is represented as a block matrix, with N blocks on the diagonal. Each block consists of either a single eigenvalue, or a MxM matrix with a repeated eigenvalue on the diagonal and 1s on the super-diagonal (the diagonal above and to the right of the "main" diagonal). This results in a matrix with all eigenvalues (with multiplicity) on the main diagonal, and some 1s on the super-diagonal corresponding to repeated eigenvalues. This returns the product of the diagonal of the Jordan normal form, which is the product of the eigenvalues (with multiplicty), so this is a more roundabout way of performing the same computation as the previous solution.

Because Sage wants the Jordan normal form to be over the same ring as the original matrix, this only works if all of the eigenvalues are rational. Complex eigenvalues result in an error (unless the original matrix is over the ring CDF (complex double floats) or SR). However, this means that taking the real part is not necessary, compared to the above solution.


Product of Diagonal in Smith Decomposition

lambda m:prod(m.smith_form()[0].diagonal())

Unlike Jordan normal form, Smith normal form is guaranteed to be over the same field as the original matrix. Rather than computing the eigenvalues and representing them with a block diagonal matrix, Smith decomposition computes the elementary divisors of the matrix (which is a topic a bit too complicated for this post), puts them into a diagonal matrix D, and computes two matrices with unit determinant U and V such that D = U*A*V (where A is the original matrix). Since the determinant of the product of matrices equals the product of the determinants of the matrices (det(A*B*...) = det(A)*det(B)*...), and U and V are defined to have unit determinants, det(D) = det(A). The determinant of a diagonal matrix is simply the product of the elements on the diagonal.

Laplace Expansion, 109 bytes

lambda m:m.nrows()>1and sum((-1)**j*m[0,j]*L(m[1:,:j].augment(m[1:,j+1:]))for j in range(m.ncols()))or m[0,0]

This performs Laplace expansion along the first row, using a recursive approach. det([[a]]) = a is used for the base case. It should be shorter to use det([[]]) = 1 for the base case, but my attempt at that implementation had a bug that I haven't been able to track down yet.


Leibniz's Formula, 100 bytes

L2 = lambda m:sum(sgn(p)*prod(m[k,p[k]-1]for k in range(m.ncols()))for p in Permutations(m.ncols()))

This directly implements Leibniz's formula. For a much better explanation of the formula and why it works than I could possibly write, see this excellent answer.


Real Part of e^(Tr(ln(M))), 48 bytes

lambda m:real(exp(sum(map(ln,m.eigenvalues()))))

This function returns symbolic expressions. To get a numerical approximation, call n(result) before printing.

This is an approach that I haven't seen anyone use yet. I'm going to give a longer, more-detailed explanation for this one.

Let A be a square matrix over the reals. By definition, the determinant of A is equal to the product of the eigenvalues of A. The trace of A is equal to the sum of A's eigenvalues. For real numbers r_1 and r_2, exp(r_1) * exp(r_2) = exp(r_1 + r_2). Since the matrix exponential function is defined to be analogous to the scalar exponential function (especially in the previous identity), and the matrix exponential can be computed by diagonalizing the matrix and applying the scalar exponential function to the eigenvalues on the diagonal, we can say det(exp(A)) = exp(trace(A)) (the product of exp(λ) for each eigenvalue λ of A equals the sum of the eigenvalues of exp(A)). Thus, if we can find a matrix L such that exp(L) = A, we can compute det(A) = exp(trace(L)).

We can find such a matrix L by computing log(A). The matrix logarithm can be computed in the same way as the matrix exponential: form a square diagonal matrix by applying the scalar logarithm function to each eigenvalue of A (this is why we restriced A to the reals). Since we only care about the trace of L, we can skip the construction and just directly sum the exponentials of the eigenvalues. The eigenvalues can be complex, even if the matrix isn't over the complex ring, so we take the real part of the sum.


1
The last part is a fascinating idea, but the header and explanation don't match the code, which doesn't take a matrix logarithm. It's just real(prod(m.eigenvalues())) ungolfed.
Peter Taylor

2

Java 8, 266 261 259 258 bytes

long d(int[][]m){long r=0;int i=0,j,k,l=m.length,t[][]=new int[l-1][l-1],q=m[0][0];if(l<3)return l<2?q:q*m[1][1]-m[0][1]*m[1][0];for(;i<l;r+=m[0][i]*(1-i++%2*2)*d(t))for(j=0;++j<l;)for(k=l;k-->0;){q=m[j][k];if(k<i)t[j-1][k]=q;if(k>i)t[j-1][k-1]=q;}return r;}

Look mom, no build-ins.. because Java has none.. >.>

-7 bytes thanks to @ceilingcat.

Explanation:

Try it here. (Only the last test case is too big to fit in a long of size 263-1.)

long d(int[][]m){             // Method with integer-matrix parameter and long return-type
  long r=0;                   //  Return-long, starting at 0
  int i=0,j,k,                //  Index-integers
      l=m.length,             //  Dimensions of the square matrix
      t[][]=new int[l-1][l-1],//  Temp-matrix, one size smaller than `m`
      q=m[0][0];              //  The first value in the matrix (to reduce bytes)
  if(l<3)                     //  If the dimensions are 1 or 2:
    return l<2?               //   If the dimensions are 1:
      q                       //    Simply return the only item in it
     :                        //   Else (the dimensions are 2):
      q*m[1][1]-m[0][1]*m[1][0];
                              //    Calculate the determinant of the 2x2 matrix
                              //  If the dimensions are 3 or larger: 
  for(;i<l;                   //  Loop (1) from 0 to `l` (exclusive)
      r+=                     //    After every iteration: add the following to the result:
         m[0][i]              //     The item in the first row and `i`'th column,
         *(1-i++%2*2)         //     multiplied by 1 if `i` is even; -1 if odd,
         *d(t))               //     multiplied by a recursive call with the temp-matrix
    for(j=0;                  //   Reset index `j` to 0
        ++j<l;)               //   Inner loop (2) from 0 to `l` (exclusive)
      for(k=l;k-->0;){        //    Inner loop (3) from `l-1` to 0 (inclusive)
        q=m[j][k];            //     Set the integer at location `j,k` to reduce bytes
        if(k<i)               //     If `k` is smaller than `i`:
          t[j-1][k]=q;        //      Set this integer at location `j-1,k`
        if(k>i)               //     Else-if `k` is larger than `i`:
          t[j-1][k-1]=q;      //      Set this integer at location `j-1,k-1`
                              //     Else: `k` and `i` are equals: do nothing (implicit)
      }                       //    End of inner loop (3)
                              //   End of inner loop (2) (implicit / single-line body)
                              //  End of loop (1) (implicit / single-line body)
  return r;                   //  Return the result-long
}                             // End of method

2

JavaScript (ES6), 91

Recursive Laplace

q=(a,s=1)=>+a||a.reduce((v,[r],i)=>v-(s=-s)*r*q(a.map(r=>r.slice(1)).filter((r,j)=>j-i)),0)

Less golfed

q = (a,s=1) => // s used as a local variable
  a[1] // check if a is a single element array 
       // if array, recursive call expanding along 1st column
  ? a.reduce((v,[r],i) => v-(s=-s)*r*q(a.map(r=>r.slice(1)).filter((r,j)=>j-i)),0) 
  : +a // single element, convert to number

Test

q=(a,s=1)=>+a||a.reduce((v,[r],i)=>v-(s=-s)*r*q(a.map(r=>r.slice(1)).filter((r,j)=>j-i)),0)

TestCases=`[[42]] -> 42
[[2, 3], [1, 4]] -> 5
[[1, 2, 3], [4, 5, 6], [7, 8, 9]] -> 0
[[13, 17, 24], [19, 1, 3], [-5, 4, 0]] -> 1533
[[372, -152, 244], [-97, -191, 185], [-53, -397, -126]] -> 46548380
[[100, -200, 58, 4], [1, -90, -55, -165], [-67, -83, 239, 182], [238, -283, 384, 392]] -> 571026450
[[432, 45, 330, 284, 276], [-492, 497, 133, -289, -28], [-443, -400, 56, 150, -316], [-344, 316, 92, 205, 104], [277, 307, -464, 244, -422]] -> -51446016699154`
.split('\n')

TestCases.forEach(r=>{
  [a,k] = r.split (' -> ')
  a = eval(a)
  d = q(a)
  console.log('Test '+(k==d ? 'OK':'KO')+
    '\nMatrix '+a.join('|')+
    '\nResult '+d+
    '\nCheck  '+k)
})


83 bytes with the same behavior
Arnauld

Or 85 bytes to support the empty matrix (whose determinant should be 1).
Arnauld

(I used the same optimizations in this answer, which is derived from yours.)
Arnauld




1

Java (OpenJDK 8), 195 192 177 bytes

long d(int[][]m){long D=0;for(int l=m.length-1,t[][]=new int[l][l],i=0,j,k;i<=l;D+=m[0][i]*(1-i++%2*2)*(l<1?1:d(t)))for(j=0;j<l*l;)t[j/l][k=j%l]=m[1+j++/l][k<i?k:k+1];return D;}

Try it online!

Like many other answers, this also uses the Laplace formula. A slightly less golfed version:

long d(int[][]m){
  long D=0;
  int l=m.length-1,t[][]=new int[l][l],i=0,j,k;
  for(;i<=l;)
    for(j=0;j<l*l;)
      t[j/l][k=j%l]=m[1+j++/l][k<i?k:k+1];
    D+=m[0][i]*(1-i++%2*2)*(l<1?1:d(t));
  return D;
}

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.