Hafnian'ı olabildiğince çabuk hesapla


12

Zorluk, bir matrisin Hafnianını hesaplamak için mümkün olan en hızlı kodu yazmaktır .

Simetrik 2n-by- 2nmatrisin Hafnyanı Aşu şekilde tanımlanır:

İşte S 2n gelen sayının tüm permütasyon kümesini temsil 1etmek 2nolduğunu, [1, 2n].

Vikipedi bağlantısı da ilgi çekici olabilecek farklı görünümlü bir formül verir (ve web'e daha fazla bakarsanız daha hızlı yöntemler de mevcuttur). Aynı wiki sayfası bitişik matrislerden bahseder, ancak kodunuz diğer matrisler için de çalışmalıdır. Değerlerin hepsinin tamsayı olacağını, ancak hepsinin pozitif olduğunu varsayamazsınız.

Ayrıca daha hızlı bir algoritma var ama anlaşılması zor görünüyor. ve Christian Sievers bunu ilk uygulayan oldu (Haskell'de).

Bu soruda matrislerin hepsi kare ve eşit boyutta simetriktir.

Başvuru uygulaması (bunun mümkün olan en yavaş yöntemi kullandığını unutmayın).

Bay Xcoder'dan bazı örnek python kodları.

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

Görev

Bir matris 2ntarafından verilen 2nHafnian çıktısını veren bir kod yazmalısınız.

Kodunuzu test etmem gerekeceğinden, kodunuza girdi olarak bir matris vermem için basit bir yol verebilirseniz, örneğin standarttan okuyarak kodunuzu test etmeniz yararlı olacaktır. {-1, 0, 1} arasından seçildi. Bunun gibi testlerin amacı Hafnian'ın çok büyük bir değer olma şansını azaltmaktır.

İdeal olarak kodunuz matrislerde tam olarak bu sorudaki örneklerde olduğu gibi doğrudan standart olarak okuyabilecektir. Bu, [[1,-1],[-1,-1]]örneğin örneğin giriş gibi görünecektir . Başka bir giriş formatı kullanmak istiyorsanız, lütfen sorun; uyum sağlamak için elimden geleni yapacağım.

Skorlar ve bağlar

Kodunuzu artan boyuttaki rastgele matrislerde test edeceğim ve kodunuzun bilgisayarımda ilk kez 1 dakikadan fazla sürdüğünü durduracağım. Puanlama matrisleri, adaleti sağlamak için tüm başvurular için tutarlı olacaktır.

Eğer iki kişi aynı puanı alırsa, kazanan o değerin en hızlı olanıdır n. Bunlar birbirinin 1 saniyesindeyse, ilk önce gönderilir.

Diller ve kütüphaneler

Hafnian'ı hesaplamak için istediğiniz herhangi bir dili ve kütüphaneyi kullanabilirsiniz, ancak önceden var olan bir işlevi kullanamazsınız. Mümkünse, kodunuzu çalıştırabilmeniz iyi olur, bu yüzden lütfen mümkünse kodunuzu Linux'ta nasıl çalıştıracağınız / derleyeceğiniz konusunda tam bir açıklama ekleyin.

Benim Makine zamanlamaları benim 64 bit makinede işletilecek. Bu, 8GB RAM, AMD FX-8350 Sekiz Çekirdekli İşlemci ve Radeon HD 4250 ile standart bir ubuntu yüklemesidir. Bu aynı zamanda kodunuzu çalıştırabilmem gerektiği anlamına gelir.

Daha fazla dilde yanıt arayın

En sevdiğiniz süper hızlı programlama dilinde cevaplar almak harika olurdu. İşe başlamak için fortran , nim ve pas ne dersiniz ?

Liderler Sıralaması

  • C ++ kullanarak 52 km . 30 saniye.
  • 50 ngn ile C kullanılarak . 50 saniye.
  • 46 Haskell kullanarak Christian Sievers tarafından . 40 saniye.
  • Python 2 + pypy kullanarak 40 km . 41 saniye.
  • 34 ngn tarafından Python 3 + pypy kullanılarak . 29 saniye.
  • 28 Dennis tarafından Python kullanarak 3 . 35 saniye. (Pypy daha yavaştır)

Matris girişlerinin mutlak değerleri için bir sınır var mı? Kayan nokta yaklaşımı döndürebilir miyiz? Rasgele hassasiyet tamsayıları kullanmak zorunda mıyız?
Dennis

@Dennis Pratikte test etmek için sadece -1,0,1 kullanacağım (rastgele seçilir). Bunun büyük bir int meydan okuması olmasını istemiyorum. Dürüst olmak gerekirse, kod çalıştırmak için çok yavaş hale gelmeden önce 64 bit ints sınırlarını vuracak mıyız bilmiyorum ama benim tahminim olmaz. Şu anda bunun hiçbir yerinde değiliz.

Girişler -1,0,1 ile sınırlıysa , bu soruda belirtilmelidir. Kodumuzun diğer matrisler için hiç çalışması gerekiyor mu?
Dennis

@Dennis Eski bir versiyon bunu söylerdi ama üzerine yazmış olmalıydım. Kod -1,0,1 girişleri için özel olmasaydı tercih ederdim ama herhalde bunu durduramıyorum.

Daha fazla test vakanız var mı? belki daha büyük n için ?
mil

Yanıtlar:


14

Haskell

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

Bu, Andreas Björklund'un Algoritması 2'nin bir varyasyonunu gerçekleştirir : Mükemmel Eşleşmeleri Ryser kadar Hızlı Saymak .

Derleme ghczamanı seçenekleriyle derleme -O3 -threadedve +RTS -Nparalelleştirme için çalışma zamanı seçeneklerini kullanma . Stdin'den girdi alır.


2
Belki unutmayın parallelve vectorkurulması gerekir?
H.PWiz

@ H.PWiz Burada kimse şikayet etmedi , ama emin değilim, acıtmayacak. Didimdi yaptýn.
Christian Sievers

@ChristianSievers Şikayet ettiklerini sanmıyorum. OP, Haskell'e aşina olmayabilir, bu nedenle kodun zamanlamasını sağlamak için neyin yüklenmesi gerektiğini açıkça belirtmek iyi bir fikirdir.
Dennis

@Dennis "şikayet ettiniz" demek istemedim ama "bunu not ettiniz". Şikayet etmeyi olumsuz bir şey olarak düşünmedim. OP, bağlandığım zorluktakiyle aynı, bu yüzden sorun olmamalı.
Christian Sievers

TIO'da 7.5 saniyede N = 40 ... Adamım, bu hızlı!
Dennis

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())))))

Çevrimiçi deneyin!


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];
}

Çevrimiçi deneyin! (N = 24 için 13s)

Diğer yazımdaki daha hızlı Python uygulamasına dayanıyor . #define S 38 çekirdekli makinenizde ikinci satırı düzenleyin ve ile derleyin g++ -pthread -march=native -O2 -ftree-vectorize.

Çalışmayı ikiye böler, bu yüzden değeri Solmalıdır log2(#threads). Türleri arasında kolaylıkla değiştirilebilir int, long, floatve doubledeğerini değiştirerek #define TYPE.


Bu şimdiye kadar önde gelen cevaptır. Kodunuz boşluklarla baş edemediği için girişte belirtildiği gibi okunmuyor. Yapmak zorunda kaldım örneğintr -d \ < matrix52.txt > matrix52s.txt

@Lembik Üzgünüm, sadece 24 boyutlu boşluksuz matris karşısında kullandı. Şimdi boşluklarla çalışmak için düzeltildi.
mil

4

Python 3

Bu, haf (A) 'yı kaydedilmiş bir toplam (A [i] [j] * haf (A ve satır i ve j içermeyen A) olarak hesaplar.

#!/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

Andreas Björklund'un makalesinin bir başka implantı , Christian Sievers'ın Haskell koduna da bakarsanız daha kolay . Özyinelemenin ilk birkaç seviyesi için, yuvarlak iş parçacıkları kullanılabilir CPU'lara dağıtır. Çağrıların yarısını oluşturan özyinelemenin son düzeyi elle optimize edilir.

Derleme gcc -O3 -pthread -march=native:; 2x hız için teşekkürler @Dennis

TIO'da 24s'de 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;
}

Algoritma:

Simetrik olan matris, sol alt üçgen formda saklanır. Üçgen indeksler , bir makronun bulunduğu i,jdoğrusal indekse karşılık gelir . Matris elemanları derece polinomlarıdır . Bir polinom, sabit terim ( ) ile x n'nin katsayısı (T(max(i,j))+min(i,j)Ti*(i-1)/2np[0]p[n] ) . İlk -1,0,1 matris değerleri ilk olarak sabit polinomlara dönüştürülür.

