尽快计算Hafnian


12

挑战在于编写可能最快的代码来计算矩阵Hafnian

对称2n2n矩阵的Hafnian A定义为:

这里S 2n表示整数从1to 的所有置换的集合2n,即[1, 2n]

Wikipedia链接还提供了一个可能引起您兴趣的不同外观公式(如果您在网络上进一步浏览,甚至可以使用更快的方法)。相同的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矩阵,并输出其Hafnian。

因为我将需要测试您的代码,所以如果您能提供一种简单的方法将矩阵作为代码的输入,这将很有帮助,例如通过读取标准输入。我将使用元素随机选择矩阵来测试代码从{-1,0,1}中选择。这样测试的目的是为了减少Hafnian值很大的机会。

理想情况下,您的代码将能够完全按照我在本问题中的示例中的格式直接从标准输入中读取矩阵。这就是[[1,-1],[-1,-1]]例如输入。如果您想使用其他输入格式,请询问,我将尽力适应。

得分和联系

我将在大小增加的随机矩阵上测试您的代码,并在您的代码第一次在我的计算机上花费超过1分钟时停止。所有提交的评分矩阵将保持一致,以确保公平。

如果两个人获得相同的分数,那么胜者就是那个值最快的人n。如果它们彼此相距1秒以内,则它是第一个发布的。

语言和图书馆

您可以使用任何喜欢的可用语言和库,但不能使用预先存在的函数来计算Hafnian。在可行的情况下,能够运行您的代码将是一个很好的选择,因此,请尽可能提供有关如何在Linux中运行/编译代码的完整说明。

我的机器计时将在我的64位机器上运行。这是带有8GB RAM,AMD FX-8350八核处理器和Radeon HD 4250的标准ubuntu安装。这也意味着我需要能够运行您的代码。

要求更多语言的答案

最好用您喜欢的超快速编程语言获得答案。首先,fortrannimrust怎么样?

排行榜

  • 使用C ++达到52英里。30秒。
  • 50通过NGN使用Ç。50秒
  • 46由Christian Sievers使用Haskell撰写。40秒
  • 使用Python 2 + pypy达到40英里。41秒。
  • 34,通过使用NGN 的Python 3 + pypy。29秒。
  • 28由Dennis使用Python 3编写。35秒 (Pypy慢一些)

矩阵条目的绝对值是否有限制?我们可以返回浮点近似值吗?我们必须使用任意精度的整数吗?
丹尼斯

@Dennis实际上,我只会使用-1,0,1进行测试(随机选择)。我不希望这是一个很大的int挑战。老实说,我不知道我们是否会在代码变得太慢之前无法达到64位整数的极限,但是我想我们不会。目前,我们离这还很遥远。

如果条目限制为-1,0,1,则应在问题上提及。我们的代码是否必须完全适用于其他矩阵?
丹尼斯

@Dennis一个旧版本曾经这样说,但是我必须写了它。如果代码不是专门用于-1,0,1条目的,我会更喜欢它,但是我想我不能阻止它。

您还有更多的测试用例吗?也许对于更大的n
英里

Answers:


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
也许注意,parallelvector必须安装?
H.PWiz '18

@ H.PWiz 这里没有人抱怨,但可以肯定的是,它不会造成伤害。好吧,现在你做到了。
Christian Sievers

@ChristianSievers我不认为他们在抱怨。OP可能不熟悉Haskell,因此明确说明必须安装什么才能计时代码是一个好主意。
丹尼斯

@丹尼斯我不是说“您确实抱怨过”,而是“您确实注意到了”。而且我没有想到抱怨是负面的。OP与我链接到的挑战相同,因此应该没有问题。
Christian Sievers

在TIO上的7.5秒内N = 40 ...真是太快了!
丹尼斯

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核计算机上,并使用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

