计算真正独特的数组


9

这是对创建唯一集合的Count数组的跟踪。最大的区别是唯一性的定义。

考虑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 唯一数组,除了反向数组。作为示例,但是没有其他长度数组会产生相同的和集。Bf(A) = f(B)Af((1,2,3)) = f((3,2,1)) = {1,2,3,5,6}3

任务

任务,对于给定ns是计算该长度的独特阵列的数量。您可以假设s介于1和之间9。您只需要对元素为给定整数s或的数组进行计数s+1。例如,如果s=1您要计数的数组仅包含12。但是,唯一性的定义是针对相同长度的任何其他数组。举一个具体的例子[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的代码,您的代码应输出s1to的所有值的答案9。您的分数是n在一分钟内完成的最高分。

测试中

我将需要在ubuntu机器上运行您的代码,因此请尽可能详细地说明如何编译和运行您的代码。

排行榜

  • Haskell中,Christian Sievers的n = 13(42秒)

我们允许消耗多少内存?
黑猫头鹰Kai

@BlackOwlKai我的机器有8GB,所以我猜6GB安全吗?
Anush

我认为,在示例中的最后一个数字应该是472而不是427
基督教西弗斯

@ChristianSievers谢谢。立即修复。
Anush

什么s啊 它代表什么?
Gigaflop

Answers:


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函数处理IO和s1到9 的循环。

编译并运行

在debian上,这需要软件包ghclibghc-vector-dev以及libghc-parallel-dev。将程序保存在文件中,prog.hs然后使用进行编译ghc -threaded -feager-blackholing -O2 -o prog prog.hs。使用./prog <n> +RTS -Nwhere <n>是要计算唯一数组的数组长度。


这段代码非常棒(而且很短!)。如果您可以添加一些解释,我相信人们会喜欢理解您的所作所为。
Anush

您的新版本无法为我编译。我得到bpaste.net/show/c96c4cbdc02e
Anush

抱歉,删除和粘贴较大的代码非常不舒服,以至于我有时只需要手动更改几行。当然,我犯了一个错误。其他更改更为重要。
Christian Sievers
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.