Hitung array yang benar-benar unik

9

Ini adalah tindak lanjut ke Count array yang membuat set unik . Perbedaan yang signifikan adalah definisi keunikan.

Pertimbangkan Apanjang array n. Array hanya berisi bilangan bulat positif. Sebagai contoh A = (1,1,2,2). Mari kita definisikan f(A)sebagai himpunan jumlah semua sub-susunan berdekatan yang tidak kosong A. Dalam hal ini f(A) = {1,2,3,4,5,6}. Langkah-langkah untuk menghasilkan f(A) adalah sebagai berikut:

Subarrays dari Aadalah (1), (1), (2), (2), (1,1), (1,2), (2,2), (1,1,2), (1,2,2), (1,1,2,2). Jumlah masing-masing adalah 1,1,2,2,2,3,4,4,5,6. Karenanya, set yang Anda dapatkan dari daftar ini {1,2,3,4,5,6}.

Kami memanggil array A unik jika tidak ada array lain Bdengan panjang yang sama sehingga f(A) = f(B), kecuali untuk array Aterbalik. Sebagai contoh, f((1,2,3)) = f((3,2,1)) = {1,2,3,5,6}tetapi tidak ada array panjang 3yang menghasilkan jumlah penjumlahan yang sama.

Tugas

Tugas, untuk yang diberikan ndan suntuk menghitung jumlah array unik dengan panjang itu. Anda dapat berasumsi bahwa itu sadalah antara 1dan 9. Anda hanya perlu menghitung array di mana elemen-elemennya adalah bilangan bulat yang diberikan satau s+1. Misal jika s=1array yang Anda hitung hanya berisi 1dan 2. Namun, definisi keunikan adalah sehubungan dengan array lain dengan panjang yang sama. Sebagai contoh konkret [1, 2, 2, 2]adalah tidak unik karena memberikan set yang sama jumlah sebagai [1, 1, 2, 3].

Anda harus menghitung kebalikan dari array serta array itu sendiri (selama array bukan palindrome tentu saja).

Contohnya

s = 1, jawaban untuk n = 2,3,4,5,6,7,8,9 adalah:

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

Sebab s = 1, susunan unik panjang 4 adalah

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

s = 2, jawaban untuk n = 2,3,4,5,6,7,8,9 adalah:

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

Contoh array yang tidak unik s = 2adalah:

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

Ini memiliki jumlah penjumlahan yang sama dengan: (3, 2, 2, 2, 4, 3)dan (3, 2, 2, 4, 2, 3).

s = 8, jawaban untuk n = 2,3,4,5,6,7,8,9 adalah:

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

Skor

Untuk yang diberikan n, kode Anda harus menampilkan jawaban untuk semua nilai smulai dari 1hingga 9. Skor Anda adalah nilai tertinggi nyang menyelesaikannya dalam satu menit.

Pengujian

Saya perlu menjalankan kode Anda di mesin ubuntu saya, jadi harap sertakan instruksi sedetail mungkin untuk cara mengkompilasi dan menjalankan kode Anda.

Papan peringkat

  • n = 13 oleh Christian Sievers di Haskell (42 detik)
Anush
sumber
Berapa banyak memori yang boleh kita konsumsi?
Black Owl Kai
@ BlackOwlKai Mesin saya memiliki 8GB jadi saya kira 6GB aman?
Anush
Saya pikir angka terakhir dalam contoh harus 472 bukan 427.
Christian Sievers
@ChristianSievers Terima kasih. Diperbaiki sekarang
Anush
Apa s? Apa yang diwakilinya?
Gigaflop

Jawaban:

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 )

The origFungsi menciptakan semua daftar panjang ndengan entri satau s+1, membuat mereka jika mereka datang sebelum terbalik mereka, menghitung sublist mereka sumsdan menempatkan mereka di peta yang juga ingat elemen pertama dari daftar. Ketika set jumlah yang sama ditemukan lebih dari sekali, elemen pertama diganti Nothing, jadi kita tahu kita tidak perlu mencari cara lain untuk mendapatkan jumlah ini.

The constructpencarian fungsi untuk daftar yang diberikan panjang dan diberi nilai awal yang memiliki himpunan jumlah sublist. Bagian rekursifnya constrpada dasarnya mengikuti logika yang sama dengan ini , tetapi memiliki argumen tambahan yang memberikan jumlah yang harus dimiliki oleh entri daftar. Ini memungkinkan untuk berhenti lebih awal ketika nilai sekecil mungkin terlalu besar untuk mendapatkan jumlah ini, yang memberikan peningkatan kinerja besar-besaran. Peningkatan besar lebih lanjut diperoleh dengan memindahkan tes ini ke tempat sebelumnya (versi 2) dan dengan mengganti daftar jumlah saat ini dengan a Vector(versi 3 (rusak) dan 4 (dengan keketatan tambahan)). Versi terbaru melakukan tes keanggotaan dengan tabel pencarian dan menambahkan beberapa kekakuan dan paralelisasi.

Ketika constructtelah menemukan daftar yang memberikan jumlah sublist dan lebih kecil dari kebalikannya, ia dapat mengembalikannya, tetapi kami tidak benar-benar tertarik padanya. Hampir akan cukup untuk hanya kembali ()untuk menunjukkan keberadaannya, tetapi kita perlu tahu apakah kita harus menghitungnya dua kali (karena itu bukan palindrom dan kita tidak akan pernah menangani kebalikannya). Jadi kami menempatkan 1 atau 2 sebagai weightdaftar hasil.

Fungsi countmenyatukan bagian-bagian ini. Untuk setiap set jumlah sublist (berasal dari orig) yang unik di antara daftar yang hanya berisi sdan s+1, ia memanggil value, yang memanggil constructdan, melalui uniqueval, memeriksa apakah hanya ada satu hasil. Jika demikian, itu adalah berat yang harus kita hitung, jika tidak jumlah himpunan tidak unik dan nol dikembalikan. Perhatikan bahwa karena malas, constructakan berhenti ketika sudah menemukan dua hasil.

The mainFungsi menangani IO dan loop dari sdari 1 sampai 9.

Kompilasi dan Lari

Pada debian ini membutuhkan paket ghc, libghc-vector-devdan libghc-parallel-dev. Simpan program dalam file prog.hsdan kompilasi dengan ghc -threaded -feager-blackholing -O2 -o prog prog.hs. Jalankan dengan di ./prog <n> +RTS -Nmana <n>panjang array yang ingin kita hitung array unik.

Sievers Kristen
sumber
Kode ini sangat menakjubkan (dan singkat!). Jika Anda dapat menambahkan beberapa penjelasan, saya yakin orang-orang akan senang memahami apa yang telah Anda lakukan.
Anush
Versi baru Anda tidak dikompilasi untuk saya. Saya mendapatkan bpaste.net/show/c96c4cbdc02e
Anush
Maaf, menghapus dan menempelkan kode yang lebih besar sangat tidak nyaman sehingga saya terkadang hanya mengganti beberapa baris dengan tangan. Tentu saja saya membuat kesalahan ... Tetap sekarang (saya harap), dan menambahkan peningkatan lain, kali ini hanya untuk beberapa persen. Perubahan lainnya jauh lebih penting.
Christian Sievers