F# - 在F#中编写(GA)评估函数

时间:2015-06-11 08:59:18

标签: f#

背景
我目前正在研究F#中的遗传算法(GA) (我来自强大的C#背景,现在只使用F#一天了)

问题:
我有一个错误评估函数来确定建议的解决方案的有效性。
让我们假设一个简化的错误算法:

Calculate how may digits in the sequence are larger than their successor 

我们使用[1;6;3;6;8;9;5;]作为示例集。

因此26>3答案为9>5

到目前为止我的尝试:
这是一次失败的尝试,但我希望它可以作为帮助我的起点:

let proposedSolution = [1;6;3;6;8;9;5;]

for y in [0..proposedSolution.Length-2] do 
    printf "%d = (%d,%d) " y proposedSolution.[y] proposedSolution.[y+1] 
    if ( proposedSolution.[y] > proposedSolution.[y+1]) then printfn "!" else printfn "*"

结果:

val proposedSolution : int list = [1; 6; 3; 6; 8; 9; 5]

> 
0 = (1,6) *
1 = (6,3) !
2 = (3,6) *
3 = (6,8) *
4 = (8,9) *
5 = (9,5) !

所以不知怎的,我需要将*!映射到1和0并对它们求和,但是上面的代码可能无法实现。

问题:
我怎么能用F#以正确的方式写这个? 谢谢!

4 个答案:

答案 0 :(得分:2)

你可以在列表上进行模式匹配,以便对此进行计数:

let rec mismatchs xs =
    match xs with
    | [] | [_]  -> 0
    | (a::b::t) -> (if a > b then 1 else 0) + mismatchs (b::t)

一个小问题是,该函数不是尾递归的,所以你会遇到更大的列表问题。使尾部递归的常用方法是使用累加器:

let mismatchs xs =
    let rec mismatchs' xs acc =
        match xs with
        | [] | [_]  -> acc
        | (a::b::t) -> mismatchs (b::t) (if a > b then acc+1 else acc)
    mismatchs' xs 0

当然你也可以fold

let mismatchs xs =
    List.fold 
        (fun (last,acc) x -> 
            if last > x 
            then (x,acc+1) 
            else (x,acc)) 
        (0,0) xs
    |> snd

这里的国家只记得最后看到的价值以及错误的累积价值。

如果您愿意,您甚至可以将问题分开:首先使用1标记所有问题 - 位置(另一个标记为0):

let markProblems xs =
    xs
    |> Seq.scan 
        (fun (last, _) x -> 
            if last > x 
            then (x, 1) 
            else (x, 0)) 
        (0,0)
    |> Seq.map snd

然后sum他们:

let mismatchs xs = 
    markProblems xs
    |> Seq.sum

评论以上这两个假设您有正整数作为输入 - 如果不是,您应该使0中的第一个(0,0)(初始状态)比以往更小其他可能的值 - 例如(System.Int32.MinValue, 0)

在每种情况下答案都是:

let proposedSolution = [1;6;3;6;8;9;5;]
let answer = mismatchs proposedSolution // = 2

答案 1 :(得分:1)

由于您正在处理整数对,因此更容易使用List.pairwise。然后,您的输入数据将转换为整数元组列表:

proposedSolution 
    |> List.pairwise
val it : (int * int) list = [(1, 6); (6, 3); (3, 6); (6, 8); (8, 9); (9, 5)]

获得元组列表后,您可以用不同的方式计算结果。例如,使用countBy

proposedSolution
    |> List.pairwise
    |> List.countBy (fun (a,b) -> a > b)
val it : (bool * int) list = [(false, 4); (true, 2)]

fold获取所有匹配对:

proposedSolution
    |> List.pairwise
    |> List.fold (fun l (a,b) -> if (a>b) then (a,b)::l else l) []
val it : (int * int) list = [(9, 5); (6, 3)]

如果您正在处理大型数据集,那么您应该使用Seq而不是列表。例如:

let rnd = new System.Random(1234) // use fixed seed to generate same data set
#time
Seq.init 1000000 (fun _ -> rnd.Next(10))
    |> Seq.pairwise
    |> Seq.countBy (fun (a, b) -> a > b)
    |> Seq.toList   // force evalutation of sequence
val it : (bool * int) list = [(false, 550219); (true, 449780)]
Real: 00:00:00.462, CPU: 00:00:00.453, GC gen0: 38, gen1: 0, gen2: 0

答案 2 :(得分:1)

这是一个使用窗口的解决方案。它返回罪魁祸首值,然后您可以轻松地将它们相加作为答案。

let values = [1;6;3;6;8;9;5]

let largerInSeq s =
  s
  |> Seq.windowed 2
  |> Seq.fold (fun acc t -> if t.[1] < t.[0] then t::acc else acc) []

values |> largerInSeq |> printfn "%A"
values |> largerInSeq |> Seq.length

[[|9; 5|]; [|6; 3|]]
val it : int = 2

答案 3 :(得分:0)

我最终得到了一个解决方案:

let answer = 
    [0..proposedSolution.Length-2]
        |> List.map (fun x -> if proposedSolution.[x] > proposedSolution.[x+1] then 1 else 0)  
        |> List.sum 

这样可以改善吗?