Wyzwanie polega na napisaniu najszybszego możliwego kodu do obliczenia Hafniana matrycy .
Hafnian symetrycznego 2n
-by- 2n
matrycę A
określa się jako:
Tutaj S 2n reprezentuje zestaw wszystkich permutacji liczb całkowitych od 1
do 2n
, to znaczy [1, 2n]
.
Link do wikipedii daje również inną formułę, która może być interesująca (a jeszcze szybsze metody istnieją, jeśli spojrzysz dalej w Internecie). Ta sama strona wiki mówi o macierzach przylegania, ale twój kod powinien również działać dla innych macierzy. Możesz założyć, że wszystkie wartości będą liczbami całkowitymi, ale nie że wszystkie są dodatnie.
Istnieje również szybszy algorytm, ale wydaje się trudny do zrozumienia. a Christian Sievers jako pierwszy wdrożył go (w Haskell).
W tym pytaniu wszystkie macierze są kwadratowe i symetryczne o równych wymiarach.
Implementacja referencyjna (należy pamiętać, że jest to najwolniejsza możliwa metoda).
Oto przykładowy kod python od pana Xcodera.
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
Zadanie
Powinieneś napisać kod, który podany 2n
przez 2n
macierz, wyświetla swój Hafnian.
Ponieważ będę musiał przetestować kod, pomocne byłoby podanie prostego sposobu na przekazanie macierzy jako danych wejściowych do kodu, na przykład poprzez czytanie ze standardowego wejścia. Przetestuję kod w losowo wybranych matrycach z elementami wybrane z {-1, 0, 1}. Celem takich testów jest zmniejszenie szansy, że Hafnian będzie miał bardzo dużą wartość.
Idealnie twój kod będzie mógł czytać w matrycach dokładnie tak, jak ja je w przykładach w tym pytaniu prosto ze standardowego w. To byłoby wejście, które wyglądałoby [[1,-1],[-1,-1]]
na przykład. Jeśli chcesz użyć innego formatu wejściowego, zapytaj, a ja dołożę wszelkich starań, aby to uwzględnić.
Wyniki i krawaty
Przetestuję twój kod na losowych matrycach o rosnącym rozmiarze i zatrzymam się, gdy twój kod po raz pierwszy zajmie więcej niż 1 minutę na moim komputerze. Macierze oceny będą spójne dla wszystkich wniosków, aby zapewnić uczciwość.
Jeśli dwie osoby otrzymają ten sam wynik, zwycięzcą jest ta, która jest najszybsza dla tej wartości n
. Jeśli są one w odległości 1 sekundy od siebie, to jest to pierwszy.
Języki i biblioteki
Możesz użyć dowolnego dostępnego języka i bibliotek, które ci się podobają, ale nie ma wcześniejszej funkcji do obliczenia Hafniana. Tam, gdzie to możliwe, dobrze byłoby móc uruchomić kod, więc proszę podać pełne wyjaśnienie, w jaki sposób uruchomić / skompilować kod w systemie Linux, jeśli to w ogóle możliwe.
Moja maszyna Czasy zostaną uruchomione na moim komputerze 64-bitowym. Jest to standardowa instalacja ubuntu z 8 GB pamięci RAM, ośmiordzeniowym procesorem AMD FX-8350 i Radeon HD 4250. Oznacza to również, że muszę mieć możliwość uruchomienia kodu.
Zadzwoń po więcej języków
Byłoby wspaniale uzyskać odpowiedzi w swoim ulubionym, bardzo szybkim języku programowania. Na początek, co powiesz na fortran , nim i rdzę ?
Tabela liderów
- 52 przez mile przy użyciu C ++ . 30 sekund.
- 50 przy użyciu NGN C . 50 sekund.
- 46 Christian Sievers korzystający z Haskell . 40 sekund.
- 40 mil za pomocą Python 2 + pypy . 41 sekund.
- 34 przez ngn przy użyciu Python 3 + pypy . 29 sekund.
- 28 autorstwa Dennisa przy użyciu Pythona 3 . 35 sekund. (Pypy jest wolniejszy)