Cuenta matrices que son realmente únicas

9

Este es un seguimiento de las matrices de recuento que hacen conjuntos únicos . La diferencia significativa es la definición de unicidad.

Considere una variedad Ade longitud n. La matriz contiene solo enteros positivos. Por ejemplo A = (1,1,2,2). Definamos f(A)como el conjunto de sumas de todos los subconjuntos contiguos no vacíos de A. En este caso f(A) = {1,2,3,4,5,6}. Los pasos para producir f(A) son los siguientes:

Las submatrices de Ason (1), (1), (2), (2), (1,1), (1,2), (2,2), (1,1,2), (1,2,2), (1,1,2,2). Sus sumas respectivas son 1,1,2,2,2,3,4,4,5,6. El conjunto que obtienes de esta lista es por lo tanto {1,2,3,4,5,6}.

Llamamos a una matriz A única si no hay otra matriz Bde la misma longitud que f(A) = f(B), excepto por la matriz Ainvertida. Como ejemplo, f((1,2,3)) = f((3,2,1)) = {1,2,3,5,6}pero no hay otra matriz de longitud 3que produzca el mismo conjunto de sumas.

Tarea

La tarea, para un dado ny ses contar el número de matrices únicas de esa longitud. Puede suponer que sestá entre 1y 9. Solo necesita contar matrices donde los elementos son un número entero dado so s+1. Por ejemplo, si s=1las matrices que está contando solo contienen 1y 2. Sin embargo, la definición de unicidad es con respecto a cualquier otra matriz de la misma longitud. Como ejemplo concreto no[1, 2, 2, 2] es único, ya que da el mismo conjunto de sumas que .[1, 1, 2, 3]

Debe contar el reverso de una matriz, así como la matriz misma (siempre que la matriz no sea un palíndromo, por supuesto).

Ejemplos

s = 1, las respuestas para n = 2,3,4,5,6,7,8,9 son:

4, 3, 3, 4, 4, 5, 5, 6

Para s = 1, las matrices únicas de longitud 4 son

(1, 1, 1, 1)
(2, 1, 1, 2)
(2, 2, 2, 2)

s = 2, las respuestas para n = 2,3,4,5,6,7,8,9 son:

4, 8, 16, 32, 46, 69, 121, 177

Un ejemplo de una matriz que no es única con s = 2es:

(3, 2, 2, 3, 3, 3). 

Tiene el mismo conjunto de sumas que ambas: (3, 2, 2, 2, 4, 3)y (3, 2, 2, 4, 2, 3).

s = 8, las respuestas para n = 2,3,4,5,6,7,8,9 son:

4, 8, 16, 32, 64, 120, 244, 472

Puntuación

Para un determinado n, su código debe generar la respuesta para todos los valores de sfrom 1a 9. Su puntaje es el valor más alto npara el cual esto se completa en un minuto.

Pruebas

Necesitaré ejecutar su código en mi máquina ubuntu, así que incluya las instrucciones más detalladas posibles sobre cómo compilar y ejecutar su código.

Tabla de clasificación

  • n = 13 por Christian Sievers en Haskell (42 segundos)
Anush
fuente
¿Cuánta memoria se nos permite consumir?
Black Owl Kai
@BlackOwlKai Mi máquina tiene 8GB, ¿así que supongo que 6GB es seguro?
Anush
Creo que el último número en los ejemplos debería ser 472 en lugar de 427.
Christian Sievers
@ChristianSievers Gracias. Corregido ahora.
Anush
¿Qué es s? ¿Que representa?
Gigaflop

Respuestas:

5

Haskell

import Control.Monad (replicateM)
import Data.List (tails)
import qualified Data.IntSet as S
import qualified Data.Map.Strict as M
import qualified Data.Vector.Unboxed as V
import Data.Vector.Unboxed.Mutable (write)
import System.Environment (getArgs)
import Control.Parallel.Strategies

orig:: Int -> Int -> M.Map S.IntSet (Maybe Int)
orig n s = M.fromListWith (\ _ _ -> Nothing) 
               [(sums l, Just $! head l) | 
                   l <- replicateM n [s, s+1],
                   l <= reverse l ]

sums :: [Int] -> S.IntSet
sums l = S.fromList [ hi-lo | (lo:r) <- tails $ scanl (+) 0 l, hi <- r ]

