2010-09-21 33 views
41

Tôi đang mã hóa một cái gì đó tại thời điểm mà tôi đang dùng một loạt các giá trị theo thời gian từ một la bàn phần cứng. La bàn này là rất chính xác và cập nhật rất thường xuyên, với kết quả là nếu nó cười khúc khích một chút, tôi kết thúc với giá trị kỳ lạ đó là cực kỳ không phù hợp với các nước láng giềng của nó. Tôi muốn làm mịn những giá trị đó.Làm mịn các giá trị theo thời gian: di chuyển trung bình hoặc một cái gì đó tốt hơn?

Sau khi đọc xong, có vẻ như những gì tôi muốn là bộ lọc thông cao, bộ lọc thông thấp hoặc trung bình di chuyển. Di chuyển trung bình tôi có thể nhận được, chỉ cần giữ một lịch sử của 5 giá trị cuối cùng hoặc bất cứ điều gì, và sử dụng trung bình của những giá trị hạ lưu trong mã của tôi, nơi tôi đã từng chỉ sử dụng giá trị gần đây nhất. Đó là, tôi nghĩ rằng, mịn ra những tiếng cười khúc khích độc đáo, nhưng nó tấn công tôi rằng nó có thể khá kém hiệu quả, và điều này có lẽ là một trong những vấn đề được biết đến đúng lập trình mà có một giải pháp Clever Math thực sự gọn gàng.

Tuy nhiên, tôi là một trong những lập trình viên tự học khủng khiếp không có giáo dục chính quy trong bất kỳ thứ gì mơ hồ liên quan đến CompSci hoặc Toán. Đọc xung quanh một chút cho thấy rằng đây có thể là bộ lọc thông qua cao hoặc thấp, nhưng tôi không thể tìm thấy bất cứ điều gì giải thích về mặt dễ hiểu đối với một hack như tôi tác động của các thuật toán này lên một mảng giá trị, các công trình toán học. Câu trả lời được đưa ra here, ví dụ, về mặt kỹ thuật trả lời câu hỏi của tôi, nhưng chỉ về mặt hiểu được đối với những người có lẽ đã biết cách giải quyết vấn đề.

Nó sẽ là một người rất đáng yêu và thông minh thực sự những người có thể giải thích loại vấn đề này, và làm thế nào các giải pháp làm việc, trong điều khoản dễ hiểu cho một nghệ sĩ tốt nghiệp.

Trả lời

34

Nếu trung bình di chuyển của bạn phải dài để đạt được yêu cầu làm mịn, và bạn không thực sự cần bất kỳ hình dạng hạt nhân cụ thể nào, thì tốt hơn hết nếu bạn sử dụng trung bình di chuyển theo cấp số mũ:

a(i+1) = tiny*data(i+1) + (1.0-tiny)*a(i) 

nơi bạn chọn tiny là hằng số thích hợp (ví dụ: nếu bạn chọn tiny = 1- 1/N, nó sẽ có cùng số lượng trung bình dưới dạng cửa sổ có kích thước N, nhưng phân phối khác với điểm cũ).

Dù sao, vì giá trị tiếp theo của trung bình di chuyển chỉ phụ thuộc vào dữ liệu trước đó và dữ liệu của bạn, bạn không phải giữ hàng đợi hoặc bất kỳ thứ gì. Và bạn có thể nghĩ về điều này như làm một cái gì đó như, "Vâng, tôi đã có một điểm mới, nhưng tôi không thực sự tin tưởng nó, vì vậy tôi sẽ giữ 80% ước tính cũ của tôi về đo lường, và chỉ tin tưởng điểm dữ liệu mới này 20% ". Điều đó cũng tương tự như khi nói, "Chà, tôi chỉ tin tưởng điểm mới này 20%, và tôi sẽ sử dụng 4 điểm khác mà tôi tin tưởng cùng một số tiền", ngoại trừ việc thay vì dùng một cách rõ ràng 4 điểm khác, bạn giả sử rằng trung bình bạn đã làm lần trước là hợp lý để bạn có thể sử dụng công việc trước đó của bạn.

