Вычисление скользящего среднего значения в F#

Проблема заключается в том, что приложение на имитаторе / устройстве iOS устарело и несовместимо с текущей версией Expo, которую вы используете.

Удалите приложение Expo из симулятора iOS, и оно должно работать, или установите последнее обновление, если вы находитесь на устройстве. / 1595

Один из способов сделать это с открытым имитатором - это Erase All Content and Settings. Затем снова запустите npm start и приложение Expo снова будет установлено на симуляторе этого устройства.

enter image description here

5
задан warren 17 November 2008 в 11:37
поделиться

4 ответа

Если Вы не заботитесь слишком много о производительности, вот очень простое решение:

#light

let MovingAverage n s =
   Seq.windowed n s
   |> Seq.map Array.average

let avgs = MovingAverage 5000 (Seq.map float [|1..999999|])

for avg in avgs do
    printfn "%f" avg
    System.Console.ReadKey() |> ignore

Это повторно вычисляет среднее число каждого 'окна' с нуля, таким образом, это плохо, если окна являются большими.

В любом случае, выезд Seq.windowed:

http://research.microsoft.com/projects/cambridge/fsharp/manual/FSharp.Core/Microsoft.FSharp.Collections.Seq.html

поскольку удобно иметь в Вашем заднем кармане для таких вещей.

7
ответ дан 14 December 2019 в 01:20
поделиться

Вот (исправленный, я надеюсь), версия F# решения Haskell, предложенного здесь.

Править: Теперь рекурсивный хвостом, не немного быстрее, но не взрывается с n = 50000. (см. историю редактирования для non-tail-recursive версии),

let LimitedAverage n ls = 
    let rec loop acc i n ls = 
        match i with
        | 0 -> acc //i counts down from n to 0, when we hit 0 we stop
        | _ -> match ls with
               | [] -> acc //if we hit empty list before end of n, we stop too
               | x::xs -> (loop (acc + (x / float n)) (i - 1) n xs) //divide this value by n, perform average on 'rest' of list
    loop 0. n n ls

LimitedAverage 50000 (List.map float [1..9999999])
//>val it : float = 25000.5

let rec MovingAverage3 n ls = 
    let rec acc loop i n ls = 
        match i with 
        | 0 -> List.rev acc //i counts down from n to 0, when we hit 0 we stop
        | _ -> match ls with
                | [] -> List.rev acc //if we hit empty list before end of n, we stop too
                | x::xs -> loop (LimitedAverage2 n ls :: acc) (i - 1) n xs // limited average on whole list, then moving average on tail
    loop [] (n + 1) n ls 

MovingAverage3 50000 (List.map float [1..9999999])
//>val it : float list = [25000.5; 25001.5; 25002.5; ...]
1
ответ дан 14 December 2019 в 01:20
поделиться

Насколько я вижу, Ваш код полон let операторы. Я не знаком с F#, но действительно делал некоторого Haskell. Функциональные средства парадигмы, не думая, о "как", но о "какой": Вы думаете первым прибыл, первым обслужен, но необходимо на самом деле просто указать семантику скользящего среднего значения.

-- the limited average of a list
limitedaverage 0 _ = 0
limited verage n (x:xs) = (x/n) + ( limited average (n-1) xs )

-- a list, transformed into a sequence of moving averages of 
movingaverages n [] = []
movingaverages n (x:xs) = ( movingaverage n (x:xs) : movingaverages n xs )
-2
ответ дан 14 December 2019 в 01:20
поделиться

Если Вы действительно заботитесь о производительности, то можно вычислить скользящее среднее значение эффективно с помощью чего-то вроде этого (предположение, что мы вычисляем скользящее среднее значение по 3-дневному окну),

Numbers[n]    Running Total[n]
---------     ---------------
n[0] = 7       7 = Numbers[0]
n[1] = 1       8 = RunningTotal[1-1] + Numbers[1]
n[2] = 2      10 = RunningTotal[2-1] + Numbers[2]
n[3] = 8      11 = RunningTotal[3-1] + Numbers[3] - Numbers[3-3]
n[4] = 4      14 = RunningTotal[4-1] + Numbers[4] - Numbers[4-3]
n[5] = 1      13 = RunningTotal[5-1] + Numbers[5] - Numbers[5-3] 
n[6] = 9      14 = RunningTotal[6-1] + Numbers[6] - Numbers[6-3]
...
N             RunningTotal[N] = RunningTotal[N-1] + Numbers[N] - Numbers[N-3]

Твердая часть об этом держится Ваше предыдущее рабочее общее количество и NumberN-окно. Я придумал следующий код:

let movingAverage days l =
    seq {
        let queue = new Queue<_>(days : int)
        let divisor = decimal days

        let total = ref 0m
        for cur in l do
            queue.Enqueue(cur)
            total := !total + cur
            if queue.Count < days then
                yield (cur, 0m)
            else
                yield (cur, !total / divisor)
                total := !total - (queue.Dequeue())
    }

Эта версия не так симпатична как код Haskell, но это должно избежать проблем производительности, связанных с перевычислениями Вашего "окна" на каждом выполнении. Это сохраняет рабочее общее количество и содержит ранее используемые числа в очереди, таким образом, это должно быть очень быстро.

Только для забавы, я записал простой сравнительный тест:

#light
open System
open System.Collections.Generic
open System.Diagnostics;

let windowAverage days (l : #seq<decimal>) = Seq.windowed days l |> Seq.map (Seq.average)

let princessAverage days l =
    seq {
        let queue = new Queue<_>(days : int)
        let divisor = decimal days

        let total = ref 0m
        for cur in l do
            queue.Enqueue(cur)
            total := !total + cur
            if queue.Count < days then
                yield (cur, 0m)
            else
                yield (cur, !total / divisor)
                total := !total - (queue.Dequeue())
    }

let testData =
    let rnd = new System.Random()
    seq { for a in 1 .. 1000000 -> decimal (rnd.Next(1000)) }

let benchmark msg f iterations =
    let rec loop = function
        | 0 -> ()
        | n -> f 3 testData |> ignore; loop (n - 1)

    let stopWatch = Stopwatch.StartNew()
    loop iterations
    stopWatch.Stop()
    printfn "%s: %f" msg stopWatch.Elapsed.TotalMilliseconds

let _ =
    let iterations = 10000000
    benchmark "princessAverage" princessAverage iterations
    benchmark "windowAverage" windowAverage iterations
    printfn "Done"

Результаты:

princessAverage: 1670.791800
windowAverage: 2986.146900

Моя версия является ~1.79x быстрее.

2
ответ дан 14 December 2019 в 01:20
поделиться
Другие вопросы по тегам:

Похожие вопросы: