Le défi est d'écrire le code le plus rapide possible pour calculer le Hafnien d'une matrice .
Le Hafnian d'une matrice symétrique 2n
-par- est défini comme:2n
A
Ici S 2n représente l'ensemble de toutes les permutations des entiers de 1
à 2n
, c'est-à-dire [1, 2n]
.
Le lien wikipedia donne également une formule différente qui peut être intéressante (et des méthodes encore plus rapides existent si vous regardez plus loin sur le web). La même page wiki parle des matrices de contiguïté mais votre code devrait également fonctionner pour d'autres matrices. Vous pouvez supposer que les valeurs seront toutes des entiers mais pas qu'elles soient toutes positives.
Il existe également un algorithme plus rapide mais il semble difficile à comprendre. et Christian Sievers a été le premier à le mettre en œuvre (à Haskell).
Dans cette question, les matrices sont toutes carrées et symétriques avec une dimension paire.
Implémentation de référence (notez que cela utilise la méthode la plus lente possible).
Voici un exemple de code python de M. Xcoder.
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
La tâche
Vous devez écrire du code qui, donné 2n
par une 2n
matrice, génère son Hafnian.
Comme je devrai tester votre code, il serait utile que vous me donniez un moyen simple de donner une matrice en entrée à votre code, par exemple en lisant à partir de standard in. Je testerai votre code dans des matrices choisies au hasard avec des éléments sélectionné parmi {-1, 0, 1}. Le but de tests comme celui-ci est de réduire les chances que le Hafnian soit d'une très grande valeur.
Idéalement, votre code sera capable de lire dans les matrices exactement comme je les ai dans les exemples de cette question directement à partir de la norme en. C'est-à-dire que l'entrée ressemblerait [[1,-1],[-1,-1]]
par exemple. Si vous souhaitez utiliser un autre format d'entrée, veuillez demander et je ferai de mon mieux pour s'adapter.
Scores et égalités
Je vais tester votre code sur des matrices aléatoires de taille croissante et arrêter la première fois que votre code prend plus d'une minute sur mon ordinateur. Les matrices de notation seront cohérentes pour toutes les soumissions afin d'assurer l'équité.
Si deux personnes obtiennent le même score, le gagnant est celui qui est le plus rapide pour cette valeur de n
. Si ceux-ci sont à moins d'une seconde les uns des autres, c'est celui affiché en premier.
Langues et bibliothèques
Vous pouvez utiliser n'importe quelle langue et bibliothèque disponibles, mais aucune fonction préexistante pour calculer le hafnien. Dans la mesure du possible, il serait bon de pouvoir exécuter votre code, veuillez donc inclure une explication complète sur la façon d'exécuter / compiler votre code sous Linux si possible. »
Ma machine Les temporisations seront exécutées sur ma machine 64 bits. Il s'agit d'une installation Ubuntu standard avec 8 Go de RAM, processeur AMD FX-8350 à huit cœurs et Radeon HD 4250. Cela signifie également que je dois pouvoir exécuter votre code.
Appel à réponses dans plus de langues
Ce serait formidable d'obtenir des réponses dans votre langage de programmation super rapide préféré. Pour commencer, que diriez-vous du fortran , du nim et de la rouille ?
Classement
- 52 par miles en utilisant C ++ . 30 secondes.
- 50 en utilisant ngn C . 50 secondes.
- 46 par Christian Sievers utilisant Haskell . 40 secondes.
- 40 miles en utilisant Python 2 + pypy . 41 secondes.
- 34 par ngn en utilisant Python 3 + pypy . 29 secondes.
- 28 par Dennis en utilisant Python 3 . 35 secondes. (Pypy est plus lent)