ハフニアンのコードゴルフ


22

課題は、行列のハフニアンの codegolfを書くことです。2n行ごとの2n対称行列のハフニアンは、A次のように定義されます。

ここに画像の説明を入力してください

ここで、S 2nはから1までの整数のすべての順列のセット2n、つまりを表し[1, 2n]ます。

ウィキペディアのリンクでは隣接行列について説明していますが、コードは実際の値の対称入力行列に対して機能するはずです。

ハフニアンのアプリケーションに興味のある人のために、mathoverflowリンクでもう少し説明します。

コードは必要に応じて入力を受け取り、適切な形式で出力できますが、コードに入力を提供するための明確な指示を含む完全に機能する例を回答に含めてください。

入力行列は常に正方で、最大16 x 16です。空の行列または奇数次元の行列を処理できる必要はありません。

リファレンス実装

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

Wikiページは現在(2018年3月2日)ShreevatsaRによって更新され、ハフニアンを計算する別の方法が含まれています。このゴルフを見ることは非常に興味深いでしょう。


5
これはハフニアンの非公式の説明で消化するのが簡単だと思います。たとえば、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、そして@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とても素敵です!我々は移動することができますNし、k削除し、1つの文にそれを得るために、関数の引数に{}、別の2つのバイトを保存します。
ジュゼッペ

@Giuseppe Darnは、関数argsでそれらを定義できることを忘れ続けています。これらの変数を使いこなすために数分を費やしました...
Vlo

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]は競争するのに十分なマトリックスチャレンジです)。
魔法のタコ

@MagicOctopusUrn私はすでにPythを打つ05AB1Eソリューションを持っている:-)(少なくとも、今のところ)それを投稿するつもりはない
氏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。しかし、このプログラムは浮動小数点値で動作するため、精度は許容範囲内であると思います。また、非常に遅く、このマシンでの入力例に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èじゃない、ハハ...ハハ...私はおかしい。
魔法のタコ

@MagicOctopusUrn修正済み... 05AB1Eが0インデックス付きであることを忘れていました> _ <
Mr. Xcoder

3

ゼリー、19バイト

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

オンラインでお試しください!

代替バージョン、15バイト、ポストデートチャレンジ

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

Jellyはついに 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バイト

  • 2つのポストインクリメントとループ配置をいじることで3バイトを節約しました。
  • 他のポストインクリメントをいじって、両方の変数の初期化をブランチの前に移動して、3バイトを節約if(...)...k=0...else...,j=0...しました。if(k=j=0,...)...else...行って、インデックスシフトを実行して、。
  • サポートに必要な11バイト floatマトリックスです。
  • Xcoder氏に感謝しました。ゴルフ2*j+++1j-~j++
  • 余分なものを削除して20バイトを節約 int変数型宣言をし、階乗関数を使用せずに、既存のforループを使用して階乗値を計算。
  • 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
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)

オンラインでお試しください!

編集:-6バイトのVloに感謝します。

ここの誰もが順列を使用して標準参照アルゴリズムを実装しているようですが、関連する課題で得られたコミュニティの知識を活用しようとしましたで。

行列のスライス(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 niftio.run/##XU/LCsIwELz7FcFTVtOQl1pf1/...
VLO

きちんとした!私はそれを速度の挑戦に投稿すると言いますが、おそらくいくつかの最適化(スレッド化など)を行うことができます。 。
ジュゼッペ

ベンチマークの目的で実行してください!
Vlo

実際にこれを速度でテストしてみましたが、結果にすぐにがっかりしました。同じ正確なアルゴリズムを使用した速度チャレンジで最も遅いPythonサブミッションは、TIOで数秒で24x24マトリックスをクランチしますが、Rはタイムアウトします。私のローカルマシン上で、それはまた...「メモ」パッケージからメモ化を支援しても、合理的な時間内に応答しなかった
キリル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いいね
ディルナン

@milesすごい節約です。私はそれを私の答えに編集しますが、それはかなり異なるので、あなたが望むならあなた自身の答えを提出してください
-dylnan


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

Perl 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.