Comptez les tableaux qui sont vraiment uniques

9

Il s'agit d'un suivi des tableaux de nombre qui créent des ensembles uniques . La différence significative est la définition de l'unicité.

Considérez un tableau Ade longueur n. Le tableau ne contient que des entiers positifs. Par exemple A = (1,1,2,2). Définissons f(A)comme l'ensemble des sommes de tous les sous-réseaux contigus non vides de A. Dans ce cas f(A) = {1,2,3,4,5,6}. Les étapes de production f(A) sont les suivantes:

Les sous-réseaux de Asont (1), (1), (2), (2), (1,1), (1,2), (2,2), (1,1,2), (1,2,2), (1,1,2,2). Leurs sommes respectives sont 1,1,2,2,2,3,4,4,5,6. L'ensemble que vous obtenez de cette liste est donc {1,2,3,4,5,6}.

Nous appelons un tableau A unique s'il n'y a pas d'autre tableau Bde la même longueur tel que f(A) = f(B), à l'exception du tableau Ainversé. Par exemple, f((1,2,3)) = f((3,2,1)) = {1,2,3,5,6}mais aucun autre tableau de longueur 3ne produit le même ensemble de sommes.

Tâche

La tâche, pour une donnée net sest de compter le nombre de tableaux uniques de cette longueur. Vous pouvez supposer que sc'est entre 1et 9. Il vous suffit de compter les tableaux dont les éléments sont soit un entier donné, ssoit s+1. Par exemple, si s=1les tableaux que vous comptez contiennent uniquement 1et 2. Cependant, la définition de l'unicité s'applique à tout autre tableau de même longueur. Un exemple concret [1, 2, 2, 2]n'est pas unique car il donne le même ensemble de sommes que [1, 1, 2, 3].

Vous devez compter l'inverse d'un tableau ainsi que le tableau lui-même (tant que le tableau n'est pas un palindrome bien sûr).

Exemples

s = 1, les réponses pour n = 2,3,4,5,6,7,8,9 sont:

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

Pour s = 1, les tableaux uniques de longueur 4 sont

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

s = 2, les réponses pour n = 2,3,4,5,6,7,8,9 sont:

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

Un exemple d'un tableau qui n'est pas unique avec s = 2est:

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

Cela a le même ensemble de sommes que: (3, 2, 2, 2, 4, 3)et (3, 2, 2, 4, 2, 3).

s = 8, les réponses pour n = 2,3,4,5,6,7,8,9 sont:

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

But

Pour une donnée n, votre code devrait afficher la réponse pour toutes les valeurs de sà 1à 9. Votre score est la valeur la plus élevée npour laquelle cela se termine en une minute.

Essai

Je devrai exécuter votre code sur ma machine Ubuntu, veuillez donc inclure des instructions aussi détaillées que possible sur la façon de compiler et d'exécuter votre code.

Classement

  • n = 13 par Christian Sievers à Haskell (42 secondes)
Anush
la source
Quelle quantité de mémoire peut-on consommer?
Black Owl Kai
@BlackOwlKai Ma machine a 8 Go, donc je suppose que 6 Go sont sûrs?
Anush
Je pense que le dernier nombre dans les exemples devrait être 472 au lieu de 427.
Christian Sievers
@ChristianSievers Merci. Fixé maintenant.
Anush
Qu'est-ce que c'est s? Qu'est-ce que cela représente?
Gigaflop

Réponses:

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 origfonction crée toutes les listes de longueur navec des entrées sou s+1, les conserve si elles viennent avant leur inverse, calcule leur sous sums- liste et les place dans une carte qui se souvient également du premier élément de la liste. Lorsque le même ensemble de sommes est trouvé plus d'une fois, le premier élément est remplacé par Nothing, nous savons donc que nous n'avons pas à chercher d'autres moyens pour obtenir ces sommes.

La constructfonction recherche des listes de longueur et de valeur de départ données qui ont un ensemble donné de sommes de sous-liste. Sa partie récursive constrsuit essentiellement la même logique que celle-ci , mais a un argument supplémentaire donnant la somme que les entrées de liste restantes doivent avoir. Cela permet d'arrêter tôt lorsque même les plus petites valeurs possibles sont trop grandes pour obtenir cette somme, ce qui a donné une amélioration massive des performances. D'autres améliorations importantes ont été obtenues en déplaçant ce test à un endroit antérieur (version 2) et en remplaçant la liste des sommes actuelles par un Vector(version 3 (cassée) et 4 (avec une rigueur supplémentaire)). La dernière version effectue le test d'appartenance avec une table de recherche et ajoute un peu plus de rigueur et de parallélisation.

Quand constructa trouvé une liste qui donne les sommes de la sous-liste et est plus petite que son inverse, il pourrait la renvoyer, mais cela ne nous intéresse pas vraiment. Il suffirait presque de revenir ()pour indiquer son existence, mais nous devons savoir si nous devons le compter deux fois (car ce n'est pas un palindrome et nous ne gérerons jamais son inverse). Nous avons donc mis 1 ou 2 comme son weightdans la liste des résultats.

La fonction countrassemble ces pièces. Pour chaque ensemble de sous- origlistes (provenant de ) qui était unique parmi les listes ne contenant que set s+1, il appelle value, qui appelle constructet, via uniqueval, vérifie s'il n'y a qu'un seul résultat. Si c'est le cas, c'est le poids que nous devons compter, sinon l'ensemble des sommes n'était pas unique et zéro est retourné. Notez qu'en raison de la paresse, constructs'arrêtera lorsqu'il aura trouvé deux résultats.

La mainfonction gère les E / S et la boucle de s1 à 9.

Compilation et exécution

Sur debian, cela nécessite les paquets ghc, libghc-vector-devet libghc-parallel-dev. Enregistrez le programme dans un fichier prog.hset compilez-le avec ghc -threaded -feager-blackholing -O2 -o prog prog.hs. Exécuter avec ./prog <n> +RTS -N<n>est la longueur de tableau pour laquelle nous voulons compter les tableaux uniques.

Christian Sievers
la source
Ce code est assez étonnant (et court!). Si vous pouviez ajouter quelques explications, je suis sûr que les gens aimeraient comprendre ce que vous avez fait.
Anush
Votre nouvelle version ne se compile pas pour moi. Je reçois bpaste.net/show/c96c4cbdc02e
Anush
Désolé, la suppression et le collage de code plus volumineux sont si inconfortables que je change parfois simplement les quelques lignes à la main. Bien sûr, j'ai fait une erreur ... Corrigé maintenant (j'espère), et ajouté une autre amélioration, cette fois seulement pour quelques pour cent. Les autres changements étaient beaucoup plus importants.
Christian Sievers