ハフニアンをできるだけ早く計算する


12

課題は、行列のハフニアンを計算するために可能な限り速いコードを書くことです。

対称のHafnian 2n行列の2n行列はA次のように定義されます。

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

ウィキペディアのリンクはまた、興味を引くかもしれない異なる見た目の数式を提供します(さらにWebを見ると、さらに高速な方法が存在します)。同じWikiページで隣接行列について説明していますが、コードは他の行列でも機能するはずです。値はすべて整数であると仮定できますが、すべてが正であるとは限りません。

より高速なアルゴリズム もありますが、理解するのは難しいようです。そして、Christian Sieversが(Haskellで)最初に実装しました。

この質問では、行列はすべて正方形で、偶数次元で対称です。

参照実装(これは可能な限り遅い方法を使用していることに注意してください)。

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([[-1, 1, 1, -1, 0, 0, 1, -1], [1, 0, 1, 0, -1, 0, -1, -1], [1, 1, -1, 1, -1, -1, 0, -1], [-1, 0, 1, -1, -1, 1, -1, 0], [0, -1, -1, -1, -1, 0, 0, -1], [0, 0, -1, 1, 0, 0, 1, 1], [1, -1, 0, -1, 0, 1, 1, 0], [-1, -1, -1, 0, -1, 1, 0, 1]]))
4

M = [[1, 1, 0, 0, 0, 0, 0, 1, 0, 0], [1, 1, -1, 0, -1, 1, 1, 1, 0, -1], [0, -1, -1, -1, 0, -1, -1, 0, -1, 1], [0, 0, -1, 1, -1, 1, -1, 0, 1, -1], [0, -1, 0, -1, -1, -1, -1, 1, -1, 1], [0, 1, -1, 1, -1, 1, -1, -1, 1, -1], [0, 1, -1, -1, -1, -1, 1, 0, 0, 0], [1, 1, 0, 0, 1, -1, 0, 1, 1, -1], [0, 0, -1, 1, -1, 1, 0, 1, 1, 1], [0, -1, 1, -1, 1, -1, 0, -1, 1, 1]]

print(hafnian(M))
-13

M = [[-1, 0, -1, -1, 0, -1, 0, 1, -1, 0, 0, 0], [0, 0, 0, 0, 0, -1, 0, 1, -1, -1, -1, -1], [-1, 0, 0, 1, 0, 0, 0, 1, -1, 1, -1, 0], [-1, 0, 1, -1, 1, -1, -1, -1, 0, -1, -1, -1], [0, 0, 0, 1, 0, 0, 0, 0, 0, 1, -1, 0], [-1, -1, 0, -1, 0, 0, 1, 1, 1, 1, 1, 0], [0, 0, 0, -1, 0, 1, 1, -1, -1, 0, 1, 0], [1, 1, 1, -1, 0, 1, -1, 1, -1, -1, -1, -1], [-1, -1, -1, 0, 0, 1, -1, -1, -1, 1, -1, 0], [0, -1, 1, -1, 1, 1, 0, -1, 1, -1, 1, 1], [0, -1, -1, -1, -1, 1, 1, -1, -1, 1, 0, -1], [0, -1, 0, -1, 0, 0, 0, -1, 0, 1, -1, 1]]

print(hafnian(M))
13

M = [[-1, 1, 0, 1, 0, -1, 0, 0, -1, 1, -1, 1, 0, -1], [1, -1, 1, -1, 1, 1, -1, 0, -1, 1, 1, 0, 0, -1], [0, 1, 1, 1, -1, 1, -1, -1, 0, 0, -1, 0, -1, -1], [1, -1, 1, -1, 1, 0, 1, 1, -1, -1, 0, 0, 1, 1], [0, 1, -1, 1, 0, 1, 0, 1, -1, -1, 1, 1, 0, -1], [-1, 1, 1, 0, 1, 1, -1, 0, 1, -1, -1, -1, 1, -1], [0, -1, -1, 1, 0, -1, -1, -1, 0, 1, -1, 0, 1, -1], [0, 0, -1, 1, 1, 0, -1, 0, 0, -1, 0, 0, 0, 1], [-1, -1, 0, -1, -1, 1, 0, 0, 1, 1, 0, 1, -1, 0], [1, 1, 0, -1, -1, -1, 1, -1, 1, 1, 1, 0, 1, 0], [-1, 1, -1, 0, 1, -1, -1, 0, 0, 1, -1, 0, -1, 0], [1, 0, 0, 0, 1, -1, 0, 0, 1, 0, 0, 1, 1, 1], [0, 0, -1, 1, 0, 1, 1, 0, -1, 1, -1, 1, 1, -1], [-1, -1, -1, 1, -1, -1, -1, 1, 0, 0, 0, 1, -1, -1]]

