Zählen Sie Arrays, die wirklich einzigartig sind

9

Dies ist eine Fortsetzung von Count-Arrays, die eindeutige Sätze erstellen . Der wesentliche Unterschied ist die Definition der Einzigartigkeit.

Betrachten Sie ein Array Avon Länge n. Das Array enthält nur positive Ganzzahlen. Zum Beispiel A = (1,1,2,2). Definieren wir f(A)als die Menge der Summen aller nicht leeren zusammenhängenden Subarrays von A. In diesem Fall f(A) = {1,2,3,4,5,6}. Die zu produzierenden Schritte f(A) sind wie folgt:

Die Subarrays von Asind (1), (1), (2), (2), (1,1), (1,2), (2,2), (1,1,2), (1,2,2), (1,1,2,2). Ihre jeweiligen Beträge sind 1,1,2,2,2,3,4,4,5,6. Das Set, das Sie aus dieser Liste erhalten, ist daher {1,2,3,4,5,6}.

Wir nennen ein Array A eindeutig, wenn es kein anderes Array Bmit der gleichen Länge gibt f(A) = f(B), außer dem Aumgekehrten Array . Zum Beispiel, f((1,2,3)) = f((3,2,1)) = {1,2,3,5,6}aber es gibt kein anderes Längenarray 3, das den gleichen Satz von Summen erzeugt.

Aufgabe

Die Aufgabe für eine gegebene nund sist es, die Anzahl der eindeutigen Arrays dieser Länge zu zählen. Sie können davon ausgehen, dass szwischen 1und 9. Sie müssen nur Arrays zählen, bei denen die Elemente entweder eine bestimmte Ganzzahl soder eine bestimmte Ganzzahl sind s+1. ZB wenn s=1die Arrays, die Sie zählen, nur 1und enthalten 2. Die Definition der Eindeutigkeit bezieht sich jedoch auf jedes andere Array gleicher Länge. Ein konkretes Beispiel [1, 2, 2, 2]ist nicht eindeutig, da es die gleichen Summen wie ergibt [1, 1, 2, 3].

Sie sollten die Umkehrung eines Arrays sowie das Array selbst zählen (solange das Array natürlich kein Palindrom ist).

Beispiele

s = 1Die Antworten für n = 2,3,4,5,6,7,8,9 lauten:

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

Denn s = 1die eindeutigen Arrays der Länge 4 sind

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

s = 2Die Antworten für n = 2,3,4,5,6,7,8,9 lauten:

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

Ein Beispiel für ein Array, das nicht eindeutig s = 2ist, ist:

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

Dies hat die gleichen Summen wie: (3, 2, 2, 2, 4, 3)und (3, 2, 2, 4, 2, 3).

s = 8Die Antworten für n = 2,3,4,5,6,7,8,9 lauten:

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

Ergebnis

Für einen bestimmten nFall sollte Ihr Code die Antwort für alle Werte von svon 1bis ausgeben 9. Ihre Punktzahl ist der höchste Wert, nfür den dies in einer Minute abgeschlossen ist.

Testen

Ich muss Ihren Code auf meinem Ubuntu-Computer ausführen. Geben Sie daher so detaillierte Anweisungen wie möglich zum Kompilieren und Ausführen Ihres Codes an.

Bestenliste

  • n = 13 von Christian Sievers in Haskell (42 Sekunden)
Anush
quelle
Wie viel Speicher dürfen wir verbrauchen?
Black Owl Kai
@BlackOwlKai Mein Computer hat 8 GB, also sind 6 GB sicher?
Anush
Ich denke, die letzte Zahl in den Beispielen sollte 472 statt 427 sein.
Christian Sievers
@ChristianSievers Vielen Dank. Jetzt behoben.
Anush
Was ist s? Was stellt es dar?
Gigaflop

Antworten:

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 )

