哈夫尼亚人Codegolf


22

挑战是为矩阵Hafnian写codegolf 。对称矩阵的2nHafnian 定义为:2nA

在此处输入图片说明

下面就2N代表了一组整数的所有排列的,从12n,那就是[1, 2n]

Wikipedia链接讨论了邻接矩阵,但是您的代码应适用于任何实值对称输入矩阵。

对于那些对Hafnian应用程序感兴趣的人,mathoverflow链接讨论了更多内容。

您的代码可以随意输入,并以任何合理的格式提供输出,但是请在答案中包括一个完整的示例,其中包括有关如何向代码提供输入的明确说明。

输入矩阵始终为正方形,最大为16 x16。无需处理空白矩阵或奇数维矩阵。

参考实施

这是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

维基页面现已(2018年3月2日)由ShreevatsaR更新,以包含一种不同的计算Hafnian的方式。看到它打高尔夫球会很有趣。


5
我认为,通过对哈夫尼语的非正式解释,这将更容易消化。像这样,取n个矩阵项的所有子集,其中它们的n行索引和n列索引形成1..2n的分区,取每个乘积,相加并缩放总和。
xnor

Answers:


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
}


Apply便宜2字节,通过将其他行挤在一起可以节省4字节。tio.run/##PY6xDoIwEIZ3nsLxzpxiS4ymkYEXYHIjDFDEEKBtSokS47PX4sDw5/…同样令人感兴趣的是,基数R如何缺少统计编程语言的置换函数
Vlo

@Vlo很好!我们可以移入Nk移入函数参数以将其移至一条语句,删除{}并保存另外两个字节。
朱塞佩

@Giuseppe Darn一直忘记您可以在函数args中定义它们。花了几分钟试图
嵌入

8

Pyth,24个字节

sm*Fmc@@Qhkek2d{mScd2.pU

在这里尝试!


旧版本,35字节

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

在这里尝试!


3
目前领先,但你不得不害怕的果冻答案来.... :)

嗯,果冻一定会击败我约10个字节。Pyth并不是工作的最佳工具
Xcoder先生,

05AB1E看起来甚至可以和Pyth并列(不管信不信由你,最后是一个a[b]足以竞争的矩阵挑战)。
魔术章鱼缸

@MagicOctopusUrn我已经有了一个击败Pyth的05AB1E解决方案:-)不打算发布它(至少现在是这样)
Xcoder先生,18年

是不是有点像xÍysè<¹sès·<ysè<èlmao?PS Mine是40个字节,运行不正常哈,请随时发布它,不确定我是否能在回家之前完成。
魔术章鱼缸

6

Stax23 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先生,18年

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矩阵需要11个字节。
  • 多亏了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矩阵。
乔纳森·弗雷希

2*j+++1相当于j+j+++1,这是一样的j-(-j++-1),所以我们可以有效地使用位补救一个字节:j-~j++在线试玩
Xcoder先生

3

R84 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在内分配iftio.run/##XU/LCsIwELz7FcFTVtOQl1pf1/…–
Vlo

整齐!我会说在速度挑战中发布它,但是可能还有更多的优化方法(例如线程),尽管R的速度并不确切,但最好在那里参考。
朱塞佩

这样做是为了进行基准测试!
Vlo

我实际上曾尝试测试它的速度,但很快就对结果感到沮丧。在速度挑战中使用相同精确算法的最慢Python提交在TIO上几秒钟内处理了24x24矩阵,但R超时。在我的本地计算机上,即使在借助“ memo”包进行记忆的辅助下,它也没有在合理的时间内响应...
Kirill L.

2

果冻,29个字节

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

在线尝试!

我认为该;@€€Wị@/€€P€零件可能会打倒。需要稍后返回以查看并添加说明。


打高尔夫球之前与我的解决方案相同(除外J)。果冻的头脑有何相似之处来源
user202729'3

通过重构您提到的部分以及除以2和阶乘,我能够进一步减少它。LḶŒ!s€2ḅL‘ịFZµPS÷JḤ$P$ TIO
英里

@ user202729 haha​​ nice
dylnan

@miles哇,节省了很多。我会将其编辑为答案,但有很大的不同,因此,如果您愿意,可以随时提交自己的答案
dylnan

2

Haskell,136个字节

-14字节感谢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]]

在线尝试!

啊...


2

MATL29 24 22字节

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

在线尝试!或验证所有测试用例:123

怎么运行的

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

椰子165 145 128 127字节

-1字节感谢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*

在线尝试!


1

Perl 6,86位元组

{my \n=$^m/2;^$m .permutations.map({[*] .map(->\a,\b{$m[a][b]})}).sum/(2**n*[*] 1..n)}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.