print(hafnian(M))
83

タスク

2nby 2nマトリックスが与えられ、そのハフニアンを出力するコードを記述する必要があります。

コードをテストする必要があるので、たとえば標準入力から読み取ることによって、コードへの入力としてマトリックスを提供する簡単な方法を提供できると便利です。要素を持つランダムに選択されたマトリックスでコードをテストします{-1、0、1}から選択します。このようなテストの目的は、ハフニアンが非常に大きな値になる可能性を減らすことです。

理想的には、この質問の例にあるように、標準入力からまっすぐにコードをマトリックスで読み取ることができます。これは、[[1,-1],[-1,-1]]たとえば入力は次のようになります。別の入力形式を使用する場合は、お問い合わせください。対応できるよう最善を尽くします。

スコアとタイ

サイズが増加するランダムマトリックスでコードをテストし、コンピューターでコードが1分以上かかったときに停止します。スコアリングマトリックスは、公平性を確保するために、すべての提出物で一貫しています。

2人が同じスコアを獲得した場合、勝者はその値に対して最も速いものですn。それらが互いに1秒以内にある場合は、最初に投稿されたものです。

言語とライブラリ

使用可能な任意の言語とライブラリを使用できますが、ハフニアンを計算するための既存の関数は使用できません。可能であれば、コードを実行できるとよいので、可能な限りLinuxでコードを実行/コンパイルする方法の完全な説明を含めてください。

マイマシンタイミングは64ビットマシンで実行されます。これは、8GB RAM、AMD FX-8350 8コアプロセッサ、およびRadeon HD 4250を備えた標準のUbuntuインストールです。これは、コードを実行できる必要があることも意味します。

より多くの言語で回答を求める

お気に入りの超高速プログラミング言語で答えを得ることは素晴らしいことです。物事を始めるために、fortrannimrustはどうですか?

リーダーボード

  • C ++を使用して52マイル。30秒。
  • Cを使用したngnによる50。50秒。
  • 46クリスチャンシーバーズによるHaskellの使用。40秒。
  • Python 2 + pypyを使用して40マイル。41秒。
  • 34 by ngn by Python 3 + pypy。29秒。
  • Python 3を使用したDennisによる28。35秒。(Pypyは遅いです)

マトリックスエントリの絶対値に制限はありますか?浮動小数点近似を返すことができますか?任意の精度の整数を使用する必要がありますか?
デニス

@Dennis実際には、テストには-1,0,1のみを使用します(ランダムに選択)。私はそれが大きなintチャレンジになりたくありません。正直なところ、コードの実行が遅くなりすぎる前に64ビット整数の制限に達するかどうかはわかりませんが、私は推測します。現在、私たちはその近くにありません。

エントリが-1,0,1に制限されている場合、質問で言及する必要があります。私たちのコードは他の行列に対しても機能する必要がありますか?
デニス

@Dennis古いバージョンはそれを言っていましたが、私はそれを上書きしたに違いありません。コードが-1,0,1エントリに特化していない場合はそれを好むでしょうが、それを止めることはできないと思います。

さらにテストケースはありますか?おそらくより大きいnの
マイル

回答:


14

ハスケル

import Control.Parallel.Strategies
import qualified Data.Vector.Unboxed as V
import qualified Data.Vector as VB

type Poly = V.Vector Int

type Matrix = VB.Vector ( VB.Vector Poly )

constpoly :: Int -> Int -> Poly
constpoly n c = V.generate (n+1) (\i -> if i==0 then c else 0)

add :: Poly -> Poly -> Poly
add = V.zipWith (+)