İki argümanla özyinelemeli bir adım gerçekleştiriyoruz: apolinomların yarı matrisi (yani üçgen) ve ayrı bir polinom p(gazetede beta olarak adlandırılır). Boyut mproblemini (başlangıçta m=2*n) tekrarlayan şekilde iki boyut problemine m-2indiriyoruz ve hafnalılarının farkını geri veriyoruz. Bunlardan biri a, son iki satırı olmadan aynısını kullanmak ve aynı şeyi kullanmaktır p. Bir diğeri üçgeni kullanmaktır b[i][j] = a[i][j] + shmul(a[m-1][i],a[m-2][j]) + shmul(a[m-1][j],a[m-2][i])(burada shmulpolinomlar üzerinde kaydırma-çarpma işlemi - her zamanki gibi polinom ürünü, ek olarak "x" değişkeni ile çarpılır; x ^ n'den yüksek güçler yok sayılır) ve ayrı polinom q = p + shmul(p,a[m-1][m-2]). Özyineleme bir boyut 0'a çarptığında a, p: nin ana katsayısını döndürürüz p[n].

Shift ve çarpma işlemi işlevde uygulanır f(x,y,z). O değiştirir zyerinde. Gevşek konuşursak, öyle z += shmul(x,y). Bu, performans açısından en kritik kısım gibi görünüyor.

Özyineleme bittikten sonra, sonuç işaretini (-1) n ile çarparak düzeltmemiz gerekir .


Kodunuzun kabul ettiği girdinin açık bir örneğini gösterebilir misiniz? 2'ye 2 matrisini söyle. Ayrıca, kodunuzu golf var gibi görünüyor! (Bu bir golf zorluğu değil, en hızlı kodlu bir mücadeledir.)

@Lembik Kayıt için, dediğim gibi, giriş örneklerle aynı formattadır - json (aslında sadece sayıları okur ve n = sqrt (len (giriş)) / 2 kullanır). Golf zorunluluğu olmasa bile genellikle kısa kod yazıyorum.
ngn

Bu yeni kodun desteklemesi gereken en büyük boyut matrisi nedir?

1
-march=nativeburada büyük bir fark yaratacak. En azından TIO'da, duvar süresini neredeyse yarıya indiriyor.
Dennis

1
Ayrıca, en azından TIO'da, gcc tarafından üretilen yürütülebilir dosya daha da hızlı olacaktır.
Dennis

3

piton

Bu, Algoritma 2'nin bahsedilen makaleden hemen hemen basit, referans bir uygulamasıdır . Sadece değişiklikler yalnızca mevcut değerini tutmak için olan B değerlerini bırakarak, p sadece güncelleyerek g zaman iX ve tepe bölümü kesik polinom çarpma sadece derecesi değerleri hesaplanarak 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

Çevrimiçi deneyin!

İşte bazı kolay optimizasyonlarla daha hızlı bir sürüm.

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)

Çevrimiçi deneyin!

Daha fazla eğlence için, J'de bir referans uygulaması .


Bu, tüm liste kavrayışlarından ve köşegen boyunca eşdeğer değerleri hesaplamaktan oldukça yavaştır, bu yüzden bunu karşılaştırmaya gerek yoktur.
mil

Oldukça harika!

Çok hoş! Şok edici derecede yavaş olan ve küçük örnekler için doğru olurken, - uzun bir süre sonra - 24 * 24 matris için yanlış bir sonuç veren benzer bir şey denedim. Orada neler olduğu hakkında hiçbir fikrim yok. - Algoritma 2'nin yukarıdaki açıklamasına göre, orada polinom çarpımı zaten kesilmiş olmalıdır.
Christian Sievers

2
Olarak pmulkullanmak for j in range(n-i):ve önlemekif
Christian Sievers

1
@Lembik Tüm matrisi hesaplar; İki yaklaşık bir faktör için yerini j != kile j < k. Diğer durumda, ilk iki satır ve sütun yerine son ikisini işlediğimizde ve sildiğimizde önlenebilecek bir alt matrisi kopyalar. Ve x={1,2,4}sonra ve sonra ile hesapladığında x={1,2,4,6}, hesaplamalarını tekrar eder i=5. İki dış halkanın yapısını ilk döngüde ive daha sonra tekrar tekrar i in Xve varsayarak tekrarladım i not in X. - BTW, Diğer yavaş programlara kıyasla ihtiyaç duyulan zamanın büyümesine bakmak ilginç olabilir.
Christian Sievers

