Liste Hareketli Ortalama hesaplama
Bu hafta sonu bazı Scala ve Clojure de elimi denemeye karar verdi. Nesne yönelimli programlama konusunda uzman değilim, ve Scala bir dil olarak almak kolay değildi, ama fonksiyonel programlama denemek istedim. Bu zor olduğu bir yer.
Ben sadece fonksiyonları yazma moduna başımı almak için görünmüyor olabilir. Uzman işlevsel bir programcı olarak nasıl bir sorun yaklaşım mı?
Değerler listesi ve toplamı belirli bir süre verilecek, liste nasıl basit hareketli ortalama yeni bir liste oluşturmak ister misin?
Örneğin: Verilen liste values
(2.0, 4.0, 7.0, 6.0, 3.0, 8.0, 12.0, 9.0, 4.0, 1.0), ve period
4, işlevi dönmeyin: (0.0, 0.0, 0.0, 4.75, 5.0, 6.0, 7.25, 8.0, 8.25, 6.5)
Bir gün bunu evirip, Scala ile bulabildiğim en iyi geçirdikten sonra oldu bu
def simpleMovingAverage(values: List[Double], period: Int): List[Double] = {
(for (i <- 1 to values.length)
yield
if (i < period) 0.00
else values.slice(i - period, i).reduceLeft(_ _) / period).toList
}
Bu korkunç verimsiz olduğunu biliyorum, daha çok şey gibi yapardım:
where n < period: ma(n) = 0
where n = period: ma(n) = sum(value(1) to value(n)) / period
where n > period: man(n) = ma(n -1) - (value(n-period) / period) (value(n) / period)
Şimdi bu kadar kolay zorunlu bir tarzı olması, ama işlevsel olarak o, beni hayat için nasıl ifade edeceğimi çözemiyorum.
CEVAP
İlginç bir sorun. Birçok çözüm, etkinlik değişen derecelerde düşünemiyorum. Şeyleri tekrar tekrar eklemek zorunda performansı gerçekten bir sorun değil, ama olduğunu farz edelim. Ayrıca, başında sıfır var onları üreten dert etmeyelim bu kadar geç e, olabilir. Eğer algoritma onları doğal olarak iyi sağlar Eğer değilse, daha sonra biz doğru.
Aşağıdaki sliding
Liste, kayan bir pencere ile n >= period
sonuç verecek 2.8, Scala başlayarak:
def simpleMovingAverage(values: List[Double], period: Int): List[Double] =
List.fill(period - 1)(0.0) ::: (values sliding period map (_.sum) map (_ / period))
Bu oldukça şık olmasına rağmen yine de, daha önceden hesaplanmış eklemeler yararlanmak değil çünkü en iyi performansı mümkün zorunda değildir. Bunları demişken, onları nasıl elde edebiliriz?
Hadi bunu yazıyoruz ki:
values sliding 2 map sum
Her iki çift toplamından oluşan bir listemiz var. Bakalım bu sonuç 4 element hareketli ortalama hesaplamak için kullanmayı deneyin. Yukarıdaki formül aşağıdaki hesaplama yapılır:
from d1, d2, d3, d4, d5, d6, ...
to (d1 d2), (d2 d3), (d3 d4), (d4 d5), (d5 d6), ...
Her öğe almak ve ikinci bir sonraki öğe ekleyin, 4 element için hareketli ortalama alalım, böylece:
(d1 d2) (d3 d4), (d2 d3) (d4 d5), (d3 d4) (d5 d6), ...
Şöyle yapalım:
res zip (res drop 2) map Function.tupled(_ _)
Sonra 8 elemanları ve benzeri hareketli ortalama hesaplama yapabiliriz. Peki, bu desen izleyin şeyleri hesaplamak için bilinen bir algoritma var. Bir numaralı gücü bilgisayar kullanımı için en bilinir. Şöyle bir şey:
def power(n: Int, e: Int): Int = e match {
case 0 => 1
case 1 => n
case 2 => n * n
case odd if odd % 2 == 1 => power(n, (odd - 1)) * n
case even => power(power(n, even / 2), 2)
}
Bakalım burada da geçerlidir:
def movingSum(values: List[Double], period: Int): List[Double] = period match {
case 0 => throw new IllegalArgumentException
case 1 => values
case 2 => values sliding 2 map (_.sum)
case odd if odd % 2 == 1 =>
values zip movingSum(values drop 1, (odd - 1)) map Function.tupled(_ _)
case even =>
val half = even / 2
val partialResult = movingSum(values, half)
partialResult zip (partialResult drop half) map Function.tupled(_ _)
}
Yani, burada bir mantık. Dönem 0 geçersiz dönem 1 Giriş eşittir, dönem 2 boyut 2 pencere sürgülü. Bundan daha büyük, tek veya çift olabilir.
Garip, (odd - 1)
sonraki elemanları movingSum
Her bir eleman ekliyoruz. Örneğin, 3, sonraki 2 element movingSum
her bir öğe ekleyin.
Olsa bile, biz n / 2
movingSum
n / 2
bir adım için her öğe daha sonra hesaplamak ekleyin.
Bu tanımla, sorun dönüp bunu yapabiliriz:
def simpleMovingAverage(values: List[Double], period: Int): List[Double] =
List.fill(period - 1)(0.0) ::: (movingSum(values, period) map (_ / period))
:::
kullanımı ile ilgili ufak bir verimsizlik var ama O değil(nokta), (değerler.Ç boyut). Kuyruk özyinelemeli fonksiyon ile daha verimli hale getirilebilir. Ve, tabii ki, bu tanım" ben sağlanan korkunç performans-bilge, ama Scala 2.8 çok daha iyi bir tanım olacaktır. "sürgülü Ama Iterable
bunu yapabiliriz List
** 36 etkili bir yöntem yapamayız unutmayın.
Tüm bunları söyledikten sonra, eğer kritik yol analizi büyük bir anlaşma gibi bu kesin olarak belirlemiş eğer sadece ilk tanımı ile gidin ve optimize etmek isterim.
Sonuç olarak, bakalım sorun hakkında nasıl geçtiğini düşünün. Hareketli ortalama bir sorunumuz var. Hareketli ortalama hareketli "" listesi, şu pencereden. boyutuna göre bölünmüş pencere toplamıdır Yani, önce sürgülü bir pencere, her şeyi topla ve boyutunu bölün çalışın.
Bir sonraki sorunu daha önceden hesaplanmış eklemeler tekrarlanmasını önlemek için. Bu durumda, en küçük ayrıca Olası gittim, ve daha büyük meblağlar bu sonuçları yeniden hesaplamak için nasıl anlamaya çalıştı.
En son olarak sorunu anlamış şekilde çözmek için, ve önceki sonuçtan çıkartılacak. İlk ortalama almak kolay
def movingAverage(values: List[Double], period: Int): List[Double] = {
val first = (values take period).sum / period
Şimdi iki liste yapalım. İlk olarak, içinde bulundurduğu için öğeleri listesi. Gelecek, eklenecek elemanların listesi:
val subtract = values map (_ / period)
val add = subtract drop period
zip
kullanarak bu iki liste ekleyebiliriz. Bu yöntem, yalnızca subtract
sorunu gerekenden daha büyük önler küçük listesi vardır gibi birçok unsur, üretecek:
val addAndSubtract = add zip subtract map Function.tupled(_ - _)
Kat: sonuç beste ile bitiriyoruz
val res = (addAndSubtract.foldLeft(first :: List.fill(period - 1)(0.0)) {
(acc, add) => (add acc.head) :: acc
}).reverse
döndürülecek cevap. Bütün işlevi bu gibi görünüyor:
def movingAverage(values: List[Double], period: Int): List[Double] = {
val first = (values take period).sum / period
val subtract = values map (_ / period)
val add = subtract drop period
val addAndSubtract = add zip subtract map Function.tupled(_ - _)
val res = (addAndSubtract.foldLeft(first :: List.fill(period - 1)(0.0)) {
(acc, add) => (add acc.head) :: acc
}).reverse
res
}
Hesaplama aritmetik ortalama (ortalama...
Hesaplanmasında hareketli ortalama R...
Genel Liste - hareketli bir öğe listes...
Basit bir istatistik - ortalama hesapl...
EÄŸer bir liste varsa kontrol etmenin e...