Hafnian 코드 골프


22

문제는 매트릭스의 Hafnian에 대한 codegolf 를 작성하는 것입니다 . 의 Hafnian 2n-by- 2n대칭 행렬 A로 정의된다 :

여기에 이미지 설명을 입력하십시오

여기서 S 2N가 에서 모든 정수 순열의 집합을 나타냄 12n즉, [1, 2n].

Wikipedia 링크는 인접 행렬에 대해 설명하지만 실제 대칭 입력 행렬에 대해서는 코드가 작동해야합니다.

Hafnian의 응용에 관심이있는 사람들을 위해 mathoverflow 링크가 더 자세히 설명합니다.

코드는 원하는대로 입력을 할 수 있고 합리적인 형식으로 출력 할 수 있지만 코드에 입력을 제공하는 방법에 대한 명확한 지침을 포함한 완전한 예제를 답에 포함하십시오.

입력 행렬은 항상 정사각형이며 최대 16 x 16입니다. 빈 행렬 또는 홀수 차원의 행렬을 처리 할 필요가 없습니다.

참조 구현

다음은 Mr. Xcoder의 Python 코드 예입니다.

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

Wiree 페이지는 ShreevatsaR에 의해 업데이트되어 Hafnian을 계산하는 다른 방법을 포함합니다. 이 골프를 보는 것은 매우 흥미로울 것입니다.


5
나는 이것이 hafnian에 대한 비공식적 인 설명으로 소화하기가 더 쉬울 것이라고 생각합니다. n 행 인덱스와 n 열 인덱스가 1..2n의 파티션을 형성하는 n 행렬 항목의 모든 하위 집합을 가져 와서 각각의 곱을 가져 와서 합을 계산하십시오.
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하고 @Vlofor-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
}


Apply는 2 바이트 씩 저렴하여 다른 행을 함께 구성하여 추가로 4 바이트를 절약 할 수 있습니다. tio.run/##PY6xDoIwEIZ3nsLxzpxiS4ymkYEXYHIjDFDEEKBtSokS47PX4sDw5/… 또한베이스 R에 통계 프로그래밍 언어에 대한 순열 함수가없는 방법도 매우 흥미 롭습니다.
Vlo

@Vlo 아주 좋은! 우리는 이동할 수 Nk을 제거, 하나 개의 문장으로 얻을 함수 인수로 {}다른 2 바이트를 절약.
Giuseppe

@Giuseppe Darn은 함수 인수에서 정의 할 수 있다는 것을 잊어 버렸습니다. 몇 분 동안 그 변수를 없애려고
썼다

8

Pyth , 24 바이트

sm*Fmc@@Qhkek2d{mScd2.pU

여기 사용해보십시오!


이전 버전, 35 바이트

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

여기 사용해보십시오!


3
현재 리드하지만 당신은 오는 젤리 답변을 두려워해야합니다 .... :)

Eh Jelly는 분명히 약 10 바이트만큼 내 비트를 이길 것입니다. Pyth는 그 일을위한 최고의 도구가 아닙니다
Mr. Xcoder

05AB1E는 심지어 Pyth를 묶는 것처럼 보입니다 (믿거 나 말거나, a[b]경쟁하기에 충분한 매트릭스 도전 입니다).
Magic Octopus Urn

@MagicOctopusUrn 나는 이미 Pyth를 능가하는 05AB1E 솔루션을 가지고 있습니다 :-) (최소한,) 게시하지 않을 것입니다
Mr. Xcoder

xÍysè<¹sès·<ysè<èlmao 의 라인을 따라 뭔가인가 ? PS Mine은 40 바이트이며 잘 작동하지 않으므로 집에 가기 전에 끝낼 수 있는지 확실하지 않은 경우 게시하십시오.
Magic Octopus Urn

6

Stax , 23 22 19 17 바이트

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

온라인으로 실행 및 디버깅

동일한 프로그램의 해당 ASCII 표현은 이렇습니다.

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

프로그램에서 일부 부동 소수점 반올림 오류가 발생합니다. 특히, 33673.5000000011대신에 보고 합니다 33673.5. 그러나이 프로그램이 부동 소수점 값에서 작동한다는 점을 감안할 때 정확성이 허용됩니다. 이 머신의 예제 입력에 거의 1 분이 걸리므로 매우 느립니다.

