F#中的排名函数

时间:2017-06-28 14:54:26

标签: arrays f# ranking

我写了一个排序数组的算法。

let rankFun array =
    let arrayNew = List.toArray array
    let arrayLength = Array.length arrayNew
    let rankedArray = Array.create arrayLength 1.0
    for i in 0 .. arrayLength - 2 do
        for j in (i+1) .. arrayLength - 1 do
            if arrayNew.[i] > arrayNew.[j] then
                rankedArray.[i] <- rankedArray.[i] + 1.0

            elif arrayNew.[i] < arrayNew.[j] then
                rankedArray.[j] <- rankedArray.[j] + 1.0

            else 
                rankedArray.[i] <- rankedArray.[i] + 0.0
    rankedArray

我想问你对性能有何看法?我用过循环,我想知道你是否认为还有另一种比这更好的方法。在达到这个目标之前,我正在对我的数组进行排序,保留原始索引,排名,然后才将每个等级调整到其原始位置,这在性能方面是非常糟糕的。现在我得到了这个改进版本,并正在寻找一些反馈。有什么想法吗?

编辑:重复的元素应具有相同的排名。 ;)

非常感谢您提前。 :)

1 个答案:

答案 0 :(得分:4)

我假设可以从排序输入中获取排名,因为您评论了问题在重复项上的行为是一个错误。令人惊讶的是,您所描述的排序解决方案比问题中显示的代码慢。它应该快得多。

通过排序解决此问题的一种简单方法是从所有值构建有序集。 F#中的集合总是有序且不包含重复项,因此它们可用于创建排名。

从集合中,创建一个映射,从每个值到集合中的索引(加一,以保持以1开头的排名)。这样,您可以查找每个值的等级以填充输出数组。

let getRankings array =
    let rankTable =
        Set.ofArray array
        |> Seq.mapi (fun i n -> n, i + 1)
        |> Map.ofSeq
    array |> Array.map (fun n -> rankTable.[n])

这需要一个数组,而不是一个列表,因为问题中的输入参数被称为array。它还使用整数作为排名,因为这是用于此目的的常规数据类型。

这比原始算法快得多,因为所有操作最多为O(n * log(n)),而问题中的嵌套for循环为O(n ^ 2)。 (另请参阅:Wikipedia on Big O notation。)对于仅10000个元素,基于排序的解决方案在我的计算机上运行速度已经快了100倍。

(顺便说一句,声明else rankedArray.[i] <- rankedArray.[i] + 0.0似乎什么都不做。除非你用优化器做某种黑魔法,否则你可以删除它。)