查找阵列中的本地分钟

时间:2010-10-06 01:53:35

标签: algorithm f#

是否有一种简单的方法可以确定值数组的局部最小值和最大值。例如

Element Value   Note
1         1 
2         3 
3         5 
4         6 
5         7       max
5         5 
6         4       min
7         6 
8         9 
9         10      max
10        8 
11        7 
12        5      min
13        10    

所以定义的数组如下:

let arr = [|1;3;5;6;7;5;4;6;9;10;8;7;5;10|]

会识别

mins  = [|4;5|]

maxs  = [|7;10|]

它可以是列表或序列以及数组。两个问题

  1. F#中是否有任何有助于此任务的设施
  2. 是否有一种常用的算法来确定分钟或最大值,还是两者都有?
  3. 如果从头开始写作是应该在功能上还是在命令式上进行?
  4. THX

6 个答案:

答案 0 :(得分:10)

这似乎是...... Seq.windowed的工作! < cue superhero music>

let arr = [|1;3;5;6;7;5;4;6;9;10;8;7;5;10|] 

let _,mins,maxs = 
    arr |> Seq.windowed 3 |> Seq.fold (fun (i,mins,maxs) [|a;b;c|] -> 
    if a>b&&b<c then   (i+1, i::mins,    maxs)
    elif a<b&&b>c then (i+1,    mins, i::maxs)
    else               (i+1,    mins,    maxs)) (1,[],[])

arr |> Seq.iteri (fun i x -> printfn "%2d: %2d" i x)
printfn "mins %A" mins
printfn "maxs %A" maxs
(*
 0:  1
 1:  3
 2:  5
 3:  6
 4:  7
 5:  5
 6:  4
 7:  6
 8:  9
 9: 10
10:  8
11:  7
12:  5
13: 10
mins [12; 6]
maxs [9; 4]
*)

答案 1 :(得分:2)

根据Brian的回答,我稍微偏爱这个版本:

let arr = [|1;3;5;6;7;5;4;6;9;10;8;7;5;10|] 

let find_min_max input = 
    let windows = Seq.windowed 3 input 
    let mins = Seq.filter (fun [|a;b;c|] -> a>b && b<c) windows
               |> Seq.map (fun [|a;b;c|] -> b)
    let maxs = Seq.filter (fun [|a;b;c|] -> a<b && b>c) windows
               |> Seq.map (fun [|a;b;c|] -> b)

    mins, maxs


let mins, maxs = find_min_max arr 

printfn "mins %A" mins
printfn "maxs %A" maxs

答案 2 :(得分:2)

基于Massif的回答,这是基于Brian的回答,我可能会将过滤器滚动并映射成一个:

let find_min_max input =  
    let windows = Seq.windowed 3 input  
    let mins = Seq.choose (fun [|a;b;c|] -> if a>b && b<c then Some(b) else None) windows 
    let maxs = Seq.choose (fun [|a;b;c|] -> if a<b && b>c then Some(b) else None) windows 

    mins, maxs 

:)

答案 3 :(得分:1)

我认为编写

是微不足道的
for x from 0 to size-2:
if (a[x] > a[x+1] && a[x+1] < a[x+2]) // also, there should be bound checking
    //a[x+1] is a min!
    minima.cram(x+1)
if (a[x] < a[x+1] && a[x+1] > a[x+2]) // also, there should be bound checking
    //a[x+1] is a max!
    maxima.cram(x+1)

或者我过度简化了?

答案 4 :(得分:0)

如果你正在寻找一个功能性的例子,你可能会(在Ruby中)

def derivative_signs(list)
  (0..list.length-2).map { |i| (list[i+1] - list[i]) <=> 0 }
  ## <=> is the "rocketship" operator which is basically: be -1 if number is negative
  ##                                                      be 0 if number is zero
  ##                                                      be 1 if number is positive
  ## Alternatively, one could use x / x.abs, with an exception if x == 0
end

def derivative(list)
  (0..list.length-2).map { |i| list[i+1] - list[i] }
end

来自微积分,最小值/最大值是一阶导数改变符号的时间。因此,可以查看derivative_signs(arr)并查找所有符号更改。

first_derivative_signs = derivative_signs(arr)
# => [1, 1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1]

或者,您也可以

second_derivative = derivative(derivative_signs(arr))

在您提供的列表中,您将获得:

[0, 0, 0, -2, 0, 2, 0, 0, -2, 0, 0, 2]

很明显,二阶导数-2的值是最大值,二阶导数2的值是最小值。二阶导数的索引是原始列表的索引+ 1.因此second_derivative[4] -2对应arr[5](7),这是最大值。

为什么我们第二次做一个“正常”导数,而不是衍生数据?

这是因为当一个值连续重复两次时,你会得到不想要的行为。

例如,考虑

[1, 3, 6, 6, 7, 5, 4, 6, 9, 10, 8, 7, 5, 10]
# first_derivative_signs =>  [1, 1, 0, 1, -1, -1, 1, 1, 1, -1, -1, -1, 1]
# second_derivative_signs => [0, -1, 1, -1, 0, 1, 0, 0, -1, 0, 0, 1]
# second_derivative       => [0, -1, 1, -2, 0, 2, 0, 0, -2, 0, 0, 2]

请注意,second_derivative_signs会向我们抛出一些“虚假”的最小值/最大值,而second_derivative,当我们只检查-22时,这是好的。

答案 5 :(得分:0)

我会使用Seq.pairwise来获取每个号码及其前身。然后,您可以计算每个数字与其前任之间的差异,以获得所有值之间的差异

在第三步中,您将检查差异并查找符号更改。当符号发生变化时,您知道之前符号更改的值是极值(最小值或最大值)。

我完全不知道F#,但第一步应该是这样的:

let diffs = Seq.pairwise arr |> Seq.map (-)