Кодегольф Гафній


22

Завдання полягає в тому, щоб написати кодогольф для Хафніана матриці . Хафніан 2n-бісиметричної 2nматриці Aвизначається як:

введіть тут опис зображення

Тут S 2n являє собою сукупність усіх перестановок цілих чисел від 1до 2n, тобто [1, 2n].

Посилання на вікіпедію розповідає про матриці суміжності, але ваш код повинен працювати для будь-яких справжніх симетричних матриць введення.

Для тих, хто цікавиться програмами Хафніана, посилання mathoverflow обговорює ще декілька.

Ваш код може брати вхід, як би він хотів, і давати вихід у будь-якому розумному форматі, але, будь ласка, включіть у свою відповідь повний відпрацьований приклад, включаючи чіткі інструкції щодо того, як подати вхід до вашого коду.

Вхідна матриця завжди квадратна і буде максимум 16 на 16. Немає необхідності обробляти порожню матрицю або матриці непарних розмірів.

Довідкова реалізація

Ось приклад коду python від містера 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

Сторінку вікі зараз (2 березня 2018 року) оновив ShreevatsaR, щоб включити інший спосіб обчислення Хафніана. Було б дуже цікаво побачити цього гольфа.


5
Я думаю, що це було б легше перетравити за допомогою неофіційного пояснення хафніана. Щось подібне, візьміть усі підмножини записів n матриць, де їхні індекси n рядків та n індекси стовпців утворюють розділ 1..2n, візьміть добуток кожного, додайте їх та масштабуйте суму.
xnor

Відповіді:


9

R , 150 142 127 119 байт

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)

Спробуйте в Інтернеті!

Використовую той самий трюк, коли я виявив, що гольф вниз відповів, щоб індексувати матрицю P, і @Vlo запропонував підхід повністю усунути forцикл на -6 байт!

Щоб створити новий тестовий випадок, ви можете зробити це matrix(c(values,separated,by,commas,going,across,rows),nrow=2n,ncol=2n,byrow=T).

Пояснення: (код той самий; він використовує applyскоріше forцикл, але логіка інакше ідентична).

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
}


Застосувати дешевше на 2 байти, що дозволяє додатково зберегти 4 байти, збивши інші рядки разом. tio.run/##PY6xDoIwEIZ3nsLxzpxiS4ymkYEXYHIjDFDEEKBtSokS47PX4sDw5/… Також цікаво, як базовій R не вистачає функції перестановки для мови статистичного програмування
Vlo

@Vlo дуже приємно! ми можемо перейти Nі kв аргументи функції , щоб отримати його в одній заяві, видалення {}і збереження ще два байта.
Джузеппе

@Giuseppe Darn продовжує забувати, що ви можете визначити їх у аргументах функцій. Провів кілька хвилин, намагаючись
зімкнути

8

Pyth , 24 байти

sm*Fmc@@Qhkek2d{mScd2.pU

Спробуйте тут!


Стара версія, 35 байт

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

Спробуйте тут!


3
Наразі ведуча, але ви повинні побоюватися відповідей Jelly, щоб прийти .... :)

Eh Jelly напевно обіграє мене приблизно на 10 байт. Піт - не найкращий інструмент для роботи
містер Xcoder

05AB1E виглядає так, що він може навіть зв'язати Pyth (вірте чи ні, нарешті, матричне завдання, де a[b]достатньо, щоб конкурувати).
Чарівна восьминога Урна

@MagicOctopusUrn У мене вже є рішення 05AB1E, яке б'є Pyth :-) Не збираюся його публікувати (принаймні поки що)
Містер Xcoder

Це щось на зразок xÍysè<¹sès·<ysè<èlmao? PS Mine - 40 байт і працює не так добре, так що не соромтеся розміщувати це, не знаючи, що я зможу закінчити, перш ніж мені повернутися додому.
Чарівна восьминога Урна

6

Стакс , 23 22 19 17 байт

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

Запустити та налагодити його в Інтернеті

Відповідне представлення такої ж програми ascii.

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

Програма зазнає деякої помилки округлення плаваючої точки. Зокрема, він звітує 33673.5000000011замість 33673.5. Але я вважаю, що точність є прийнятною, враховуючи, що ця програма працює на значеннях з плаваючою комою. Це також дуже повільно, витрачаючи майже хвилину на приклади входів на цій машині.

%                             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
Дуже вражає!

5

05AB1E , 21 байт

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

Спробуйте в Інтернеті!


Стара версія, 32 байти

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

Спробуйте в Інтернеті!

Як це працює?

āœ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
Не жартуйте èsè, ха-ха ... ха-ха ... я караюсь.
Чарівна восьминога Урна

