2012-05-13 42 views
5

Tôi có hai mảng. Mảng đầu tiên chứa thứ tự sắp xếp. Mảng thứ hai chứa một số phần tử tùy ý.Sắp xếp một dãy số dựa trên một thứ tự nhất định

Tôi có thuộc tính rằng tất cả các phần tử (giá trị khôn ngoan) từ mảng thứ hai được đảm bảo nằm trong mảng đầu tiên và tôi chỉ làm việc với các số.

A = [1,3,4,4,4,5,2,1,1,1,3,3] 
Order = [3,1,2,4,5] 

Khi tôi loại A, tôi muốn các yếu tố xuất hiện theo thứ tự quy định của Order:

[3, 3, 3, 1, 1, 1, 1, 2, 4, 4, 4, 5] 

Lưu ý rằng các bản sao là trò chơi công bằng. Các yếu tố trong A không nên thay đổi, chỉ được sắp xếp lại. Tôi có thể làm cái này như thế nào?

+1

Bạn không nên bắt đầu tên biến của bạn với các chữ cái viết hoa, sau đó chúng trở thành hằng số. Ngoài ra, không có giá trị nào trong 'A' ngoài các giá trị trong' Thứ tự'? –

+0

Đối với trường hợp cụ thể này, có, không có giá trị nào khác. Nếu một số mảng ban đầu đã có giá trị khác, chúng sẽ được lọc ra trước khi đến loại này. – MxyL

Trả lời

11
>> source = [1,3,4,4,4,5,2,1,1,1,3,3] 
=> [1, 3, 4, 4, 4, 5, 2, 1, 1, 1, 3, 3] 
>> target = [3,1,2,4,5] 
=> [3, 1, 2, 4, 5] 
>> source.sort_by { |i| target.index(i) } 
=> [3, 3, 3, 1, 1, 1, 1, 2, 4, 4, 4, 5] 
+0

+1. Bạn đánh bại tôi đến nó sau 19 giây, tôi đã xóa câu trả lời của tôi :-) –

+2

@MichaelKohl bạn đã thực hiện một điểm tốt rằng nếu mảng này có khả năng nhận được lớn thì cách tiếp cận có thể được xem xét lại, nhưng điều này sẽ đủ nhanh cho hầu hết các mục đích – Gareth

4

Nếu (và chỉ khi!) @ Câu trả lời Gareth của hóa ra là quá chậm, thay vì đi với:

# Pre-create a hash mapping value to index once only… 
index = Hash[ Order.map.with_index.to_a ] #=> {3=>0,1=>1,2=>2,4=>3,5=>4} 

# …and then sort using this constant-lookup-time 
sorted = A.sort_by{ |o| index[o] } 

Benchmarked:

require 'benchmark' 

order = (1..50).to_a.shuffle 
items = 1000.times.map{ order.sample } 
index = Hash[ order.map.with_index.to_a ] 

Benchmark.bmbm do |x| 
    N = 10_000 
    x.report("Array#index"){ N.times{ 
    items.sort_by{ |n| order.index(n) } 
    }} 
    x.report("Premade Hash"){ N.times{ 
    items.sort_by{ |n| index[n] } 
    }} 
    x.report("Hash on Demand"){ N.times{ 
    index = Hash[ order.map.with_index.to_a ] 
    items.sort_by{ |n| index[n] } 
    }} 
end 

#=>      user  system  total  real 
#=> Array#index  12.690000 0.010000 12.700000 (12.704664) 
#=> Premade Hash  4.140000 0.000000 4.140000 ( 4.141629) 
#=> Hash on Demand 4.320000 0.000000 4.320000 ( 4.323060) 
+0

'# sort_by' đã tạo ra nội bộ một mảng các giá trị ánh xạ tạm thời - đây có phải là bộ nhớ cache Hash hiệu quả hơn mảng tuple [được đề cập trong tài liệu] (http://apidock.com/ruby/Enumerable/sort_by) không? – Gareth

+1

@Gareth Có, vì giá trị _n_ trong mảng có kích thước _m_ sử dụng 'Array # index' yêu cầu hoạt động trung bình _n * m/2_ (trường hợp xấu nhất: _n * m_), trong khi sử dụng tra cứu băm luôn sử dụng các thao tác _m_ hoặc _n + m_ cho trường hợp bạn đang bao gồm thời gian băm trong phép tính). Hơn nữa, _n_ cho 'index' phải diễn ra ở vùng đất Ruby chậm, trong khi _n_ với chuẩn bị băm diễn ra gần như hoàn toàn trong C. Xem phần chỉnh sửa của tôi. – Phrogz

+0

@Gareth Nhưng, như bạn đã nói trong bình luận của bạn, câu trả lời của bạn có lẽ sẽ là "đủ nhanh" hầu hết thời gian. Ví dụ, sắp xếp một mảng gồm 50 mục với một trong 10 giá trị là khoảng 30µs theo cách của bạn và 15-20µs theo cách của tôi. :) – Phrogz

1

Một giải pháp khả thi mà không phân loại rõ ràng:

source = [1,3,4,4,4,5,2,1,1,1,3,3] 
target = [3,1,2,4,5] 
source.group_by(&lambda{ |x| x }).values_at(*target).flatten(1) 
Các vấn đề liên quan