construct :: Int -> Int -> S.IntSet -> [Int]
construct n start set =
   setmax `seq` setmin `seq` setv `seq`
   [ weight r | r <- map (start:) $ constr (del start setlist)
                                           (V.singleton start)
                                           (n-1)
                                           (setmax - start),
                r <= reverse r ]
  where
    setlist = S.toList set
    setmin = S.findMin set
    setmax = S.findMax set
    setv = V.modify (\v -> mapM_ (\p -> write v p True) setlist)
                    (V.replicate (1+setmax) False)

    constr :: [Int] -> V.Vector Int -> Int -> Int -> [[Int]]
    constr m _ 0 _ | null m    = [[]]
                   | otherwise = []
    constr m a i x =
         [ v:r | v <- takeWhile (x-(i-1)*setmin >=) setlist,
                 V.all (V.unsafeIndex setv . (v+)) a,
                 let new = V.cons v $ V.map (v+) a,
                 r <- (constr (m \\\ new) $! new) (i-1) $! (x-v) ]

del x [] = []
del x yl@(y:ys) = if x==y then ys else if y<x then y : del x ys else yl

(\\\) = V.foldl (flip del)

weight l = if l==reverse l then 1 else 2

count n s = sum ( map value [ x | x@(_, Just _) <- M.toList $ orig n s]
                      `using` parBuffer 128 rseq )
  where 
    value (sms, Just st) = uniqueval $ construct n st sms
    uniqueval [w] = w
    uniqueval _   = 0


main = do
  [ n ] <- getArgs
  mapM_ print ( map (count (read n)) [1..9]
                    `using` parBuffer 2 r0 )

La origfunción crea todas las listas de longitud ncon entradas so s+1, las mantiene si vienen antes de su reversa, calcula su sublista sumsy las coloca en un mapa que también recuerda el primer elemento de la lista. Cuando se encuentra el mismo conjunto de sumas más de una vez, se reemplaza el primer elemento Nothing, por lo que sabemos que no tenemos que buscar otras formas de obtener estas sumas.

La constructfunción busca listas de longitud dada y valor inicial dado que tienen un conjunto dado de sumas de sublista. Su parte recursiva constrsigue esencialmente la misma lógica que esta , pero tiene un argumento adicional que proporciona la suma que deben tener las entradas de lista restantes. Esto permite detenerse temprano cuando incluso los valores más pequeños posibles son demasiado grandes para obtener esta suma, lo que dio una mejora masiva en el rendimiento. Se obtuvieron grandes mejoras adicionales moviendo esta prueba a un lugar anterior (versión 2) y reemplazando la lista de sumas actuales por una Vector(versión 3 (rota) y 4 (con rigor adicional)). La última versión realiza la prueba de membresía establecida con una tabla de búsqueda y agrega más rigor y paralelización.

Cuando constructha encontrado una lista que da las sumas de la sublista y es más pequeña que su reverso, podría devolverla, pero no estamos realmente interesados ​​en ella. Casi bastaría con volver ()para indicar su existencia, pero necesitamos saber si tenemos que contarlo dos veces (porque no es un palíndromo y nunca manejaremos su reversa). Entonces ponemos 1 o 2 como su weighten la lista de resultados.

La función countpone estas partes juntas. Para cada conjunto de sumas sublista (procedente de orig) que era único entre las listas que contienen solamente sy s+1, llama value, que llama constructy, a través de uniqueval, comprueba si sólo hay un resultado. Si es así, ese es el peso que tenemos que contar, de lo contrario, el conjunto de sumas no era único y se devuelve cero. Tenga en cuenta que debido a la pereza, constructse detendrá cuando haya encontrado dos resultados.

La mainfunción maneja IO y el ciclo de s1 a 9.

Compilar y ejecutar

En debian esto necesita los paquetes ghc, libghc-vector-devy libghc-parallel-dev. Guarde el programa en un archivo prog.hsy compílelo ghc -threaded -feager-blackholing -O2 -o prog prog.hs. Ejecute con ./prog <n> +RTS -Ndónde <n>está la longitud de la matriz para la que queremos contar las matrices únicas.

Christian Sievers
fuente
Este código es bastante sorprendente (¡y corto!). Si pudiera agregar alguna explicación, estoy seguro de que a la gente le encantaría entender lo que ha hecho.
Anush
Tu nueva versión no se compila para mí. Obtengo bpaste.net/show/c96c4cbdc02e
Anush
Lo sentimos, borrar y pegar código más grande es tan incómodo que a veces solo cambio las pocas líneas a mano. Por supuesto, cometí un error ... Reparado ahora (espero), y agregué otra mejora, esta vez solo por un pequeño porcentaje. Los otros cambios fueron mucho más importantes.
Christian Sievers