这会将haf(A)计算为存储的总和(A [i] [j] * haf(A没有行和列i和j的行)。

#!/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对应于线性索引T(max(i,j))+min(i,j),其中T是的宏i*(i-1)/2。矩阵元素是度的多项式n。多项式表示为从常数项(p[0])到x n的系数(p[n])。初始-1​​,0,1矩阵值首先转换为const多项式。

我们使用两个参数执行递归步骤:a多项式的半矩阵(即三角形)和一个单独的多项式p(在本文中称为beta)。我们将大小m问题(最初m=2*n)递归地减少为两个大小问题,m-2并返回它们的ha夫差。其中之一是使用相同的内容a而没有最后两行,并且非常相同p。另一个方法是使用三角形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]

移位和乘法运算在函数中实现f(x,y,z)。它就地修改z。松散地说,确实如此z += shmul(x,y)。这似乎是性能最关键的部分。

递归完成后,我们需要乘以(-1)n来固定结果的符号。


您能否显示代码接受的输入的明确示例?说出2 x 2矩阵。另外,您似乎已经打了代码!(这是最快的代码挑战,而不是高尔夫挑战。)

@Lembik作为记录,正如我在聊天中所说,输入的格式与示例相同-json(实际上,它仅读取数字并使用n = sqrt(len(input))/ 2)。即使不需要打高尔夫球,我通常也会编写短代码。
ngn

新代码应支持的最大尺寸矩阵是多少?

1
-march=native在这里将有很大的不同。至少在TIO上,它几乎将挂墙时间减少了一半。
丹尼斯

1
同样,至少在TIO上,gcc生成的可执行文件甚至会更快。
丹尼斯

3

蟒蛇

这几乎是上述论文中算法2的直接参考实现。唯一的变化是仅保持的当前值,滴的值β通过仅更新X,和截短的多项式乘法仅通过计算值高达度Ñ

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的描述,多项式乘法已经被截断了。
Christian Sievers

2
pmul,使用for j in range(n-i):和避免if
Christian Sievers

1
@Lembik它计算整个矩阵;为约两倍的替换j != kj < k。在其他情况下,它会复制一个子矩阵,当我们处理和删除后两行而不是前两行和列时,可以避免这种情况。当使用x={1,2,4}和之后进行x={1,2,4,6}计算时,它将重复其计算直到i=5。我先用on循环替换了两个外部循环的结构,i然后递归地假设i in Xand i not in X。-顺便说一句,与其他较慢的程序相比,查看所需时间的增长可能会很有趣。
Christian Sievers

1

八度

这基本上是Dennis条目的副本,但已针对Octave进行了优化。主要优化是通过使用完整的输入矩阵(及其转置)和仅使用矩阵索引进行递归来完成的,而不是创建归约矩阵。

主要优点是减少了矩阵的复制。虽然Octave在指针/引用和值之间没有区别,并且在功能上仅传递值,但这是幕后的故事。在那里,使用写时复制(惰性复制)。这意味着,对于代码而言a=1;b=a;b=b+1b更改后,变量只会复制到最后一条语句的新位置。由于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

最近,Andreas Bjorklund,Brajesh Gupt和我本人发布了一种用于复杂矩阵的Hafnian的新算法: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/中,欢迎您提出任何评论,批评,改进,错误,改进。

干杯!


欢迎光临本站!但是,此问题的答案应包含代码。最好留个评论((不幸的是,您没有代表)。
Ad Hoc Garf Hunter

欢迎来到PPCG。尽管您的回答可能会做出很好的评论,但该网站不用于质量检查。这是一个挑战性站点,对挑战的回答必须具有代码,不能解释其他内容。请更新或删除(如果您不这样做,则将使用mods)
Muhammad Salman

好的,代码在github上,但是我想只是在这里复制粘贴就没有意义了。
尼古拉斯克萨达

2
如果它适合答案,尤其是如果您是其中的一位作者,那么我认为发布适当的,具有竞争力的解决方案(在其他地方发布)没有任何问题。
丹尼斯

@NicolásQuesada如果可能,此站点上的答案应该是独立的,这意味着我们不必去另一个站点查看您的答案/代码。
mbomb007 '18
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.