+6

Giải thích tốt, cảm ơn Rex. Tôi có nghĩ rằng một cách khác để diễn tả cùng một điều sẽ là: workingAverage = (newValue * smoothingFactor) + (workingAverage * (1.0 - smoothingFactor))? –

+4

@Henry - Phải, đó là cách để làm điều đó mà không cần sử dụng bất kỳ bộ nhớ bổ sung nào. –

+2

Xin chào, tôi biết đây là trễ 5 năm nhưng cảm ơn bạn đã có câu trả lời tuyệt vời. Tôi đang làm việc trên một trò chơi mà âm thanh thay đổi dựa trên vận tốc của bạn, nhưng do chạy trò chơi trên máy tính chạy chậm, tốc độ sẽ dao động dữ dội, điều đó tốt cho việc điều khiển, nhưng siêu gây phiền nhiễu về mặt âm thanh. Đây là một giải pháp thực sự đơn giản và rẻ tiền cho thứ mà tôi nghĩ là một vấn đề thực sự phức tạp. – Adam

6

Di chuyển trung bình Tôi có thể gỡ xuống ... nhưng nó cho tôi biết rằng có lẽ không hiệu quả lắm.

Thực sự không có lý do gì khiến trung bình di chuyển không hiệu quả. Bạn giữ số lượng các điểm dữ liệu bạn muốn trong một số bộ đệm (như một hàng đợi hình tròn). Trên mỗi điểm dữ liệu mới, bạn bật giá trị cũ nhất và trừ giá trị đó khỏi tổng, và đẩy giá trị mới nhất và thêm nó vào tổng. Vì vậy, mọi điểm dữ liệu mới thực sự chỉ đòi hỏi một pop/push, một phép cộng và phép trừ. Trung bình động của bạn luôn là tổng số chuyển đổi này chia cho số lượng giá trị trong bộ đệm của bạn.

Nó nhận được một nhỏ phức tạp hơn nếu bạn đang nhận dữ liệu đồng thời từ nhiều luồng, nhưng vì dữ liệu của bạn đến từ thiết bị phần cứng có vẻ nghi ngờ cao đối với tôi.

Oh và cũng: các lập trình viên tự học khủng khiếp đoàn kết! ;)

+0

Trung bình di chuyển dường như không hiệu quả với tôi bởi vì bạn phải lưu trữ một bộ đệm giá trị - tốt hơn là chỉ thực hiện một số Toán học thông minh với giá trị đầu vào và giá trị hiện tại của bạn? Tôi nghĩ đó là cách hoạt động trung bình của hàm mũ. Một tối ưu hóa tôi đã thấy cho loại di chuyển trung bình này liên quan đến việc sử dụng hàng đợi có độ dài cố định và một con trỏ đến nơi bạn đang ở trong hàng đợi đó và chỉ bao quanh con trỏ (với% hoặc if). Thì đấy! Không có push/pop đắt tiền. Quyền lực cho những người nghiệp dư, anh trai! –

+0

@Henry: Đối với một trung bình di chuyển thẳng lên, bạn cần bộ đệm đơn giản để bạn biết giá trị nào được xuất hiện khi giá trị tiếp theo được đẩy. Điều đó nói rằng, "hàng đợi có độ dài cố định và một con trỏ" mà bạn mô tả chính xác là những gì tôi muốn nói "hàng đợi tròn". Đó là lý do tại sao tôi nói nó không hiệu quả. Bạn đã nghĩ gì * Ý tôi là gì? Và nếu phản ứng của bạn là "một mảng làm thay đổi giá trị của nó trở lại trên mọi loại bỏ được lập chỉ mục" (như 'std :: vector' trong C++) ... thì, tôi rất đau Tôi thậm chí không muốn nói chuyện với bạn nữa;) –

+0