shiftmult :: Poly -> Poly -> Poly
shiftmult a b = V.generate (V.length a) 
                           (\i -> sum [ a!j * b!(i-1-j) | j<-[0..i-1] ])
  where (!) = V.unsafeIndex

x :: Matrix -> Int -> Int -> Int -> Poly -> Int
x  _    0  _ m p = m * V.last p
x mat n c m p =
  let mat' = VB.generate (2*n-2) $ \i ->
             VB.generate i       $ \j ->
                 shiftmult (mat!(2*n-1)!i) (mat!(2*n-2)!j) `add`
                 shiftmult (mat!(2*n-1)!j) (mat!(2*n-2)!i) `add`
                 (mat!i!j)
      p' = p `add` shiftmult (mat!(2*n-1)!(2*n-2)) p
      (!) = VB.unsafeIndex
      r = if c>0 then parTuple2 rseq rseq else r0
      (a,b) = (x mat (n-1) (c-1) m p, x mat' (n-1) (c-1) (-m) p')
              `using` r
  in a+b

haf :: [[Int]] -> Int
haf m = let n=length m `div` 2
        in x (VB.fromList $ map (VB.fromList . map (constpoly n)) m) 
             n  5  ((-1)^n)  (constpoly n 1) 

main = getContents >>= print . haf . read

これは、AndreasBjörklundのアルゴリズム2のバリエーションを実装しています。完全一致をRyserのように高速にカウントします。

コンパイルghc時オプション-O3 -threadedを使用してコンパイルし+RTS -N、並列化にはランタイムオプションを使用します。stdinから入力を受け取ります。


2
多分ことに注意parallelしてvectorインストールする必要がありますか?
H.PWiz

@ H.PWiz ここで文句を言う人はいませんでしたが、確かに、それが痛くないことに注意してください。さて、あなたは今やった。
クリスチャンシーバーズ

@ChristianSievers私は彼らが不平を言っているとは思わない。OPはHaskellに慣れていない可能性があるため、コードの時間を計るために何をインストールする必要があるかを明示することは良い考えです。
デニス

@Dennis「文句を言った」という意味ではなく、「メモした」という意味です。そして、私は不満を否定的なものとは考えませんでした。OPは私がリンクしたチャレンジと同じなので、問題はないはずです。
クリスチャンシーバーズ

N = 7.5、TIOで7.5秒で...男、これは速い!
デニス

6

Python 3

from functools import lru_cache

@lru_cache(maxsize = None)
def haf(matrix):
	n = len(matrix)
	if n == 2: return matrix[0][1]
	h = 0
	for j in range(1, n):
		if matrix[0][j] == 0: continue
		copy = list(matrix)
		del copy[:j+1:j]
		copy = list(zip(*copy))
		del copy[:j+1:j]
		h += matrix[0][j] * haf(tuple(copy))
	return h

print(haf(tuple(map(tuple, eval(open(0).read())))))

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


6

C ++(gcc)

#define T(x) ((x)*((x)-1)/2)
#define S 1
#define J (1<<S)
#define TYPE int

#include <iostream>
#include <vector>
#include <string>
#include <pthread.h>

using namespace std;

struct H {
    int s, w, t;
    TYPE *b, *g;
};

void *solve(void *a);
void hafnian(TYPE *b, int s, TYPE *g, int w, int t);

int n, m, ti = 0;
TYPE r[J] = {0};
pthread_t pool[J];

int main(void) {
    vector<int> a;
    string s;
    getline(cin, s);

    for (int i = 0; i < s.size(); i++)
        if (s[i] == '0' || s[i] == '1')
            a.push_back((s[i-1] == '-' ? -1 : 1)*(s[i] - '0'));

    for (n = 1; 4*n*n < a.size(); n++);
    m = n+1;

    TYPE z[T(2*n)*m] = {0}, g[m] = {0};

    for (int j = 1; j < 2*n; j++)
        for (int k = 0; k < j; k++)
            z[(T(j)+k)*m] = a[j*2*n+k];
    g[0] = 1;

    hafnian(z, 2*n, g, 1, -1);

    TYPE h = 0;
    for (int t = 0; t < ti; t++) {
        pthread_join(pool[t], NULL);
        h += r[t];
    }

    cout << h << endl;

    return 0;
}

