背景:
我目前正在研究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#以正确的方式写这个?
谢谢!
答案 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
这样可以改善吗?