Contare le matrici che sono davvero uniche


9

Questo è il seguito degli array Count che creano set unici . La differenza significativa è la definizione di unicità.

Considera una matrice Adi lunghezza n. La matrice contiene solo numeri interi positivi. Per esempio A = (1,1,2,2). Definiamo f(A)come l'insieme delle somme di tutti i sottocampi contigui non vuoti di A. In questo caso f(A) = {1,2,3,4,5,6}. I passaggi per produrre f(A) sono i seguenti:

I subarrays di Asono (1), (1), (2), (2), (1,1), (1,2), (2,2), (1,1,2), (1,2,2), (1,1,2,2). Le loro rispettive somme sono 1,1,2,2,2,3,4,4,5,6. Il set che ottieni da questo elenco è quindi {1,2,3,4,5,6}.

Chiamiamo un array A univoco se non esiste un altro array Bdella stessa lunghezza tale che f(A) = f(B), ad eccezione dell'array Ainvertito. Ad esempio, f((1,2,3)) = f((3,2,1)) = {1,2,3,5,6}ma non esiste un altro array di lunghezza 3che produce lo stesso insieme di somme.

Compito

Il compito, per un dato ned sè quello di contare il numero di matrici uniche di quella lunghezza. Puoi presumere che ssia tra 1e 9. Devi solo contare le matrici in cui gli elementi sono un dato intero so s+1. Ad esempio se s=1gli array che stai contando contengono solo 1e 2. Tuttavia, la definizione di unicità è rispetto a qualsiasi altra matrice della stessa lunghezza. Un esempio concreto non[1, 2, 2, 2] è unico in quanto fornisce lo stesso insieme di somme di .[1, 1, 2, 3]

Dovresti contare il retro di un array e l'array stesso (purché l'array non sia un palindromo ovviamente).

Esempi

s = 1, le risposte per n = 2,3,4,5,6,7,8,9 sono:

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

Per s = 1, le matrici uniche di lunghezza 4 sono

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

s = 2, le risposte per n = 2,3,4,5,6,7,8,9 sono:

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

Un esempio di un array con cui non è univoco s = 2è:

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

Questo ha lo stesso insieme di somme di entrambi: (3, 2, 2, 2, 4, 3)e (3, 2, 2, 4, 2, 3).

s = 8, le risposte per n = 2,3,4,5,6,7,8,9 sono:

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

Punto

Per un dato motivo n, il tuo codice dovrebbe generare la risposta per tutti i valori di sda 1a 9. Il tuo punteggio è il valore più alto nper il quale questo viene completato in un minuto.

analisi

Dovrò eseguire il tuo codice sulla mia macchina Ubuntu, quindi per favore includi le istruzioni più dettagliate possibili su come compilare ed eseguire il tuo codice.

Classifica

  • n = 13 di Christian Sievers in Haskell (42 secondi)

Quanta memoria ci è permesso consumare?
Black Owl Kai,

@BlackOwlKai La mia macchina ha 8 GB, quindi immagino che 6 GB siano al sicuro?
Anush,

Penso che l'ultimo numero negli esempi dovrebbe essere 472 anziché 427.
Christian Sievers,

@ChristianSievers Grazie. Riparato ora.
Anush,

Che cosa è s? Che cosa rappresenta?
Gigaflop,

Risposte:


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 origfunzione crea tutti gli elenchi di lunghezza ncon voci so s+1, li mantiene se arrivano prima del loro contrario, calcola il loro elenco secondario sumse li inserisce in una mappa che ricorda anche il primo elemento dell'elenco. Quando la stessa serie di somme viene trovata più di una volta, il primo elemento viene sostituito con Nothing, quindi sappiamo che non dobbiamo cercare altri modi per ottenere queste somme.

La constructfunzione cerca gli elenchi di una determinata lunghezza e un dato valore iniziale che hanno un determinato insieme di somme dell'elenco secondario. La sua parte ricorsiva constrsegue essenzialmente la stessa logica di questa , ma ha un argomento aggiuntivo che fornisce la somma che le voci rimanenti dell'elenco devono avere. Ciò consente di interrompere presto quando anche i valori più piccoli possibili sono troppo grandi per ottenere questa somma, il che ha dato un notevole miglioramento delle prestazioni. Ulteriori grandi miglioramenti sono stati ottenuti spostando questo test in una posizione precedente (versione 2) e sostituendo l'elenco delle somme correnti con una Vector(versione 3 (interrotta) e 4 (con rigore aggiuntivo)). L'ultima versione esegue il test dell'appartenenza impostato con una tabella di ricerca e aggiunge un po 'più di rigore e parallelismo.

Quando constructha trovato un elenco che fornisce somme dell'elenco secondario ed è più piccolo del suo contrario, potrebbe restituirlo, ma non ci interessa davvero. Basterebbe quasi per tornare ()ad indicare la sua esistenza, ma dobbiamo sapere se dobbiamo contarlo due volte (perché non è un palindromo e non ne faremo mai il contrario). Quindi inseriamo 1 o 2 weightnell'elenco dei risultati.

La funzione countmette insieme queste parti. Per ogni serie di somme di sublist (provenienti da orig) che era unica tra le liste che contengono solo se s+1, chiama value, che chiama constructe, tramite uniqueval, controlla se c'è un solo risultato. In tal caso, questo è il peso che dobbiamo contare, altrimenti il ​​set di somme non era unico e viene restituito zero. Si noti che a causa della pigrizia, constructsi fermerà quando ha trovato due risultati.

La mainfunzione gestisce IO e il loop sda 1 a 9.

Compilazione ed esecuzione

Su Debian questo ha bisogno dei pacchetti ghc, libghc-vector-deve libghc-parallel-dev. Salvare il programma in un file prog.hse compilarlo con ghc -threaded -feager-blackholing -O2 -o prog prog.hs. Esegui con ./prog <n> +RTS -Ndove <n>è la lunghezza dell'array per cui vogliamo contare gli array univoci.


Questo codice è piuttosto sorprendente (e breve!). Se potessi aggiungere qualche spiegazione, sono sicuro che alla gente piacerebbe capire cosa hai fatto.
Anush,

La tua nuova versione non si compila per me. Ottengo bpaste.net/show/c96c4cbdc02e
Anush

Siamo spiacenti, eliminare e incollare codice più grande è così scomodo che a volte cambio le poche righe a mano. Ovviamente ho fatto un errore ... Risolto ora (spero) e ho aggiunto un altro miglioramento, questa volta solo per qualche percento. Le altre modifiche erano molto più importanti.
Christian Sievers,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.