Thuật toán không cần thiết để tính toán trung bình cửa sổ trượt


25

Tôi cần tính toán trung bình đang chạy:

  • Đầu vào: , k , vectơ ( x 1 , x 2 , Mạnh , x n ) .nk(x1,x2,,xn)

  • Output: vector , nơi y i là trung bình ( x i , x i + 1 , ... , x i + k - 1 ) .(y1,y2,,ynk+1)yi(xi,xi+1,,xi+k1)

(Không gian lận với xấp xỉ; tôi muốn có các giải pháp chính xác. Các yếu tố là các số nguyên lớn.)xi

Có một thuật toán tầm thường duy trì một cây tìm kiếm có kích thước ; tổng thời gian chạy là O ( n log k ) . (Ở đây, "cây tìm kiếm" đề cập đến một số cấu trúc dữ liệu hiệu quả hỗ trợ chèn, xóa và truy vấn trung bình trong thời gian logarit.)kO(nlogk)

Tuy nhiên, điều này có vẻ hơi ngu ngốc đối với tôi. Chúng tôi sẽ tìm hiểu một cách hiệu quả tất cả các số liệu thống kê đơn hàng trong tất cả các cửa sổ có kích thước , không chỉ trung bình. Hơn nữa, điều này không quá hấp dẫn trong thực tế, đặc biệt nếu k lớn (cây tìm kiếm lớn có xu hướng chậm, chi phí sử dụng bộ nhớ không tầm thường, hiệu quả bộ nhớ cache thường kém, v.v.).kk

Chúng ta có thể làm bất cứ điều gì tốt hơn đáng kể?

Có bất kỳ giới hạn thấp hơn (ví dụ, thuật toán tầm thường là tối ưu không có triệu chứng cho mô hình so sánh)?


Chỉnh sửa: David Eppstein đã đưa ra một giới hạn dưới tốt đẹp cho mô hình so sánh! Tôi tự hỏi liệu tuy nhiên có thể làm một cái gì đó thông minh hơn một chút so với thuật toán tầm thường không?

Ví dụ, chúng ta có thể làm gì đó dọc theo các dòng này không: chia vectơ đầu vào cho các phần có kích thước ; sắp xếp từng phần (theo dõi các vị trí ban đầu của từng yếu tố); và sau đó sử dụng vectơ được sắp xếp piecewise để tìm các trung vị đang chạy một cách hiệu quả mà không có bất kỳ cấu trúc dữ liệu phụ trợ nào? Tất nhiên đây vẫn là O ( n log k ) , nhưng trong thực tế, việc sắp xếp các mảng có xu hướng nhanh hơn nhiều so với việc duy trì các cây tìm kiếm.kO(nlogk)


Chỉnh sửa 2: Saeed muốn xem một số lý do tại sao tôi nghĩ sắp xếp nhanh hơn hoạt động của cây tìm kiếm. Dưới đây là các điểm chuẩn rất nhanh, cho , n = 10 8 :k=107n=108

  • 8s: sắp xếp các vectơ với k phần tử mỗi phần tửn/kk
  • 10s: sắp xếp một vectơ với phần tửn
  • 80s: chèn và xóa trong bảng băm có kích thước knk
  • ≈ 390s: chèn và xóa trong cây tìm kiếm cân bằng có kích thước knk

Bảng băm là có để so sánh; nó không được sử dụng trực tiếp trong ứng dụng này.

Tóm lại, chúng tôi có sự khác biệt gần như 50 nhân tố trong hiệu suất sắp xếp so với các hoạt động của cây tìm kiếm cân bằng. Và mọi thứ trở nên tồi tệ hơn nếu chúng ta tăng .k

(Chi tiết kỹ thuật: Dữ liệu = số nguyên 32 bit ngẫu nhiên. Máy tính = máy tính xách tay hiện đại điển hình. Mã kiểm tra được viết bằng C ++, sử dụng các thường trình thư viện tiêu chuẩn (std :: sort) và cấu trúc dữ liệu (std :: multiset, std :: unsort_multiset). Tôi đã sử dụng hai trình biên dịch C ++ khác nhau (GCC và Clang) và hai triển khai khác nhau của thư viện chuẩn (libstdc ++ và libc ++). Theo truyền thống, std :: multiset đã được triển khai như một cây đen tối ưu hóa cao.)


