El desafío es escribir el código más rápido posible para calcular el hafniano de una matriz .
El hafniano de una matriz simétrica 2n
-por- se define como:2n
A
Aquí S 2n representa el conjunto de todas las permutaciones de los enteros de 1
a 2n
, es decir [1, 2n]
.
El enlace de wikipedia también ofrece una fórmula de aspecto diferente que puede ser de interés (e incluso existen métodos más rápidos si busca más en la web). La misma página wiki habla de matrices de adyacencia, pero su código también debería funcionar para otras matrices. Puede suponer que todos los valores serán enteros, pero no que sean todos positivos.
También hay un algoritmo más rápido, pero parece difícil de entender. y Christian Sievers fue el primero en implementarlo (en Haskell).
En esta pregunta, todas las matrices son cuadradas y simétricas con una dimensión par.
Implementación de referencia (tenga en cuenta que esto está utilizando el método más lento posible).
Aquí hay un ejemplo de código de Python del Sr. 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 tarea
Debería escribir un código que, dado 2n
por una 2n
matriz, genere su hafniano.
Como tendré que probar su código, sería útil si pudiera darme una forma simple de dar una matriz como entrada a su código, por ejemplo, leyendo desde el estándar. Probaré su código en matrices elegidas al azar con elementos seleccionado de {-1, 0, 1}. El propósito de pruebas como esta es reducir la posibilidad de que el Hafnian sea un valor muy grande.
Idealmente, su código podrá leerse en matrices exactamente como las tengo en los ejemplos de esta pregunta directamente desde el estándar. Así es como se vería la entrada, [[1,-1],[-1,-1]]
por ejemplo. Si desea utilizar otro formato de entrada, solicítelo y haré todo lo posible para adaptarlo.
Puntuaciones y lazos
Probaré su código en matrices aleatorias de tamaño creciente y me detendré la primera vez que su código tarde más de 1 minuto en mi computadora. Las matrices de puntuación serán consistentes para todas las presentaciones a fin de garantizar la equidad.
Si dos personas obtienen el mismo puntaje, entonces el ganador es el que tiene el valor más rápido n
. Si están dentro de 1 segundo el uno del otro, entonces es el publicado primero.
Idiomas y bibliotecas
Puede usar cualquier idioma y bibliotecas disponibles que desee, pero ninguna función preexistente para calcular el hafniano. Siempre que sea posible, sería bueno poder ejecutar su código, así que incluya una explicación completa sobre cómo ejecutar / compilar su código en Linux, si es posible.
Mi máquina Los tiempos se ejecutarán en mi máquina de 64 bits. Esta es una instalación estándar de ubuntu con 8 GB de RAM, procesador AMD FX-8350 de ocho núcleos y Radeon HD 4250. Esto también significa que necesito poder ejecutar su código.
Solicite respuestas en más idiomas.
Sería genial obtener respuestas en su lenguaje de programación súper rápido favorito. Para empezar, ¿qué tal fortran , nim y óxido ?
Tabla de clasificación
- 52 por millas usando C ++ . 30 segundos.
- 50 por ngn usando C . 50 segundos
- 46 por Christian Sievers usando Haskell . 40 segundos
- 40 por millas usando Python 2 + pypy . 41 segundos
- 34 por ngn usando Python 3 + pypy . 29 segundos
- 28 por Dennis usando Python 3 . 35 segundos (Pypy es más lento)