void *solve(void *a) {
    H *p = reinterpret_cast<H*>(a);
    hafnian(p->b, p->s, p->g, p->w, p->t);
    delete[] p->b;
    delete[] p->g;
    delete p;
    return NULL;
}

void hafnian(TYPE *b, int s, TYPE *g, int w, int t) {
    if (t == -1 && (n < S || s/2 == n-S)) {
        H *p = new H;
        TYPE *c = new TYPE[T(s)*m], *e = new TYPE[m];
        copy(b, b+T(s)*m, c);
        copy(g, g+m, e);
        p->b = c;
        p->s = s;
        p->g = e;
        p->w = w;
        p->t = ti;
        pthread_create(pool+ti, NULL, solve, p);
        ti++;
    }
    else if (s > 0) {
        TYPE c[T(s-2)*m], e[m];
        copy(b, b+T(s-2)*m, c);
        hafnian(c, s-2, g, -w, t);
        copy(g, g+m, e);

        for (int u = 0; u < n; u++) {
            TYPE *d = e+u+1,
                  p = g[u], *x = b+(T(s)-1)*m;
            for (int v = 0; v < n-u; v++)
                d[v] += p*x[v];
        }

        for (int j = 1; j < s-2; j++)
            for (int k = 0; k < j; k++)
                for (int u = 0; u < n; u++) {
                    TYPE *d = c+(T(j)+k)*m+u+1,
                          p = b[(T(s-2)+j)*m+u], *x = b+(T(s-1)+k)*m,
                          q = b[(T(s-2)+k)*m+u], *y = b+(T(s-1)+j)*m;
                    for (int v = 0; v < n-u; v++)
                        d[v] += p*x[v] + q*y[v];
                }

        hafnian(c, s-2, e, w, t);
    }
    else
        r[t] += w*g[n];
}

オンラインでお試しください!(n = 24の場合は13秒)

私の他の投稿のより高速なPython 実装に基づいています。#define S 38 コアマシンで2行目を編集し、でコンパイルしg++ -pthread -march=native -O2 -ftree-vectorizeます。

