Codegolf the Hafnian


22

La sfida è scrivere codegolf per gli hafniani di una matrice . L'hafniano di una matrice 2n-by- 2nsimmetrica Aè definito come:

inserisci qui la descrizione dell'immagine

Qui S 2n rappresenta l'insieme di tutte le permutazioni degli interi da 1a 2n, cioè [1, 2n].

Il link di Wikipedia parla delle matrici di adiacenza, ma il codice dovrebbe funzionare per qualsiasi matrice di input simmetrica stimata.

Per coloro che sono interessati alle applicazioni di Hafnian, il link mathoverflow ne discute ancora.

Il tuo codice può ricevere input nel modo desiderato e fornire output in qualsiasi formato ragionevole, ma includi nella tua risposta un esempio completo che includa chiare istruzioni su come fornire input al tuo codice.

La matrice di input è sempre quadrata e sarà al massimo di 16 per 16. Non è necessario essere in grado di gestire la matrice vuota o le matrici di dimensione dispari.

Implementazione di riferimento

Ecco alcuni esempi di codice Python di Mr. Xcoder.

from itertools import permutations
from math import factorial

def hafnian(matrix):
    my_sum = 0
    n = len(matrix) // 2
    for sigma in permutations(range(n*2)):
        prod = 1
        for j in range(n):
            prod *= matrix[sigma[2*j]][sigma[2*j+1]]
        my_sum += prod
    return my_sum / (factorial(n) * 2 ** n)