1
Tôi không nghĩ rằng bạn sẽ có thể cải thiện . Lý do là, nếu bạn nhìn vào một cửa sổ x t , . . . , x t + k - 1 , bạn không bao giờ có thể loại trừ bất kỳ số nào x t + knlogkxt,...,xt+k1từ trung vị của cửa sổ tương lai. Điều này có nghĩa là bất cứ lúc nào bạn cũng phải giữ ít nhấtkxt+k2,...,xt+k1 số nguyên trong cấu trúc dữ liệu và dường như không cập nhật trong thời gian ngắn hơn thời gian đăng nhập. k2
RB

Thuật toán tầm thường của bạn với tôi có vẻ là không O ( n log k ) , tôi hiểu lầm cái gì? Và tôi nghĩ bởi vì điều này bạn có vấn đề với k lớn , nếu không thì yếu tố logarit không là gì trong các ứng dụng thực tế, cũng không có hằng số ẩn lớn trong thuật toán này. O((nk)klogk)O(nlogk)k
Saeed

@Saeed: Trong thuật toán tầm thường, bạn xử lý từng phần tử một; trong bước bạn thêm x i vào cây tìm kiếm và (nếu i > k ) bạn cũng xóa x i - k khỏi cây tìm kiếm. Đây là n bước, mỗi bước mất thời gian O ( log k ) . ixii>kxiknO(logk)
Jukka Suomela

Vì vậy, bạn có nghĩa là bạn có một cây tìm kiếm cân bằng chứ không phải cây tìm kiếm thông thường?
Saeed

1
@Saeed: Xin lưu ý rằng trong điểm chuẩn của tôi, tôi thậm chí không cố gắng tìm trung bình. Tôi vừa thực hiện chèn và n xóa trong cây tìm kiếm có kích thước k và các thao tác này được đảm bảo mất thời gian O ( log k ) . Bạn chỉ cần chấp nhận rằng hoạt động của cây tìm kiếm rất chậm trong thực tế, so với việc sắp xếp. Bạn sẽ thấy điều này một cách dễ dàng nếu bạn cố gắng viết một thuật toán sắp xếp hoạt động bằng cách thêm các phần tử vào cây tìm kiếm cân bằng - nó chắc chắn hoạt động trong thời gian O ( n log n ) , nhưng nó sẽ chậm một cách lố bịch trong thực tế và cũng lãng phí rất nhiều của bộ nhớ. nnkO(logk)O(nlogn)
Jukka Suomela 30/03/2016

Câu trả lời:


32

Đây là một giới hạn thấp hơn từ sắp xếp. Cho một tập hợp đầu vào có độ dài n được sắp xếp, tạo một đầu vào cho bài toán trung bình đang chạy của bạn bao gồm n - 1 bản sao của một số nhỏ hơn tối thiểu của S , sau đó chính S , sau đó n - 1 bản sao của một số lớn hơn tối đa của S và đặt k = 2 n - 1 . Các trung vị chạy đầu vào này cũng giống như trình tự sắp xếp của S .Snn1SSn1Sk=2n1S

Vì vậy, trong một mô hình so sánh của tính toán, thời gian là bắt buộc. Có thể nếu đầu vào của bạn là số nguyên và bạn sử dụng thuật toán sắp xếp số nguyên, bạn có thể làm tốt hơn.Ω(nlogn)


6
Câu trả lời này thực sự khiến tôi tự hỏi liệu điều ngược lại có đúng không: đưa ra một thuật toán sắp xếp hiệu quả, liệu chúng ta có nhận được một thuật toán trung bình chạy hiệu quả không? (Ví dụ: trên thuật toán sắp xếp số nguyên hiệu quả có nghĩa là thuật toán trung bình chạy hiệu quả cho các số nguyên không? Hay thuật toán sắp xếp hiệu quả IO có cung cấp thuật toán trung bình chạy hiệu quả IO không?)
Jukka Suomela

1
Một lần nữa, cảm ơn câu trả lời của bạn, nó thực sự đưa tôi đi đúng hướng và truyền cảm hứng cho thuật toán lọc trung bình dựa trên sắp xếp! Cuối cùng, tôi đã có thể tìm thấy một bài báo từ năm 1991 về cơ bản lập luận tương tự như những gì bạn đưa ra ở đây, và Pat Morin đã đưa ra một con trỏ đến một bài báo khác có liên quan từ năm 2005; xem tài liệu tham khảo. [6] và [9] tại đây .
Jukka Suomela