は作業を半分に分割するため、の値はであるS必要があります log2(#threads)。種類が容易との間に変更することができるintlongfloat、及びdoubleの値を変更することによって#define TYPE


これがこれまでの主要な答えです。コードは、スペースに対応できないため、指定された入力を実際には読み取りません。例:tr -d \ < matrix52.txt > matrix52s.txt

@Lembik申し訳ありませんが、サイズ24のスペースのないマトリックスに対してのみ使用されました。ただし、スペースで動作するように修正されました。
マイル

4

Python 3

これは、メモされたsum(A [i] [j] * haf(行と列iとjのないA))としてhaf(A)を計算します。

#!/usr/bin/env python3
import json,sys
a=json.loads(sys.stdin.read())
n=len(a)//2
b={0:1}
def haf(x):
 if x not in b:
  i=0
  while not x&(1<<i):i+=1
  x1=x&~(1<<i)
  b[x]=sum(a[i][j]*haf(x1&~(1<<j))for j in range(2*n)if x1&(1<<j)and a[i][j])
 return b[x]
print(haf((1<<2*n)-1))

3

C

AndreasBjörklundの論文の別の実装。これは、Christian SieversのHaskellのコードも見ると理解しやすいものです。再帰の最初の数レベルでは、利用可能なCPUにラウンドロビンスレッドを分散します。呼び出しの半分を占める再帰の最後のレベルは、手動で最適化されます。

でコンパイルしますgcc -O3 -pthread -march=native。@Dennisに2倍のスピードアップをありがとう

TIOの24秒でn = 24

#define _GNU_SOURCE
#include<sched.h>
#include<stdio.h>
#include<stdlib.h>
#include<memory.h>
#include<unistd.h>
#include<pthread.h>
#define W while
#define R return
#define S static
#define U (1<<31)
#define T(i)((i)*((i)-1)/2)
typedef int I;typedef long L;typedef char C;typedef void V;
I n,ncpu,icpu;
S V f(I*x,I*y,I*z){I i=n,*z1=z+n;W(i){I s=0,*x2=x,*y2=y+--i;W(y2>=y)s+=*x2++**y2--;*z1--+=s;}}
typedef struct{I m;V*a;V*p;pthread_barrier_t*bar;I r;}A;S V*(h1)(V*);
I h(I m,I a[][n+1],I*p){
 m-=2;I i,j,k=0,u=T(m),v=u+m,b[u][n+1],q[n+1];
 if(!m){I*x=a[v+m],*y=p+n-1,s=0;W(y>=p)s-=*x++**y--;R s;}
 memcpy(b,a,sizeof(b));memcpy(q,p,sizeof(q));f(a[v+m],p,q);
 for(i=1;i<m;i++)for(j=0;j<i;j++){f(a[u+i],a[v+j],b[k]);f(a[u+j],a[v+i],b[k]);k++;}
 if(2*n-m>8)R h(m,a,p)-h(m,b,q);
 pthread_barrier_t bar;pthread_barrier_init(&bar,0,2);pthread_t th;
 cpu_set_t cpus;CPU_ZERO(&cpus);CPU_SET(icpu++%ncpu,&cpus);
 pthread_attr_t attr;pthread_attr_init(&attr);
 pthread_attr_setaffinity_np(&attr,sizeof(cpu_set_t),&cpus);
 A arg={m,a,p,&bar};pthread_create(&th,&attr,h1,&arg);
 I r=h(m,b,q);pthread_barrier_wait(&bar);pthread_join(th,0);pthread_barrier_destroy(&bar);
 R arg.r-r;
}
S V*h1(V*x0){A*x=(A*)x0;x->r=h(x->m,x->a,x->p);pthread_barrier_wait(x->bar);R 0;}
I main(){
 ncpu=sysconf(_SC_NPROCESSORS_ONLN);
 S C s[200000];I i=0,j=0,k,l=0;W((k=read(0,s+l,sizeof(s)-l))>0)l+=k;
 n=1;W(s[i]!=']')n+=s[i++]==',';n/=2;
 I a[T(2*n)][n+1];memset(a,0,sizeof(a));k=0;
 for(i=0;i<2*n;i++)for(j=0;j<2*n;j++){
  W(s[k]!='-'&&(s[k]<'0'||s[k]>'9'))k++;
  I v=0,m=s[k]=='-';k+=m;W(k<l&&('0'<=s[k]&&s[k]<='9'))v=10*v+s[k++]-'0';
  if(i>j)*a[T(i)+j]=v*(1-2*m);
 }
 I p[n+1];memset(p,0,sizeof(p));*p=1;
 printf("%d\n",(1-2*(n&1))*h(2*n,a,p));
 R 0;
}

アルゴリズム:

対称のマトリックスは、左下の三角形の形式で格納されます。三角形のインデックスはi,j線形インデックスに対応するためのマクロです。行列要素は次数の多項式です。多項式は、定数項()からx nの係数()まで順序付けられた係数の配列として表されます。最初の-1,0,1行列値は、最初にconst多項式に変換されます。T(max(i,j))+min(i,j)Ti*(i-1)/2np[0]p[n]

2つの引数を使用して再帰的ステップを実行します。a多項式の半行列(つまり、三角形)と別の多項式p(論文ではベータと呼ばれます)。サイズのm問題を(最初はm=2*n)再帰的にサイズの2つの問題にm-2減らし、ハフニアンの差を返します。それらの1つはa、最後の2行なしで同じものを使用することpです。もう1つは、三角形b[i][j] = a[i][j] + shmul(a[m-1][i],a[m-2][j]) + shmul(a[m-1][j],a[m-2][i])(ここshmulで、多項式のシフト乗算操作です。通常の多項式積に変数「x」を追加したものです。x^ nより大きいべき乗は無視されます)と、別の多項式を使用しq = p + shmul(p,a[m-1][m-2])ます。再帰がサイズ0 aに達すると、p:の主要な係数を返しますp[n]

shift-and-multiply操作は、functionで実装されますf(x,y,z)zインプレースで変更します。大まかに言えば、そうz += shmul(x,y)です。これは、パフォーマンスが最も重要な部分のようです。

再帰が終了した後、(-1)nを掛けて結果の符号を修正する必要があります。


コードが受け入れる入力の明示的な例を示していただけますか?2行2列の行列について説明します。また、あなたはあなたのコードをゴルフしたようです!(これは、ゴルフチャレンジではなく、最速のコードチャレンジです。)

@Lembikチャットでは、チャットで述べたように、入力は例と同じ形式です-json(実際には、数値のみを読み取り、n = sqrt(len(input))/ 2を使用します)。ゴルフは必須ではない場合でも、私は通常短いコードを書きます。
ngn

この新しいコードがサポートする最大サイズのマトリックスは何ですか?

1
-march=nativeここで大きな違いが生まれます。少なくともTIOでは、壁時間をほぼ半分に短縮します。
デニス

1
また、少なくともTIOでは、gccによって生成される実行可能ファイルはさらに高速になります。
デニス

3

Python

これは、前述の論文のアルゴリズム2の単純な参照実装です。唯一の変更は、唯一の現在値保持していたBの値落とし、βをのみ更新することにより、GときIX、および短縮型多項式の乗算のみ程度の値を計算することによって、N

from itertools import chain,combinations

def powerset(s):
    return chain.from_iterable(combinations(s, k) for k in range(len(s)+1))

def padd(a, b):
    return [a[i]+b[i] for i in range(len(a))]

def pmul(a, b):
    n = len(a)
    c = [0]*n
    for i in range(n):
        for j in range(n):
            if i+j < n:
                c[i+j] += a[i]*b[j]
    return c

def hafnian(m):
    n = len(m) / 2
    z = [[[c]+[0]*n for c in r] for r in m]
    h = 0
    for x in powerset(range(1, n+1)):
        b = z
        g = [1] + [0]*n
        for i in range(1, n+1):
            if i in x:
                g = pmul(g, [1] + b[0][1][:n])
                b = [[padd(b[j+2][k+2], [0] + padd(pmul(b[0][j+2], b[1][k+2]), pmul(b[0][k+2], b[1][j+2]))[:n]) if j != k else 0 for k in range(2*n-2*i)] for j in range(2*n-2*i)]
            else:
                b = [r[2:] for r in b[2:]]
        h += (-1)**(n - len(x)) * g[n]
    return h

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

これは、簡単な最適化のいくつかを備えた高速バージョンです。

def hafnian(m):
  n = len(m)/2
  z = [[0]*(n+1) for _ in range(n*(2*n-1))]
  for j in range(1, 2*n):
    for k in range(j):
      z[j*(j-1)/2+k][0] = m[j][k]
  return solve(z, 2*n, 1, [1] + [0]*n, n)

def solve(b, s, w, g, n):
  if s == 0:
    return w*g[n]
  c = [b[(j+1)*(j+2)/2+k+2][:] for j in range(1, s-2) for k in range(j)]
  h = solve(c, s-2, -w, g, n)
  e = g[:]
  for u in range(n):
    for v in range(n-u):
      e[u+v+1] += g[u]*b[0][v]
  for j in range(1, s-2):
    for k in range(j):
      for u in range(n):
        for v in range(n-u):
          c[j*(j-1)/2+k][u+v+1] += b[(j+1)*(j+2)/2][u]*b[(k+1)*(k+2)/2+1][v] + b[(k+1)*(k+2)/2][u]*b[(j+1)*(j+2)/2+1][v]
  return h + solve(c, s-2, w, e, n)

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

追加の楽しみのために、ここにJのリファレンス実装があります


これは、すべてのリスト内包表記および対角線上の同等の値の計算からかなり遅いため、これをベンチマークする必要はありません。
マイル

めっちゃすごい!

非常に素晴らしい!私はsympyで同様のことを試みましたが、これは驚くほど遅く、小さな例では正しいものの、長い時間の後、24 * 24マトリックスに対して間違った結果を返しました。何が起こっているのか分かりません。-上記のアルゴリズム2の説明により、そこにある多項式の乗算はすでに切り捨てられることになっています。
クリスチャンシーバーズ

2
ではpmul、使用for j in range(n-i):して回避しますif
クリスチャンジーバーズ

1
@Lembikマトリックス全体を計算します。約2倍の係数はに置き換えj != kてくださいj < k。elseケースではサブマトリックスをコピーします。これは、最初の2つの行と列ではなく最後の2つを処理および削除するときに回避できます。そしてで計算しx={1,2,4}、その後で計算すると、x={1,2,4,6}最大での計算を繰り返しi=5ます。私は、最初にループを有する2つの外側ループの構造を交換iした後、再帰的仮定i in Xi not in X。-ところで、他のより遅いプログラムと比較して必要な時間の成長を見るのは面白いかもしれません。
クリスチャンシーバーズ

1

オクターブ

これは基本的にDennisのentryのコピーですが、Octave用に最適化されています。主な最適化は、縮小行列を作成するのではなく、完全な入力行列(およびその転置)と行列インデックスのみを使用した再帰を使用して行われます。

主な利点は、マトリックスのコピーの削減です。Octaveにはポインター/参照と値の違いはなく、機能的には値渡しのみを行いますが、舞台裏では別の話です。そこでは、コピーオンライト(遅延コピー)が使用されます。つまり、コードのa=1;b=a;b=b+1場合、変数bは変更されたときに最後のステートメントで新しい場所にのみコピーされます。以来matinmatransp変更されることはありません、彼らはコピーされませんで。欠点は、関数が正しいインデックスの計算により多くの時間を費やすことです。これを最適化するには、数値インデックスと論理インデックスの間でさまざまなバリエーションを試す必要がある場合があります。

重要な注意:入力行列はint32!と呼ばれるファイルに関数を保存しますhaf.m

function h=haf(matin,indices,matransp,transp)

    if nargin-4
        indices=int32(1:length(matin));
        matransp=matin';
        transp=false;
    end
    if(transp)
        matrix=matransp;
    else
        matrix=matin;
    end
    ind1=indices(1);
    n=length(indices);
    if n==2
        h=matrix(ind1,indices(2));
        return
    end
    h=0*matrix(1); 
    for j=1:(n-1)
        indj=indices(j+1);
        k=matrix(ind1,indj);
        if logical(k)
            indicestemp=true(n,1);
            indicestemp(1:j:j+1)=false;
            h=h+k.*haf(matin,indices(indicestemp),matransp,~transp);
        end
    end
end

テストスクリプトの例:

matrix = int32([0 0 1 -1 1 0 -1 -1 -1 0 -1 1 0 1 1 0 0 1 0 0 1 0 1 1;0 0 1 0 0 -1 -1 -1 -1 0 1 1 1 1 0 -1 -1 0 0 1 1 -1 0 0;-1 -1 0 1 0 1 -1 1 -1 1 0 0 1 -1 0 0 0 -1 0 -1 1 0 0 0;1 0 -1 0 1 1 0 1 1 0 0 0 1 0 0 0 1 -1 -1 -1 -1 1 0 -1;-1 0 0 -1 0 0 1 -1 0 1 -1 -1 -1 1 1 0 1 1 1 0 -1 1 -1 -1;0 1 -1 -1 0 0 1 -1 -1 -1 0 -1 1 0 0 0 -1 0 0 1 0 0 0 -1;1 1 1 0 -1 -1 0 -1 -1 0 1 1 -1 0 1 -1 0 0 1 -1 0 0 0 -1;1 1 -1 -1 1 1 1 0 0 1 0 1 0 0 0 0 1 0 1 0 -1 1 0 0;1 1 1 -1 0 1 1 0 0 -1 1 -1 1 1 1 0 -1 -1 -1 -1 0 1 1 -1;0 0 -1 0 -1 1 0 -1 1 0 1 0 0 0 0 0 1 -1 0 0 0 1 -1 -1;1 -1 0 0 1 0 -1 0 -1 -1 0 0 1 0 0 -1 0 -1 -1 -1 -1 -1 1 -1;-1 -1 0 0 1 1 -1 -1 1 0 0 0 -1 0 0 -1 0 -1 -1 0 1 -1 0 0;0 -1 -1 -1 1 -1 1 0 -1 0 -1 1 0 1 -1 -1 1 -1 1 0 1 -1 1 -1;-1 -1 1 0 -1 0 0 0 -1 0 0 0 -1 0 0 -1 1 -1 -1 0 1 0 -1 -1;-1 0 0 0 -1 0 -1 0 -1 0 0 0 1 0 0 1 1 1 1 -1 -1 0 -1 -1;0 1 0 0 0 0 1 0 0 0 1 1 1 1 -1 0 0 1 -1 -1 -1 0 -1 -1;0 1 0 -1 -1 1 0 -1 1 -1 0 0 -1 -1 -1 0 0 -1 1 0 0 -1 -1 1;-1 0 1 1 -1 0 0 0 1 1 1 1 1 1 -1 -1 1 0 1 1 -1 -1 -1 1;0 0 0 1 -1 0 -1 -1 1 0 1 1 -1 1 -1 1 -1 -1 0 1 1 0 0 -1;0 -1 1 1 0 -1 1 0 1 0 1 0 0 0 1 1 0 -1 -1 0 0 0 1 0;-1 -1 -1 1 1 0 0 1 0 0 1 -1 -1 -1 1 1 0 1 -1 0 0 0 0 0;0 1 0 -1 -1 0 0 -1 -1 -1 1 1 1 0 0 0 1 1 0 0 0 0 1 0;-1 0 0 0 1 0 0 0 -1 1 -1 0 -1 1 1 1 1 1 0 -1 0 -1 0 1;-1 0 0 1 1 1 1 0 1 1 1 0 1 1 1 1 -1 -1 1 0 0 0 -1 0])

tic
i=1;
while(toc<60)
    tic
    haf(matrix(1:i,1:i));
    i=i+1;
end

TIOとMATLABを使用してこれを試しました(実際にOctaveをインストールしたことはありません)。私はそれを機能させるのは簡単だと想像していsudo apt-get install octaveます。このコマンドoctaveは、Octave GUIをロードします。これより複雑な場合は、より詳細なインストール手順を提供するまでこの回答を削除します。


0

:最近アンドレアスBjorklund、Brajesh Guptと私は複雑なマトリックスのHafniansのための新しいアルゴリズム公表 https://arxiv.org/pdf/1805.12498.pdfを。n \ times n行列の場合、n ^ 3 2 ^ {n / 2}のようにスケーリングします。

https://arxiv.org/pdf/1107.4466.pdfからAndreasのオリジナルアルゴリズムを正しく理解している場合、n ^ 4 2 ^ {n / 2}またはn ^ 3 log(n)2 ^ {n / 2}のようにスケーリングします。フーリエ変換を使用して多項式の乗算を行った場合。
私たちのアルゴリズムは複雑な行列用に特別に調整されているため、{-1,0,1}行列用にここで開発されたものほど高速ではありません。しかし、私たちの実装を改善するために使用したいくつかのトリックを使用できるのだろうか?また、人々が興味を持っているなら、整数の代わりに複素数が与えられたときに彼らの実装がどのように行われるかを見たいです。最後に、リポジトリhttps://github.com/XanaduAI/hafnian/でコメント、批判、改善、バグ、改善を歓迎します

乾杯!


サイトへようこそ!ただし、この質問に対する回答にはコードを含める必要があります。これはコメントとして残したほうがいいでしょう(残念ながら、あなたは担当者がいません)。
ポストロックガーフハンター

PPCGへようこそ。あなたの答えは素晴らしいコメントをするかもしれませんが、このサイトはQA向けではありません。これはチャレンジサイトであり、チャレンジへの回答には他の何かの説明ではなくコードが必要です。親切に更新または削除します(そうしない場合、MODはそうなります)
ムハンマドサルマン

さて、コードはgithubにありますが、ここにコピーして貼り付けるだけでは意味がありません。
ニコラスケサダ

2
それが答えに収まる場合、特にあなたが著者の一人である場合、他の場所で公開された適切に属性付けされた競争力のあるソリューションを投稿することに何の問題もないと思います。
デニス

このサイトの@NicolásQuesadaAnswersは、可能であれば自己完結型である必要があります。つまり、回答/コードを表示するために別のサイトに移動する必要はありません。
mbomb007
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.