SORU
23 AĞUSTOS 2009, Pazar


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
24 AĞUSTOS 2009, PAZARTESİ


İ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
 }

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • Capcom Unity

    Capcom Unity

    5 NİSAN 2010
  • Stanislav Petrov

    Stanislav Pe

    7 ŞUBAT 2009
  • FF Radioo

    FF Radioo

    14 ŞUBAT 2007