我想知道是否有人知道微软(或其他库)用于计算F#中数组/列表/整数序列的中位数。我看到一个平均功能,但没有中位数。
提前致谢,
JP
答案 0 :(得分:8)
捡起@Brian和@BrokenGlass离开的地方......
let inline median input =
let sorted = input |> Seq.toArray |> Array.sort
let m1,m2 =
let len = sorted.Length-1 |> float
len/2. |> floor |> int, len/2. |> ceil |> int
(sorted.[m1] + sorted.[m2] |> float)/2.
//by marking the function inline, we gain some extra flexibility with static member constraints
//val inline median :
// seq< ^a> -> float
// when ^a : comparison and ^a : (static member ( + ) : ^a * ^a -> ^b) and
// ^b : (static member op_Explicit : ^b -> float)
(有点让我渴望数字类型之间的隐式转换)
Here's一个描述平均O(n)算法和C#实现的链接。
答案 1 :(得分:3)
怎么样
let a = input |> Seq.toArray |> Array.sort
a.[a.Length / 2]
? (在浏览器中编码,任何错误都是我的。)
答案 2 :(得分:3)
如果您绝对需要库,可以查看this question,或者在.NET上查找有关统计库的其他问题。
以下是quickselect的实现。它预计时间 O(n)和最坏情况时间 O(n 2 )。对该类型的唯一限制是它们具有可比性。
/// Calculate the median of a list of items.
/// The result is a tuple of two items whose mean is the median.
let median xs =
/// Partition list into three piles; less-than, equal and greater-than
/// x: Current pivot
/// xs: Sublist to partition
/// cont: Continuation function
let rec partition x xs cont =
match xs with
| [] ->
// place pivot in equal pile
cont [] 0 [x] 1 [] 0
| y::ys ->
if y < x then
// place item in less-than pile
partition x ys (fun lts n1 eqs n2 gts n3 ->
cont (y::lts) (n1+1) eqs n2 gts n3)
elif y = x then
// place pivot in equal pile, and use item as new pivot,
// so that the order is preserved
partition y ys (fun lts n1 eqs n2 gts n3 ->
cont lts n1 (x::eqs) (n2+1) gts n3)
else // y > x
// place item in greater-than pile
partition x ys (fun lts n1 eqs n2 gts n3 ->
cont lts n1 eqs n2 (y::gts) (n3+1))
/// Partition input and recurse into the part than contains the median
/// before: Number of elements before this sublist.
/// xs: Current sublist.
/// after: Number of elements after this sublist.
let rec loop before xs after =
match xs with
| [] -> failwith "Median of empty list"
| x::xs ->
partition x xs (fun lts numlt eqs numeq gts numgt ->
if before + numlt > numeq + numgt + after then
// Recurse into less pile
loop before lts (after + numeq + numgt)
elif before + numlt = numeq + numgt + after then
// Median is split between less and equal pile
(List.max lts, x)
elif before + numlt + numeq > numgt + after then
// Median is completely inside equal pile
(x, x)
elif before + numlt + numeq = numgt + after then
// Median is split between equal and greater pile
(x, List.min gts)
else
// Recurse into greater pile
loop (before + numlt + numeq) gts after)
loop 0 xs 0
我使用continuation来使其尾递归。我尝试以这样一种方式编写调用,它类似于一个简单的递归调用;而不是let x, y = f a b; body
我使用了f a b (fun x y -> body)
。可以使用CPS monad简化一点。
示例:
> median [1];;
val it : int * int = (1, 1)
> median [1;2];;
val it : int * int = (1, 2)
> median [1..9];;
val it : int * int = (5, 5)