%                             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è아냐, 하 하하하 ..
Magic Octopus Urn

@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 바이트

  • 두 개의 후행 증가와 루프 배치를 통해 3 바이트를 절약했습니다.
  • golfed - anothter 사후 증가 만지작 분기 전에 두 변수의 초기화를 이동하여 3 바이트의 저장 if(...)...k=0...else...,j=0...if(k=j=0,...)...else...- 및 인덱스 시프트를 수행 하였다.
  • float행렬 을 지원하여 11 바이트가 필요합니다 .
  • Mr. Xcoder 덕분에 바이트를 절약했습니다 . 골프 2*j+++1j-~j++.
  • 불필요한 int변수 유형 선언 을 제거하고 계승 함수를 사용하지 않고 이미 존재하는 for 루프를 사용하여 계승 값을 계산하여 20 바이트를 절약했습니다 .
  • 로 골프 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. 모든 Hafnian 계산은 단순히 그 위에 구축되고 더 골프를칩니다.

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
C 답변이있는 것이 좋지만 제안한대로 현재 준수하지 않습니다.

@Lembik 수정. 이제 float행렬을 지원 합니다.
Jonathan Frech

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)

온라인으로 사용해보십시오!

편집 : -6 바이트의 Vlo 덕분입니다.

여기있는 모든 사람들이 순열과 함께 표준 참조 알고리즘을 구현하고있는 것 같지만 관련 도전 에서 얻은 커뮤니티 지식을 활용하려고 시도 했습니다 . 기본적으로 골프 대신 가장 빠른 코드를 대상으로하는 동일한 작업입니다.

매트릭스를 슬라이싱하는 데 능숙한 언어 (예 : R)에서 재귀 알고리즘 hafnian(m) = sum(m[i,j] * hafnian(m[-rows and columns at i,j])은 더 빠를뿐만 아니라 상당히 골프적인 것으로 밝혀졌습니다. 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
}

아주 좋은 답변입니다. -1 호출하는 If괄호로 -4 이용해 F초기화 변수 -1 할당 n내에 if. tio.run/##XU/LCsIwELz7FcFTVtOQl1pf1/...
VLO

산뜻한! 스피드 챌린지에 게시하고 싶지만 스레딩과 같은 최적화가 더있을 수 있으며 R은 속도로 정확하게 알려지지 않았지만 참조를 위해 거기에있는 것이 좋습니다. .
Giuseppe

벤치 마크 목적으로 사용하십시오!
Vlo

실제로 속도 테스트를 시도했지만 결과에 빨리 낙담했습니다. 동일한 정확한 알고리즘을 사용하여 속도 문제에서 가장 느린 Python 제출은 TIO에서 몇 초 만에 24x24 매트릭스를 처리하지만 R 시간이 초과됩니다. 내 로컬 컴퓨터에서 패키지 'memo'의 메모가 도움이 되더라도 합리적인 시간 내에 응답하지 않았습니다 ...
Kirill L.

2

젤리 , 29 바이트

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

온라인으로 사용해보십시오!

나는 그 ;@€€Wị@/€€P€부분이 골프를 칠 가능성이 있다고 생각한다 . 설명을 확인하고 추가하려면 나중에 다시 방문해야합니다.


골프 다운 하기 전에 내 솔루션 (제외 J)과 동일합니다 . 젤리의 마음은 비슷하게 생각 하십니까? 소스
user202729

언급 한 부분과 2로 나누고 계승을 리팩토링하여 조금 더 줄일 수있었습니다. LḶŒ!s€2ḅL‘ịFZµPS÷JḤ$P$ TIO
마일

@ user202729 haha ​​nice
dylnan

@ 마일 와우 그것은 많은 저축입니다. 내 답변으로 편집 할 것입니다. 그러나 그것은 매우 다릅니다. 원하는 경우 자신의 답변을 자유롭게 제출하십시오
dylnan


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

펄 6, 86 바이트

{my \n=$^m/2;^$m .permutations.map({[*] .map(->\a,\b{$m[a][b]})}).sum/(2**n*[*] 1..n)}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.