F# - 在 F# 中编写 (GA) 评估函数
F# - writing an (GA) evaluation function in F#
背景:
我目前正在 F# 中研究遗传算法 (GA)。
(我有很强的 C# 背景,现在只使用 F# 一天)
问题:
我有一个错误评估函数来确定建议的解决方案的有效性。
让我们假设一个简化的错误算法:
Calculate how may digits in the sequence are larger than their successor
我们以[1;6;3;6;8;9;5;]
为例。
所以答案是 2
因为 6>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# 以正确的方式编写此代码?
谢谢!
最后我得到了解决方案:
let answer =
[0..proposedSolution.Length-2]
|> List.map (fun x -> if proposedSolution.[x] > proposedSolution.[x+1] then 1 else 0)
|> List.sum
这样好还是可以改进?
您可以在列表中进行模式匹配以像这样计算:
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
remark 以上两个假设,您有正整数作为输入 - 如果不是,您应该在 (0,0)
中创建第一个 0
(初始state) 比任何其他可能的值都小 - 例如 (System.Int32.MinValue, 0)
在每种情况下,答案都是:
let proposedSolution = [1;6;3;6;8;9;5;]
let answer = mismatchs proposedSolution // = 2
由于您处理的是整数对,因此使用起来更容易 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
这是一个使用窗口的解决方案。它 returns 罪魁祸首值,然后您可以轻松地将它们相加作为答案。
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
背景:
我目前正在 F# 中研究遗传算法 (GA)。
(我有很强的 C# 背景,现在只使用 F# 一天)
问题:
我有一个错误评估函数来确定建议的解决方案的有效性。
让我们假设一个简化的错误算法:
Calculate how may digits in the sequence are larger than their successor
我们以[1;6;3;6;8;9;5;]
为例。
所以答案是 2
因为 6>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# 以正确的方式编写此代码?
谢谢!
最后我得到了解决方案:
let answer =
[0..proposedSolution.Length-2]
|> List.map (fun x -> if proposedSolution.[x] > proposedSolution.[x+1] then 1 else 0)
|> List.sum
这样好还是可以改进?
您可以在列表中进行模式匹配以像这样计算:
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
remark 以上两个假设,您有正整数作为输入 - 如果不是,您应该在 (0,0)
中创建第一个 0
(初始state) 比任何其他可能的值都小 - 例如 (System.Int32.MinValue, 0)
在每种情况下,答案都是:
let proposedSolution = [1;6;3;6;8;9;5;]
let answer = mismatchs proposedSolution // = 2
由于您处理的是整数对,因此使用起来更容易 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
这是一个使用窗口的解决方案。它 returns 罪魁祸首值,然后您可以轻松地将它们相加作为答案。
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