Порахуйте масиви, які справді унікальні


9

Це подальше підрахунок масивів, які створюють унікальні набори . Суттєвою відмінністю є визначення унікальності.

Розглянемо масив Aдовжини n. Масив містить лише додатні цілі числа. Наприклад A = (1,1,2,2). Давайте визначимо f(A)як сукупність сум усіх непустих суміжних підмагістралей A. У цьому випадку f(A) = {1,2,3,4,5,6}. Етапи для створення f(A) наступні:

Підматриці Aє (1), (1), (2), (2), (1,1), (1,2), (2,2), (1,1,2), (1,2,2), (1,1,2,2). Їх відповідні суми є 1,1,2,2,2,3,4,4,5,6. Отже, набір, який ви отримуєте з цього списку, є {1,2,3,4,5,6}.

Ми називаємо масив A унікальним, якщо немає іншого масиву Bтакої ж довжини, який f(A) = f(B), за винятком масиву, Aоберненого назад. Як приклад, f((1,2,3)) = f((3,2,1)) = {1,2,3,5,6}але не існує іншого масиву довжини, 3який б створював той самий набір сум.

Завдання

Завдання, для даного nі sє підрахунок кількості унікальних масивів цієї довжини. Ви можете припустити, що sце між 1і 9. Вам потрібно лише порахувати масиви, де елементи є або заданим цілим числом, sабо s+1. Наприклад, якщо s=1масиви, які ви рахуєте, містять лише 1та 2. Однак визначення унікальності є стосовно будь-якого іншого масиву такої ж довжини. Оскільки конкретний приклад не[1, 2, 2, 2] є унікальним, оскільки він дає той самий набір сум, що і .[1, 1, 2, 3]

Ви повинні рахувати зворотній бік масиву, а також сам масив (до тих пір, поки масив звичайно не є паліндром).

Приклади

s = 1, відповіді на n = 2,3,4,5,6,7,8,9 такі:

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

Бо s = 1унікальні масиви довжиною 4 є

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

s = 2, відповіді на n = 2,3,4,5,6,7,8,9 такі:

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

Приклад масиву, який не є унікальним, s = 2є:

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

Він має той самий набір сум, що і обидва: (3, 2, 2, 2, 4, 3)і (3, 2, 2, 4, 2, 3).

s = 8, відповіді на n = 2,3,4,5,6,7,8,9 такі:

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

Оцінка

Для даного nкоду слід вивести відповідь на всі значення від sз 1до 9. Ваш бал - це найвище значення, nза яке це завершується за одну хвилину.

Тестування

Мені потрібно запустити ваш код на моїй машині ubuntu, тому, будь ласка, включіть якомога детальніші інструкції щодо того, як компілювати та запускати ваш код.

Таблиця лідерів

  • n = 13 Крістіан Сіверс в Хаскеллі (42 секунди)

Скільки пам’яті нам дозволено споживати?
Чорна сова Кай

@BlackOwlKai Моя машина має 8 Гб, тож я думаю, 6 Гб безпечний?
Ануш

Я думаю, що останнє число в прикладах має бути 472 замість 427.
Крістіан Сіверс

@ChristianSievers Дякую Виправлено зараз.
Ануш

Що таке s? Що він представляє?
Гігафлоп

Відповіді:


5

Хаскелл

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 )

origФункція створює всі списки довжини nіз записами sабо s+1, тримає їх , якщо вони приходять , перш ніж їх реверс, обчислює їх подсписок sumsі поміщає ті в карті , яка також запам'ятовує перший елемент списку. Коли один і той самий набір сум зустрічається не один раз, перший елемент замінюється Nothing, тому ми знаємо, що нам не доведеться шукати інших способів отримання цих сум.

У constructфункції шукає списки заданою довжиною і дано початкове значення , які мають заданий набір подспісок сум. Його рекурсивна частина, constrпо суті, відповідає тій же логіці, що і ця , але має додатковий аргумент, що дає суму, яку повинні мати інші записи в списку. Це дозволяє зупинитись на ранніх термінах, коли навіть найменші можливі значення занадто великі, щоб отримати цю суму, що дало значне підвищення продуктивності. Подальші вдосконалення були отримані шляхом переміщення цього тесту на попереднє місце (версія 2) та заміни списку поточних сум на Vector(версія 3 (зламана) та 4 (з додатковою суворістю)). Остання версія робить встановлену перевірку членства за допомогою таблиці пошуку та додає ще трохи строгості та паралельності.

Коли constructзнайшов список, який дає сублістину підсумків і менший від його зворотного, він може повернути його, але нас це не дуже цікавить. Майже достатньо було б просто повернутися, ()щоб вказати на його існування, але нам потрібно знати, чи доведеться ми рахувати його двічі (адже це не паліндром, і ми ніколи не будемо обробляти його зворотний бік). Отже, ми ставимо 1 або 2 як його weightу список результатів.

Функція countз'єднує ці частини. Для кожного набору підсумкових підсумків (що надходять від orig), який був унікальним серед списків, що містять лише, sі s+1він викликає value, який викликає constructі через uniqueval, перевіряє, чи є лише один результат. Якщо так, то ми повинні порахувати вагу, інакше набір сум не був унікальним і нуль повертається. Зауважте, що через ледачість constructзупиниться, коли знайде два результати.

У mainфункції ручки введення - виведення і петля sвід 1 до 9.

Складання та виконання

В Debian це потребує пакетах ghc, libghc-vector-devі libghc-parallel-dev. Збережіть програму у файлі prog.hsта компілюйте її ghc -threaded -feager-blackholing -O2 -o prog prog.hs. Запустіть, ./prog <n> +RTS -Nде <n>довжина масиву, за яку ми хочемо порахувати унікальні масиви.


Цей код досить дивовижний (і короткий!). Якщо ви можете додати трохи пояснень, я впевнений, що люди хотіли б зрозуміти, що ви зробили.
Ануш

Ваша нова версія не складається для мене. Я отримую bpaste.net/show/c96c4cbdc02e
Ануш

Вибачте, видалення та вставлення більшого коду настільки незручно, що іноді просто змінюю кілька рядків вручну. Звичайно, я помилився ... Виправлено зараз (сподіваюся), і додав ще одне поліпшення, цього разу лише на кілька відсотків. Інші зміни були набагато важливішими.
Крістіан Сіверс
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.