@MagicOctopusUrn Виправлено ... Я забув 05AB1E є 0-індексованим> _ <
Містер Xcoder

3

Желе , 19 байт

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

Спробуйте в Інтернеті!

Альтернативна версія, 15 байт, виклик після публікації

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

Желе нарешті отримало індексацію n-мірного масиву.

Спробуйте в Інтернеті!

Як це працює

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.

19-байтна версія працює аналогічно; воно просто має реалізувати œịсебе.

...ḅ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 байт

  • Збережено три байти, перемістившись з двома інкрементами та розмістивши цикл.
  • Збережено три байти, перемістившись з іншим післякріпленням, перемістивши обидві змінні ініціалізації перед гілкою - гольф if(...)...k=0...else...,j=0...наif(k=j=0,...)...else... - і виконав індекс зрушення.
  • Необхідні одинадцять байт шляхом підтримки float матриці.
  • Збережено байт завдяки панові Xcoder ; гольф 2*j+++1доj-~j++ .
  • Збережено двадцять байтів, видаливши зайве int змінної типу та не використовуючи факториальну функцію, а замість цього обчисливши значення фактора, використовуючи вже існуючий цикл.
  • Збережено байт, погравши S=S/F/(1<<n);в гольф 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);}

Спробуйте в Інтернеті!

Пояснення

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

Спробуйте в Інтернеті!

В основі програми лежить наступний генератор перестановки, який проходить цикл S_n. Всі обчислення Хафніана просто будуються на ньому - і надалі гольфуються.

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

Спробуйте в Інтернеті!


1
Чудово мати відповідь С, але, як ви підказуєте, наразі вона не відповідає.

@ Лембік виправлено. Тепер підтримує floatматриці.
Джонатан Фрех

2*j+++1еквівалентний j+j+++1, що те саме j-(-j++-1), що ми можемо ефективно використовувати бітовий доповнення, щоб зберегти байт: j-~j++( Спробуйте це в Інтернеті )
Містер Xcoder

3

R , 84 78 байт

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)

Спробуйте в Інтернеті!

Редагувати: Завдяки Vlo за -6 байт.

Здається, що тут усі реалізують стандартний алгоритм довідки з перестановками, але я намагався скористатися знаннями спільноти, отриманими у відповідній проблемі , яка в основному є тим самим завданням, яке спрямоване на швидкий код замість гольфу.

Виявляється, що для мови, яка гарна в нарізанні матриць (як R), алгоритм рекурсивності: hafnian(m) = sum(m[i,j] * hafnian(m[-rows and columns at i,j])не тільки швидший, але і досить гострий. Ось код, який не використовується для гольфу:

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
}

Дуже приємна відповідь. -1 для виклику Ifз дужками, -4 для використання Fв якості ініціалізованої змінної, -1 для призначення nв межах if. tio.run/##XU/LCsIwELz7FcFTVtOQl1pf1/…
Вло

акуратний! Я б сказав, розмістіть це у виклику щодо швидкості, але, мабуть, можна зробити ще кілька оптимізацій (наприклад, нарізання різьби), і хоча R не точно відомий своєю швидкістю, було б добре мати його для довідки .
Джузеппе

Зробіть це для орієнтирів!
Вло

Я насправді намагався перевірити це на швидкість, але швидко відчаював результатів. Найповільніша подача Python у виклику швидкості, використовуючи той самий точний алгоритм, розбиває матрицю 24x24 протягом декількох секунд на TIO, але R скорочується. На моїй локальній машині він також не реагував у розумний час, навіть коли допомагав із запам'ятовуванням із пакета "пам'ятка" ...
Кирилл Л.

2

Желе , 29 байт

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

Спробуйте в Інтернеті!

Я думаю, що ;@€€Wị@/€€P€частина, швидше за все, може бути знищена. Потрібно повернутися пізніше, щоб перевірити та додати пояснення.


Ідентично моєму рішенню (крім J) перед гольфом . Желеві розуми думають однаково ? джерело
користувач202729

Я зміг трохи зменшити це, повернувши до себе частину, про яку ви згадали, а також поділ на 2 та фактор. LḶŒ!s€2ḅL‘ịFZµPS÷JḤ$P$ TIO
миль

@ user202729 haha ​​nice
dylnan

@miles Ого, це велика економія. Я відредагую її у своїй відповіді, але вона зовсім інша, тому
сміливо


2

MATL , 29 24 22 байт

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

Спробуйте в Інтернеті! Або перевірити всі тестові випадки: 1 , 2 , 3 .

Як це працює

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

Perl 6, 86 байт

{my \n=$^m/2;^$m .permutations.map({[*] .map(->\a,\b{$m[a][b]})}).sum/(2**n*[*] 1..n)}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.