【问题标题】:Efficiently find indices of maxima of a list有效地找到列表的最大值索引
【发布时间】:2012-03-05 10:37:26
【问题描述】:

编辑:我的措辞肯定不够清楚,但我正在寻找一个类似下面的函数,但不完全是。

给定一个列表,我希望能够找到列表中最大元素的索引
(所以,list !! (indexOfMaximum list) == maximum list
我写了一些看起来非常有效的代码,尽管我觉得我在某个地方重新发明了轮子。

indexOfMaximum :: (Ord n, Num n) => [n] -> Int
indexOfMaximum list =
   let indexOfMaximum' :: (Ord n, Num n) => [n] -> Int -> n -> Int -> Int
       indexOfMaximum' list' currIndex highestVal highestIndex
          | null list'                = highestIndex
          | (head list') > highestVal = 
               indexOfMaximum' (tail list') (1 + currIndex) (head list') currIndex
          | otherwise                 = 
               indexOfMaximum' (tail list') (1 + currIndex) highestVal highestIndex
   in indexOfMaximum' list 0 0 0

现在我想返回列表中最大 n 个数字的索引列表。

我唯一的解决方案是将前 n 个元素存储在一个列表中,并将 (head list') > highestVal 替换为 n-largest-so-far 列表中的比较。

感觉必须有比这样做更有效的方法,而且我也觉得我没有充分利用 Prelude 和 Data.List。有什么建议?

【问题讨论】:

    标签: list haskell functional-programming


    【解决方案1】:

    此方案将每个元素与其索引相关联,对列表进行排序,使最小的元素在前,将其反转,使最大的元素在前,取前 n 个元素,然后提取索引。

    maxn n xs = map snd . take n . reverse . sort $ zip xs [0..]
    

    【讨论】:

    • 我曾想过这样的事情,但该死的,现在你这么说听起来更有效率。谢谢。
    • 但是,这至少需要 O(N log N),其中 N 是列表的长度
    • 确实如此。我认为如果我们使用sortBy (flip compare),如果只需要O(1) 的顶级元素,排序函数的惰性就会开始使这个解决方案基本上是O(N)
    • 噢!我想知道如何最好地反转比较。当翻转论点时,我被挂断了反转结果。谢谢
    【解决方案2】:

    最短的方法找到最大元素的最后一个索引,

    maxIndex list = snd . maximum $ zip list [0 .. ]
    

    如果你想要第一个索引,

    maxIndex list = snd . maximumBy cmp $ zip list [0 .. ]
      where
        cmp (v,i) (w,j) = case compare v w of
                            EQ -> compare j i
                            ne -> ne
    

    缺点是maximummaximumBy 太懒了,所以它们可能会构建大的thunk。为避免这种情况,要么使用手动递归(就像您所做的那样,但可能需要一些严格性注释)或使用严格的左折叠具有严格的累加器类型,元组不适合这样做,因为@987654325 @ 仅计算为弱头范式,即此处最外层的构造函数,因此您在元组组件中构建 thunk。

    【讨论】:

      【解决方案3】:

      嗯,一个简单的方法是使用maximum 查找最大的元素,然后使用findIndices 查找它的每个出现。比如:

      largestIndices :: Ord a => [a] -> [Int]
      largestIndices ls = findIndices (== maximum ls) ls
      

      但是,这并不完美,因为maximum 是一个偏函数,如果给定一个空列表,将会非常糟糕。您可以通过添加 [] 案例轻松避免这种情况:

      largestIndices :: Ord a => [a] -> [Int]
      largestIndices [] = []
      largestIndices ls = findIndices (== maximum ls) ls
      

      这个答案的真正诀窍是我是如何想出来的。之前我什至不知道findIndices!不过,GHCi 有一个简洁的命令,叫做:browse

      Prelude> :browse Data.List
      

      这列出了Data.List 导出的每个函数。使用它,我只需先搜索maximum,然后再搜索index 以查看选项是什么。而且,就在findIndex 旁边,有findIndecies,非常完美。

      最后,除非您真正看到代码运行缓慢,否则我不会担心效率。 GHC 可以——而且确实——执行了一些非常激进的优化,因为该语言是纯粹的并且它可以侥幸逃脱。因此,您唯一需要担心性能的情况是 - 在使用 -O2 编译之后 - 您发现这是一个问题。

      编辑:如果您想找到 n 顶部元素的索引,这是一个简单的想法:按降序对列表进行排序,获取第一个 n 唯一元素,使用 elemIndices 获取它们的索引并获取第一个n 索引。我希望这是比较清楚的。

      这是我的想法的快速版本:

      nLargestInices n ls = take n $ concatMap (`elemIndices` ls) nums
        where nums = take n . reverse . nub $ sort ls
      

      【讨论】:

      • 哦,:browsefindIndices 的发现很好!不过,我认为您误解了一点:我想找到 n 个最大元素的索引,可能不相等。既然你已经指出了findIndices,我正在尝试用elemIndices 烘焙一些东西,但我仍然遇到了麻烦。
      • 哦。我以为你想找到最大元素的所有索引。我的错。
      • 绝对有用,仍然 +1。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-09
      • 1970-01-01
      • 2019-10-27
      • 1970-01-01
      • 2022-01-15
      • 2017-05-28
      • 2012-07-16
      相关资源
      最近更新 更多