1

Oktav

Bu temelde Dennis'in girişinin bir kopyasıdır , ancak Octave için optimize edilmiştir. Ana optimizasyon, azaltılmış matrisler oluşturmak yerine tam giriş matrisi (ve onun dönüşümü) ve özyineleme yalnızca matris indeksleri kullanılarak yapılır.

Ana avantaj matrislerin kopyalanmasını azaltır. Oktav'ın işaretçiler / referanslar ve değerler arasında bir farkı yoktur ve işlevsel olarak sadece by-pass değeri olsa da, sahne arkasında farklı bir hikaye. Burada, üzerine yazma (tembel kopya) kullanılır. Bu, kod a=1;b=a;b=b+1için değişkenin değiştirildiği bzaman yalnızca son ifadede yeni bir konuma kopyalandığı anlamına gelir . Yana matinve matranspasla değiştirilir, bunlar kopyalanmış asla tarafından. Dezavantajı, fonksiyonun doğru endeksleri hesaplamak için daha fazla zaman harcamasıdır. Bunu optimize etmek için sayısal ve mantıksal indeksler arasında farklı varyasyonlar denemek zorunda kalabilirim.

Önemli not: giriş matrisi olmalı int32! İşlevi adlı bir dosyaya kaydedinhaf.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

Örnek test komut dosyası:

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 ve MATLAB kullanarak bunu denedim (Asla Octave'i yüklemedim). Sanırım işe almak kadar basit sudo apt-get install octave. Komut octave, Octave GUI'yi yükleyecektir. Bundan daha karmaşıksa, daha ayrıntılı kurulum talimatları verene kadar bu cevabı sileceğim.


0

Son zamanlarda Andreas Bjorklund, Brajesh Gupt ve ben karmaşık matrislerin Hafnalıları için yeni bir algoritma yayınladık: https://arxiv.org/pdf/1805.12498.pdf . N \ times n matrisi için n ^ 3 2 ^ {n / 2} gibi ölçeklenir.

Andreas'ın orijinal algoritmasını https://arxiv.org/pdf/1107.4466.pdf adresinden doğru bir şekilde anlarsam, n ^ 4 2 ^ {n / 2} veya n ^ 3 günlüğü (n) 2 ^ {n / 2} gibi ölçeklenir. polinom çarpmalarını yapmak için Fourier dönüşümleri kullandıysanız.
Algoritmamız karmaşık matrisler için özel olarak tasarlanmıştır, bu nedenle burada {-1,0,1} matrisler için geliştirilenler kadar hızlı olmayacaktır. Bununla birlikte, uygulamanızı geliştirmek için kullandığınız bazı püf noktalarını kullanıp kullanamayacağınızı merak ediyorum. Ayrıca insanlar ilgiliyse, tamsayılar yerine karmaşık sayılar verildiğinde uygulamalarının nasıl yapıldığını görmek isterim. Son olarak, depomuzda herhangi bir yorum, eleştiri, iyileştirme, hata, iyileştirme memnuniyetle karşılanmaktadır https://github.com/XanaduAI/hafnian/

Şerefe!


Siteye Hoşgeldiniz! Ancak bu soruya verilen cevaplar kod içermelidir. Bu daha iyi bir yorum olarak bırakılır, (Hangi maalesef yapmak için temsilcisi yok).
Ad Hoc Garf Hunter

PPCG'ye hoş geldiniz. Cevabınız iyi bir yorum yapabilir, ancak site KG için değildir. Bu bir meydan okuma sitesi ve bir meydan okuma cevap kodu olmalı ve başka bir şeyin açıklaması değil. Lütfen güncelleyin veya silin (
Muhammad Salman

Kod github'da, ama sanırım buraya kopyalayıp yapıştırmak saçma geliyor.
Nicolás Quesada

2
Bir cevaba uyuyorsa, özellikle yazarlardan biriyseniz, başka bir yerde yayınlanmış uygun şekilde ilişkilendirilmiş, rekabetçi bir çözüm yayınlamanın yanlış bir şey olduğunu düşünmüyorum.
Dennis

@ NicolásQuesada Bu sitedeki yanıtlar mümkünse müstakil olmalıdır, yani cevabınızı / kodunuzu görüntülemek için başka bir siteye gitmemiz gerekmemelidir.
mbomb007
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.