print(hafnian([[0, 4.5], [4.5, 0]]))
4.5
print(hafnian([[0, 4.7, 4.6, 4.5], [4.7, 0, 2.1, 0.4], [4.6, 2.1, 0, 1.2], [4.5, 0.4, 1.2, 0]])
16.93
print(hafnian([[1.3, 4.1, 1.2, 0.0, 0.9, 4.4], [4.1, 4.2, 2.7, 1.2, 0.4, 1.7], [1.2, 2.7, 4.9, 4.7, 4.0, 3.7], [0.0, 1.2, 4.7, 2.2, 3.3, 1.8], [0.9, 0.4, 4.0, 3.3, 0.5, 4.4], [4.4, 1.7, 3.7, 1.8, 4.4, 3.2]])
262.458

La pagina wiki è stata ora (2 marzo 2018) aggiornata da ShreevatsaR per includere un modo diverso di calcolare l'Hafnian. Sarebbe molto interessante vederlo giocare a golf.


5
Penso che questo sarebbe più facile da digerire con una spiegazione informale dell'afniano. Qualcosa del genere, prendi tutti i sottoinsiemi di n voci di matrice in cui i loro indici di n righe e n indici di colonna formano una partizione di 1..2n, prendi il prodotto di ciascuno, aggiungili e ridimensiona la somma.
xnor

Risposte:


9

R , 150 142 127 119 byte

function(A,N=nrow(A),k=1:(N/2)*2)sum(apply(gtools::permutations(N,N),1,function(r)prod(A[cbind(r[k-1],r[k])])))/prod(k)

Provalo online!

Usa lo stesso trucco che ho scoperto giocando a golf su questa risposta per indicizzare la matrice Pe @Vlo ha suggerito un approccio per rimuovere completamente il forciclo per -6 byte!

Per creare un nuovo caso di prova, puoi farlo matrix(c(values,separated,by,commas,going,across,rows),nrow=2n,ncol=2n,byrow=T).

Spiegazione: (il codice è lo stesso; utilizza un ciclo applyanziché un forciclo, ma la logica è identica).

function(A){
N <- nrow(A)                   #N = 2*n
k <- 1:(N/2) * 2               #k = c(2,4,...,N) -- aka 2*j in the formula
P <- gtools::permutations(N,N) #all N-length permutations of 1:N
for(i in 1:nrow(P))
 F <- F + prod(A[cbind(P[i,k-1],P[i,k])]) # takes the product of all A_sigma(2j-1)sigma(2j) for fixed i and adds it to F (initialized to 0)
F / prod(k)                    #return value; prod(k) == n! * 2^n
}


Applicare è più economico di 2 byte, il che consente ulteriori 4 byte di risparmio stipando le altre linee insieme. tio.run/##PY6xDoIwEIZ3nsLxzpxiS4ymkYEXYHIjDFDEEKBtSokS47PX4sDw5/… È anche abbastanza interessante come la base R manchi di una funzione di permutazione per un linguaggio di programmazione statistica
Vlo

@Vlo molto bello! possiamo spostare Ne kin argomenti di funzione per ottenerlo in un'istruzione, rimuovendo {}e salvando altri due byte.
Giuseppe

@Giuseppe Darn continua a dimenticare che è possibile definirli nella funzione args. Trascorso qualche minuto nel tentativo di emettere quelle variabili ...
Vlo

8

Pyth , 24 byte

sm*Fmc@@Qhkek2d{mScd2.pU

Provalo qui!


Vecchia versione, 35 byte

*c1**FK/lQ2^2Ksm*Fm@@Q@dtyk@dykK.pU

Provalo qui!


3
Attualmente in testa ma devi temere che Jelly risponda per venire .... :)

Eh Jelly mi batterà sicuramente di circa 10 byte. Pyth non è lo strumento migliore per il lavoro
Mr. Xcoder

05AB1E sembra che potrebbe anche legare Pyth (che ci crediate o no, finalmente una sfida a matrice dove a[b]è abbastanza per competere).
Magic Octopus Urn

@MagicOctopusUrn Ho già una soluzione 05AB1E che batte Pyth :-) Non ho intenzione di pubblicarlo (almeno per ora)
Mr. Xcoder

È qualcosa sulla xÍysè<¹sès·<ysè<èfalsariga di lmao? PS Il mio è di 40 byte e non funziona così bene, quindi sentiti libero di pubblicarlo, non sono sicuro di poter finire prima di dover andare a casa.
Magic Octopus Urn

6

Stax , 23 22 19 17 byte

ü;Y╙◘▌Φq↓ê²╧▐å↑┌C

Esegui ed esegui il debug online

La corrispondente rappresentazione ASCII dello stesso programma è questa.

%r|TF2/{xsE@i^H/m:*+

Il programma soffre di un errore di arrotondamento in virgola mobile. In particolare, segnala 33673.5000000011invece di 33673.5. Ma penso che l'accuratezza sia accettabile dato che questo programma funziona su valori in virgola mobile. È anche molto lento, impiegando quasi un minuto per gli input di esempio su questa macchina.

%                             get size of matrix
 r|T                          get all permutations of [0 ... size-1]
    F                         for each, execute the rest of the program
     2/                       get consecutive pairs
       {        m             map each pair... 
        xsE@                      the matrix element at that location
            i^H/                  divided by 2*(i+1) where i=iteration index
                 :*           product of array
                   +          add to running total

1
Molto impressionante!

5

05AB1E , 21 byte

ā<œε2ô{}Ùεε`Isèsè;]PO

Provalo online!


Vecchia versione, 32 byte

āœvIg;©Lε·UIyX<èèyXèè}P}Oθ®!/®o/

Provalo online!

Come funziona?

āœvIg;©Lε·UIyX<èèyXèè}P}Oθ®!/®o/ – Full program. Argument: A matrix M.
ā                                – The range [1 ... len(M)].
 œ                               – Permutations.
  v                    }         – Iterate over the above with a variable y.
   Ig;©                          – Push len(M) / 2 and also store it in register c.
       Lε            }           – For each integer in the range [1 ... ^]:
         ·U                      – Double it and store it in a variable X.
            yX<                  – Push the element of y at index X-1.
           I   è                 – And index with the result into M.
                yXè              – Push the element of y at index X.
                   è             – And index with the result into ^^.
                      P          – Take the product of the resulting list.
                        O        – Sum the result of the mapping.
                         θ       – And take the last element*.
                          ®!     – Take the factorial of the last item in register c.
                             ®o  – Raise 2 to the power of the last item in register c.
                            /  / – And divide the sum of the mapping accordingly.

* – Yeah, this is needed because I mess up the stack when pushing so many values in the loop and not popping correctly ;P

1
Non èsèsto scherzando , ahah ... ahah ... io sono pazzo.
Magic Octopus Urn

@MagicOctopusUrn Risolto ... Ho dimenticato 05AB1E è 0-indicizzato> _ <
Mr. Xcoder

3

Gelatina , 19 byte

LŒ!s€2Ṣ€QḅL_LịFHP€S

Provalo online!

Versione alternativa, 15 byte, sfida post-date

LŒ!s€2Ṣ€QœịHP€S

Jelly ha finalmente ottenuto l'indicizzazione dell'array n-dimensionale.

Provalo online!

Come funziona

LŒ!s€2Ṣ€QœiHP€S  Main link. Argument: M (matrix / 2D array)

L                Take the length, yielding 2n.
 Œ!              Generate all permutations of [1, ..., 2n].
   s€2           Split each permutation into pairs.
      Ṣ€         Sort the pair arrays.
        Q        Unique; deduplicate the array of pair arrays.
                 This avoids dividing by n! at the end.
           H     Halve; yield M, with all of its elements divided by 2.
                 This avoids dividing by 2**n at the end.
         œị      At-index (n-dimensional); take each pair of indices [i, j] and
                 yield M[i][j].
            P€   Take the product the results corresponding the same permutation.
              S  Take the sum of the products.

La versione a 19 byte funziona in modo simile; deve solo implementarsi œị.

...ḅL_LịFH...    Return value: Array of arrays of index pairs. Argument: M

    L            Length; yield 2n.
   ḅ             Convert each pair of indices [i, j] from base 2n to integer,
                 yielding ((2n)i + j).
     _L          Subtract 2n, yielding ((2n)(i - 1) + j).
                 This is necessary because indexing is 1-based in Jelly, so the
                 index pair [1, 1] must map to index 1.
        F        Yield M, flattened.
       ị         Take the indices to the left and get the element at these indices
                 from the array to the right.
         H       Halve; divide all retrieved elements by 2.

3

C (gcc) , 288 285 282 293 292 272 271 byte

  • Salvato tre byte armeggiando con due post-incrementi e per il posizionamento del loop.
  • Salvati tre byte armeggiando con un altro post-incremento, spostando entrambe le inizializzazioni variabili prima che il ramo - scorresse if(...)...k=0...else...,j=0...a if(k=j=0,...)...else...- ed eseguisse uno spostamento dell'indice.
  • Necessari undici byte supportando le floatmatrici.
  • Salvataggio di un byte grazie a Mr. Xcoder ; giocare 2*j+++1a golf a j-~j++.
  • Ho salvato venti byte rimuovendo una intdichiarazione di tipo variabile superflua e non usando una funzione fattoriale, ma invece calcolando il valore fattoriale usando un ciclo for già esistente.
  • Salvato un byte giocando S=S/F/(1<<n);a S/=F*(1<<n);.
float S,p,F;j,i;s(A,n,P,l,o,k)float*A;int*P;{if(k=j=0,o-l)for(;k<l;s(A,n,P,l,o+1))P[o]=k++;else{for(p=-l;j<l;j++)for(i=0;i<l;)p+=P[j]==P[i++];if(!p){for(F=p=1,j=0;j<n;F*=j)p*=A[P[2*j]*2*n+P[j-~j++]];S+=p;}}}float h(A,n)float*A;{int P[j=2*n];S=0;s(A,n,P,j,0);S/=F*(1<<n);}

Provalo online!

Spiegazione

float S,p,F;                    // global float variables: total sum, temporary, factorial
j,i;                            // global integer variables: indices
s(A,n,P,l,o,k)float*A;int*P;{   // recursively look at every permutation in S_n
 if(k=j=0,o-l)                  // initialize k and j, check if o != l (possible  permutation not yet fully generated)
  for(;k<l;s(A,n,P,l,o+1))      // loop through possible values for current possible  permuation position
   P[o]=k++;                    // set possible  permutation, recursively call (golfed into the for loop)
 else{for(p=-l;j<l;j++)         // there exists a possible permutation fully generated
  for(i=0;i<l;)                 // test if the possible permutation is a bijection
   p+=P[j]==P[i++];             // check for unique elements
  if(!p){                       // indeed, it is a permutation
   for(F=p=1,j=0;j<n;F*=j)      // Hafnian product loop and calculate the factorial (over and over to save bytes)
    p*=A[P[2*j]*2*n+P[j-~j++]]; // Hafnian product
   S+=p;}}}                     // add to sum
float h(A,n)float*A;{           // Hafnian function
 int P[j=2*n];S=0;              // allocate permutation memory, initialize sum
 s(A,n,P,j,0);                  // calculate Hafnian sum
 S/=F*(1<<n);}                  // calculate Hafnian

Provalo online!

Al centro del programma c'è il seguente generatore di permutazione che scorre attraverso S_n. Tutto il calcolo hafniano è semplicemente costruito su di esso - e ulteriormente giocato a golf.

j,i,p;Sn(A,l,o,k)int*A;{          // compute every element in S_n
 if(o-l)                          // o!=l, the permutation has not fully been generated
  for(k=0;k<l;k++)                // loop through the integers [0, n)
   A[o]=k,Sn(A,l,o+1);            // modify permutation, call recursively
 else{                            // possible permutation has been generated
  for(p=-l,j=0;j<l;j++)           // look at the entire possible permutation
   for(i=0;i<l;i++)p+=A[j]==A[i]; // check that all elements appear uniquely
  if(!p)                          // no duplicat elements, it is indeed a permutation
   for(printf("["),j=0;j<l        // print
   ||printf("]\n")*0;)            //  the
    printf("%d, ",A[j++]);}}      //   permutation
main(){int l=4,A[l];Sn(A,l,0);}   // all permutations in S_4

Provalo online!


1
È fantastico avere una risposta in C ma, come suggerisci, al momento non è conforme.

@Lembik Fixed. Ora supporta le floatmatrici.
Jonathan Frech,

2*j+++1è equivalente a j+j+++1, che è lo stesso di j-(-j++-1), quindi possiamo usare il complemento bit per bit in modo efficiente per salvare un byte: j-~j++( Provalo online )
Mr. Xcoder

3

R , 84 78 byte

h=function(m)"if"(n<-nrow(m),{for(j in 2:n)F=F+m[1,j]*h(m[v<--c(1,j),v]);F},1)

Provalo online!

Modifica: grazie a Vlo per -6 byte.

Sembra che tutti qui stiano implementando l'algoritmo di riferimento standard con permutazioni, ma ho cercato di sfruttare le conoscenze della comunità acquisite nella relativa sfida , che è fondamentalmente lo stesso compito mirato al codice più veloce invece del golf.

Si scopre che per un linguaggio che è bravo a tagliare le matrici (come R), l'algoritmo ricorsivo: hafnian(m) = sum(m[i,j] * hafnian(m[-rows and columns at i,j])non è solo più veloce, ma anche piuttosto golfoso. Ecco il codice ungolfed:

hafnian<-function(m)
{
    n=nrow(m)
    #Exits one step earlier than golfed version
    if(n == 2) return(m[1,2])
    h = 0
    for(j in 2:n) {
        if(m[1,j] == 0) next
        h = h + m[1,j] * hafnian(m[c(-1,-j),c(-1,-j)])
    }
    h
}

Risposta molto bella -1 per la chiamata Ifcon parentesi, -4 per l'utilizzo Fcome variabile inizializzata, -1 per l'assegnazione nall'interno di if. tio.run/##XU/LCsIwELz7FcFTVtOQl1pf1/…
Vlo

! pulito Direi di pubblicarlo nella sfida della velocità, ma probabilmente ci sono altre ottimizzazioni (come il threading) che possono essere fatte, e sebbene R non sia esattamente noto per la sua velocità, sarebbe bello averlo lì come riferimento .
Giuseppe,

Fallo per scopi di riferimento!
Vlo,

In realtà ho provato a testarlo per la velocità, ma mi sono rapidamente scoraggiato dai risultati. La presentazione Python più lenta nella sfida della velocità usando lo stesso algoritmo esatto sgretola la matrice 24x24 in pochi secondi su TIO, ma R scade. Sul mio computer locale, inoltre, non ha risposto in un tempo ragionevole, anche quando aiutato con la memoization dal pacchetto 'memo' ...
Kirill L.

2

Gelatina , 29 byte

LHµ2*×!
LŒ!s€2;@€€Wị@/€€P€S÷Ç

Provalo online!

Penso che la ;@€€Wị@/€€P€parte possa essere probabilmente eliminata. È necessario tornare più tardi per controllare e aggiungere una spiegazione.


Identico alla mia soluzione (tranne il J) prima di giocare a golf . Le menti della gelatina pensano allo stesso modo ? fonte
user202729

Sono stato in grado di ridurlo un po 'di più rifattorizzando la parte menzionata, nonché la divisione per 2 e il fattoriale. LḶŒ!s€2ḅL‘ịFZµPS÷JḤ$P$ TIO
miglia

@ user202729 haha ​​nice
dylnan

@miles Wow, questo è un sacco di risparmi. Lo modificherò nella mia risposta, ma è piuttosto diverso, quindi sentiti libero di inviare la tua risposta se vuoi
dylnan

2

Haskell , 136 byte

-14 byte grazie agli ovs.

import Data.List
f m|n<-length m`div`2=sum[product[m!!(s!!(2*j-2)-1)!!(s!!(2*j-1)-1)/realToFrac(2*j)|j<-[1..n]]|s<-permutations[1..n*2]]

Provalo online!

Ugh ...


2

MATL , 29 24 22 byte

Zy:Y@!"G@2eZ{)tn:E/pvs

Provalo online! Oppure verifica tutti i casi di test: 1 , 2 , 3 .

Come funziona

Zy       % Size of (implicit) input: pushes [2*n 2*n], where the
         % input is a 2*n × 2*n matrix. 
:        % Range: gives row vector [1 2 ... 2*n]
Y@       % All permutation of that vector as rows of a matrix
!"       % For each permutation 
  G      %   Push input matrix
  @      %   Push current permutation
  2e     %   Reshape as a 2-row array
  Z{     %   Split rows into a cell array of size 2
  )      %   Reference indexing. With a cell array as index this
         %   applies element-wise indexing (similar to sub2ind).
         %   Gives a row vector with the n matrix entries selected
         %   by the current permutation
  t      %   Duplicate
  n:     %   Number of elements, range: this gives [1 2 ... n]
  E      %   Double, element-wise: gives [2 4 ... 2*n]
  /      %   Divide, element-wise
  p      %   Product
  vs     %   Vertically concatenate and sum
         % End (implicit). Display (implicit)


1

Cocco , 165 145 128 127 byte

-1 byte grazie a Mr. Xcoder

m->sum(reduce((o,j)->o*m[p[2*j]][p[j-~j]]/-~j/2,range(len(m)//2),1)for p in permutations(range(len(m))))
from itertools import*

Provalo online!


1

Perl 6, 86 byte

{my \n=$^m/2;^$m .permutations.map({[*] .map(->\a,\b{$m[a][b]})}).sum/(2**n*[*] 1..n)}
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.