Die origFunktion erstellt alle Längenlisten nmit Einträgen soder s+1behält sie bei, wenn sie vor ihrer Umkehrung stehen, berechnet ihre Unterliste sumsund fügt diese in eine Karte ein, die sich auch an das erste Element der Liste erinnert. Wenn derselbe Satz von Summen mehrmals gefunden wird, wird das erste Element durch ersetzt Nothing, sodass wir wissen, dass wir nicht nach anderen Wegen suchen müssen, um diese Summen zu erhalten.

Die constructFunktion sucht nach Listen mit einer bestimmten Länge und einem bestimmten Startwert, die einen bestimmten Satz von Unterlistensummen enthalten. Sein rekursiver Teil constrfolgt im Wesentlichen der gleichen Logik wie dieser , hat jedoch ein zusätzliches Argument, das die Summe angibt, die die verbleibenden Listeneinträge benötigen. Dies ermöglicht es, frühzeitig zu stoppen, wenn selbst die kleinstmöglichen Werte zu groß sind, um diese Summe zu erhalten, was zu einer massiven Leistungsverbesserung führte. Weitere große Verbesserungen wurden erzielt, indem dieser Test an einen früheren Ort verschoben wurde (Version 2) und die Liste der aktuellen Summen durch eine Vector(Version 3 (defekt) und 4 (mit zusätzlicher Strenge)) ersetzt wurde. Die neueste Version führt den Set-Mitgliedschaftstest mit einer Nachschlagetabelle durch und fügt etwas mehr Strenge und Parallelisierung hinzu.

Wenn constructeine Liste gefunden wurde, die die Summen der Unterliste angibt und kleiner als die Rückseite ist, könnte sie zurückgegeben werden, aber wir sind nicht wirklich daran interessiert. Es würde fast ausreichen, nur zurückzukehren (), um seine Existenz anzuzeigen, aber wir müssen wissen, ob wir es zweimal zählen müssen (weil es kein Palindrom ist und wir niemals mit seiner Umkehrung umgehen werden). Also setzen wir 1 oder 2 als seine weightin die Ergebnisliste.

Die Funktion countfügt diese Teile zusammen. Für jeden Satz von sublist Summen (aus orig) , das war einzigartig unter den Listen , die nur sund s+1es nennt value, die Anrufe constructund über uniqueval, prüft , ob es nur ein Ergebnis. Wenn ja, ist dies das Gewicht, das wir zählen müssen, andernfalls war der Satz von Summen nicht eindeutig und Null wird zurückgegeben. Beachten Sie, dass aufgrund von Faulheit aufgehört constructwird, wenn zwei Ergebnisse gefunden wurden.

Die mainFunktion verarbeitet E / A und die Schleife svon 1 bis 9.

Kompilieren und Ausführen

Auf debian muss diese die Pakete ghc, libghc-vector-devund libghc-parallel-dev. Speichern Sie das Programm in einer Datei prog.hsund kompilieren Sie es mit ghc -threaded -feager-blackholing -O2 -o prog prog.hs. Führen Sie mit ./prog <n> +RTS -Nwo <n>ist die Array-Länge, für die wir die eindeutigen Arrays zählen möchten.

Christian Sievers
quelle
Dieser Code ist ziemlich erstaunlich (und kurz!). Wenn Sie eine Erklärung hinzufügen könnten, würden die Leute sicher gerne verstehen, was Sie getan haben.
Anush
Ihre neue Version wird für mich nicht kompiliert. Ich bekomme bpaste.net/show/c96c4cbdc02e
Anush
Das Löschen und Einfügen von größerem Code ist leider so unangenehm, dass ich manchmal nur die wenigen Zeilen von Hand ändere. Natürlich habe ich einen Fehler gemacht ... Jetzt behoben (ich hoffe) und eine weitere Verbesserung hinzugefügt, diesmal nur für ein paar Prozent. Die anderen Änderungen waren viel wichtiger.
Christian Sievers