2016-03-11 30 views
5

Tôi có ma trận sau trong Matlab:Tìm chỉ số cột cho 1 trong mỗi hàng của một ma trận

M = [0 0 1 
    1 0 0 
    0 1 0 
    1 0 0 
    0 0 1]; 

Mỗi hàng có chính xác một 1. Làm thế nào tôi (không vòng lặp) có thể xác định một vector cột để rằng phần tử đầu tiên là 2 nếu có 1 trong cột thứ hai, phần tử thứ hai là số 3 cho một phần tử trong cột thứ ba, v.v. Ví dụ trên sẽ chuyển thành:

M = [ 3 
     1 
     2 
     1 
     3]; 

Trả lời

5

Bạn thực sự có thể giải quyết điều này bằng phép nhân ma trận đơn giản.

result = M * (1:size(M, 2)).'; 

    3 
    1 
    2 
    1 
    3 

Điều này hoạt động bằng cách nhân ma trận M x 3 với mảng 3 x 1 trong đó các phần tử của 3x1 chỉ đơn giản là [1; 2; 3]. Tóm lại, đối với mỗi hàng của M, phép nhân yếu tố được thực hiện với mảng 3 x 1. Chỉ 1 trong hàng M sẽ mang lại kết quả. Sau đó, kết quả của phép nhân phần tử này được tổng kết. Bởi vì bạn chỉ có một "1" cho mỗi hàng, kết quả sẽ là chỉ mục cột nơi mà 1 được đặt.

Ví dụ: đối với hàng đầu tiên là M.

element_wise_multiplication = [0 0 1] .* [1 2 3] 

    [0, 0, 3] 

sum(element_wise_multiplication) 

    3 

Cập nhật

Dựa trên các giải pháp được cung cấp bởi @reyryeng@Luis dưới đây, tôi quyết định chạy một so sánh để xem cách thực hiện các phương pháp khác nhau so sánh.

Để thiết lập ma trận thử nghiệm (M) Tôi đã tạo một ma trận của biểu mẫu được chỉ định trong câu hỏi gốc và thay đổi số hàng. Cột nào có 1 được chọn ngẫu nhiên bằng cách sử dụng randi([1 nCols], size(M, 1)). Thời gian thực hiện được phân tích bằng cách sử dụng timeit.

Khi chạy bằng cách sử dụng M loại double (mặc định MATLAB), bạn sẽ nhận được thời gian thực hiện sau.

enter image description here

Nếu M là một logical, sau đó các nhân ma trận có một hit do thực tế là nó đã được chuyển đổi sang một kiểu số trước khi nhân ma trận, trong khi hai người kia có một chút của một cải thiện hiệu suất.

enter image description here

Đây là mã thử nghiệm mà tôi đã sử dụng.

sizes = round(linspace(100, 100000, 100)); 
times = zeros(numel(sizes), 3); 

for k = 1:numel(sizes) 
    M = generateM(sizes(k)); 
    times(k,1) = timeit(@()M * (1:size(M, 2)).'); 
    M = generateM(sizes(k)); 
    times(k,2) = timeit(@()max(M, [], 2), 2); 
    M = generateM(sizes(k)); 
    times(k,3) = timeit(@()find(M.'), 2); 
end 

figure 
plot(range, times/1000); 
legend({'Multiplication', 'Max', 'Find'}) 
xlabel('Number of rows in M') 
ylabel('Execution Time (ms)') 

function M = generateM(nRows) 
    M = zeros(nRows, 3); 
    col = randi([1 size(M, 2)], 1, size(M, 1)); 
    M(sub2ind(size(M), 1:numel(col), col)) = 1; 
end 
+0

Cảm ơn bạn rất nhiều vì câu trả lời rất nhanh của bạn. Nó là hoàn hảo. – machinery

2

Bạn cũng có thể lạm dụng find và quan sát hàng vị trí của các transpose của M. Bạn phải transpose ma trận đầu tiên như find hoạt động theo thứ tự lớn cột:

M = [0 0 1 
    1 0 0 
    0 1 0 
    1 0 0 
    0 0 1]; 

[out,~] = find(M.'); 

Không chắc nếu điều này là nhanh hơn so với phép nhân ma trận mặc dù.

+1

'find' là một trong những trường hợp hiếm hoi mà' [out, ~] = ... 'không giống như' out = ... ' –

+0

@LuisMendo aha yes sir! – rayryeng

2

Tuy nhiên, cách tiếp cận khác: sử dụng đầu ra thứ hai của max:

[~, result] = max(M.', [], 1); 

Hoặc, theo đề nghị của @rayryeng, sử dụng max dọc theo chiều hướng thứ hai thay vì transposing M:

[~, result] = max(M, [], 2); 

Đối

M = [0 0 1 
    1 0 0 
    0 1 0 
    1 0 0 
    0 0 1]; 

này cung cấp cho

result = 
    3  1  2  1  3 

Nếu M chứa nhiều hơn một 1 trong một hàng nhất định, điều này sẽ cung cấp cho các chỉ số những người đầu tiên 1 như vậy.

+0

Thực hiện tốt. Tôi luôn quên sử dụng 'max' theo cách này. Tôi tự hỏi những gì so sánh hiệu suất là giữa tất cả các phương pháp tiếp cận. Tiền của tôi là trên 'max' – rayryeng

+2

Có sự khác biệt về hiệu suất nếu bạn không chuyển đổi ma trận và chỉ định tìm kiếm dọc theo các hàng của ma trận thay thế không? Giống như chỉ định kích thước để xem là '2'? – rayryeng

+0

@rayryeng Điểm tốt. Có lẽ nhanh hơn không để chuyển đổi, có –

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