我写了一个排序数组的算法。
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
我想问你对性能有何看法?我用过循环,我想知道你是否认为还有另一种比这更好的方法。在达到这个目标之前,我正在对我的数组进行排序,保留原始索引,排名,然后才将每个等级调整到其原始位置,这在性能方面是非常糟糕的。现在我得到了这个改进版本,并正在寻找一些反馈。有什么想法吗?
编辑:重复的元素应具有相同的排名。 ;)
非常感谢您提前。 :)
答案 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
似乎什么都不做。除非你用优化器做某种黑魔法,否则你可以删除它。)