9

Chỉnh sửa: Thuật toán này hiện được trình bày tại đây: http://arxiv.org/abs/1406.1717


Có, để giải quyết vấn đề này, việc thực hiện các thao tác sau là đủ:

  • Sắp xếp các vectơ , mỗi vectơ k .n/kk
  • Làm hậu xử lý tuyến tính thời gian.

Rất đại khái, ý tưởng là thế này:

  • Xét hai khối đầu vào liền kề, b , cả hai đều có phần tử k ; để cho các yếu tố có một 1 , một 2 , . . . , a kb 1 , b 2 , . . . , b k theo thứ tự xuất hiện trong vectơ đầu vào x .abka1,a2,...,akb1,b2,...,bkx
  • Sắp xếp các khối này và tìm hiểu thứ hạng của từng yếu tố trong khối.
  • Tăng cường các vectơ b bằng các con trỏ của người tiền nhiệm / người kế nhiệm để bằng cách theo các chuỗi con trỏ, chúng ta có thể duyệt qua các phần tử theo thứ tự tăng dần. Bằng cách này, chúng tôi đã xây dựng gấp đôi danh sách liên kết một 'b ' .abab
  • Từng người một, xóa tất cả các yếu tố từ danh sách liên kết , theo thứ tự ngược lại với sự xuất hiện b k , b k - 1 , . . . , b 1 . Bất cứ khi nào chúng tôi xóa một yếu tố, hãy nhớ những gì là người kế thừa và tiền thân của nó tại thời điểm xóa .bbk,bk1,...,b1
  • Bây giờ duy trì "con trỏ trung bình" q điểm đó vào danh sách một 'b ' , tương ứng. Khởi p đến điểm giữa của một ' , và khởi q để đuôi của danh sách trống b ' .pqabpaqb
  • Đối với mỗi :i

    • Xóa ra khỏi danh sách một ' (đây là O ( 1 ) thời gian, chỉ cần xóa khỏi danh sách liên kết). So sánh một i với phần tử được chỉ bởi p để xem chúng ta đã xóa trước hay sau p .aiaO(1)aipp
    • Đặt trở lại danh sách b ở vị trí ban đầu (đây là thời gian O ( 1 ) , chúng tôi ghi nhớ người tiền nhiệm và người kế thừa của b i ). So sánh b i với phần tử được chỉ bởi q để xem chúng ta đã thêm phần tử trước hay sau q .bibO(1)bibiqq
    • pqabpqO(1)pqpq

k


Dưới đây là một triển khai mẫu và điểm chuẩn:

n2106

  • O(nlogk)
  • O(nlogk)
  • O(nlogk)
  • O(nk)
  • k/2
  • Trục Y = thời gian chạy tính bằng giây.
  • Dữ liệu = số nguyên 32 bit và số nguyên 64 bit ngẫu nhiên, từ các bản phân phối khác nhau.

thời gian chạy


3

mO(nlogm+mlogn)

O(logm)O(logn)O(logn) phí chỉ xảy ra một lần trên trung bình.

O(nlogm+mlogk)


Rất tiếc, điều này không hoạt động như được viết, vì nếu bạn không xóa các yếu tố, số lượng sẽ không phản ánh cửa sổ mới. Tôi không chắc có thể sửa được không, nhưng tôi sẽ để lại câu trả lời trong trường hợp có cách.
Geoffrey Irving

O(nlogm)

Lưu ý bên lề: Câu hỏi không rõ ràng, cấu trúc dữ liệu không được xác định, chúng tôi chỉ biết một cái gì đó rất mơ hồ. Làm thế nào để bạn muốn cải thiện một cái gì đó mà bạn không biết nó là gì? bạn muốn so sánh cách tiếp cận của bạn như thế nào?
Saeed

1
Tôi xin lỗi vì công việc không hoàn thành. Tôi đã hỏi câu hỏi cụ thể cần thiết để sửa câu trả lời này tại đây: cstheory.stackexchange.com/questions/21778/ . Nếu bạn nghĩ nó phù hợp tôi có thể xóa câu trả lời này cho đến khi câu hỏi phụ được giải quyết.
Geoffrey Irving
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.