không, tôi nghĩ bạn có nghĩa là một array.push()/array.unshift() hoặc một cái gì đó, giống như một lập trình viên AS3 hoặc Java thực sự sẽ làm. Tha thứ cho tôi. Nghệ thuật tốt nghiệp, nhớ không? –

53

Nếu bạn đang cố gắng loại bỏ giá trị kỳ lạ không thường xuyên, bộ lọc thông thấp là lựa chọn tốt nhất trong số ba tùy chọn mà bạn đã xác định. Bộ lọc low-pass cho phép thay đổi tốc độ thấp, chẳng hạn như những thay đổi gây ra bằng cách xoay la bàn bằng tay, trong khi từ chối các thay đổi tốc độ cao như những thay đổi do va chạm trên đường.

Trung bình di chuyển có thể sẽ không đủ, vì ảnh hưởng của một "đốm" trong dữ liệu của bạn sẽ ảnh hưởng đến một số giá trị tiếp theo, tùy thuộc vào kích thước cửa sổ trung bình di chuyển của bạn.

Nếu các giá trị lẻ có thể dễ dàng phát hiện, bạn thậm chí có thể được tốt hơn off với một thuật toán trục trặc-loại bỏ hoàn toàn bỏ qua chúng:

if (abs(thisValue - averageOfLast10Values) > someThreshold) 
{ 
    thisValue = averageOfLast10Values; 
} 

Dưới đây là một đồ thị guick để minh họa:

graph comparison

Biểu đồ đầu tiên là tín hiệu đầu vào, với một trục trặc khó chịu. Biểu đồ thứ hai cho thấy hiệu ứng của trung bình động 10 mẫu. Biểu đồ cuối cùng là sự kết hợp của trung bình 10 mẫu và thuật toán phát hiện trục trặc đơn giản được hiển thị ở trên. Khi phát hiện trục trặc, trung bình 10 mẫu được sử dụng thay cho giá trị thực tế.

+0

Giải thích rõ ràng và điểm thưởng cho biểu đồ;) –

+0

Wow .. Ít khi thấy một câu trả lời hay! – Muis

+2

Trung bình di chuyển * là * bộ lọc thông thấp. – nomen

2

Một trung bình di chuyển theo cấp số nhân có thể được tính "bằng tay" chỉ với xu hướng nếu bạn sử dụng các giá trị thích hợp. Xem http://www.fourmilab.ch/hackdiet/e4/ để có ý tưởng về cách thực hiện điều này một cách nhanh chóng bằng bút và giấy nếu bạn đang tìm kiếm “trung bình di chuyển theo cấp số nhân với 10% làm mịn”. Nhưng vì bạn có một máy tính, bạn có thể muốn thực hiện chuyển dịch nhị phân thay vì chuyển số thập phân;)

Bằng cách này, tất cả những gì bạn cần là một biến cho giá trị hiện tại và giá trị trung bình. Trung bình tiếp theo có thể được tính từ đó.

+0

Giải thích tốt về EMA, cảm ơn. –

1

có một kỹ thuật được gọi là cổng phạm vi hoạt động tốt với các mẫu giả mạo xuất hiện thấp. giả sử sử dụng một trong các kỹ thuật lọc được đề cập ở trên (trung bình di động, theo hàm số mũ), khi bạn có lịch sử "đủ" (một Lần liên tục), bạn có thể kiểm tra mẫu dữ liệu mới, đến hợp lý, trước nó được thêm vào tính toán.

một số kiến ​​thức về tốc độ thay đổi tín hiệu hợp lý tối đa được yêu cầu. mẫu thô được so sánh với giá trị được làm mịn gần đây nhất, và nếu giá trị tuyệt đối của chênh lệch đó lớn hơn phạm vi cho phép, mẫu đó được loại bỏ (hoặc thay thế bằng một số phỏng đoán, ví dụ như dự đoán dựa trên độ dốc; Giá trị dự đoán “xu hướng” từ làm mịn theo cấp số nhân)

Các vấn đề liên quan