ฉันจะวนซ้ำแต่ละองค์ประกอบในเมทริกซ์ n มิติใน MATLAB ได้อย่างไร


87

ฉันมีปัญหา. ฉันต้องการวนซ้ำทุกองค์ประกอบในเมทริกซ์ n มิติใน MATLAB ปัญหาคือฉันไม่รู้ว่าจะทำอย่างไรกับจำนวนมิติโดยพลการ ฉันรู้ว่าฉันพูดได้

for i = 1:size(m,1)
    for j = 1:size(m,2)
        for k = 1:size(m,3)

และอื่น ๆ แต่มีวิธีทำตามจำนวนมิติโดยพลการหรือไม่?


13
หมายเหตุคำศัพท์ Matlab: Matlab มีประเภทข้อมูลหลักจำนวนเล็กน้อย ที่สำคัญที่สุด ได้แก่ โครงสร้างเมทริกซ์และอาร์เรย์ของเซลล์ เมื่อกล่าวถึงส่วนต่างๆของเมทริกซ์มักจะใช้คำว่า "องค์ประกอบ" และสงวนคำว่า "เซลล์" เพื่ออ้างถึงส่วนต่างๆของอาร์เรย์เซลล์ อาร์เรย์ของเซลล์และเมทริกซ์มีความแตกต่างทางวากยสัมพันธ์และความหมายแม้ว่าทั้งสองจะเป็นโครงสร้างข้อมูลแบบ N มิติ
Mr Fooz

3
ฉันขอถามว่าคุณต้องการการทำซ้ำเพื่ออะไร อาจจะมีวิธีทำแบบ "vectorized" แทน ...
Hosam Aly

คำตอบ:


92

คุณสามารถใช้การจัดทำดัชนีเชิงเส้นเพื่อเข้าถึงแต่ละองค์ประกอบ

for idx = 1:numel(array)
    element = array(idx)
    ....
end

สิ่งนี้มีประโยชน์หากคุณไม่จำเป็นต้องรู้ว่า i, j, k คุณอยู่ที่อะไร อย่างไรก็ตามหากคุณไม่จำเป็นต้องรู้ว่าคุณอยู่ที่ดัชนีใดคุณควรใช้ arrayfun ()


1
I = cell(1, ndims(array)); [I{:}] = ind2sub(size(array),idx);นอกจากนี้ถ้าคุณต้องการที่จะกู้คืนดัชนีด้วยเหตุผลบางอย่างที่คุณอาจจะยังคงใช้ทั้งสองคำสั่งง่ายๆ
knedlsepp

34

แนวคิดของดัชนีเชิงเส้นสำหรับอาร์เรย์ใน matlab เป็นสิ่งสำคัญ อาร์เรย์ใน MATLAB เป็นเพียงเวกเตอร์ขององค์ประกอบซึ่งอยู่ในหน่วยความจำ MATLAB อนุญาตให้คุณใช้ดัชนีแถวและคอลัมน์หรือดัชนีเชิงเส้นเดียว ตัวอย่างเช่น,

A = magic(3)
A =
     8     1     6
     3     5     7
     4     9     2

A(2,3)
ans =
     7

A(8)
ans =
     7

เราสามารถดูลำดับที่องค์ประกอบจะถูกเก็บไว้ในหน่วยความจำโดยการยกเลิกการเลื่อนอาร์เรย์เป็นเวกเตอร์

A(:)
ans =
     8
     3
     4
     1
     5
     9
     6
     7
     2

อย่างที่คุณเห็นองค์ประกอบที่ 8 คือตัวเลข 7 ในความเป็นจริงฟังก์ชัน find จะส่งคืนผลลัพธ์เป็นดัชนีเชิงเส้น

find(A>6)
ans =
     1
     6
     8

ผลลัพธ์คือเราสามารถเข้าถึงแต่ละองค์ประกอบโดยใช้อาร์เรย์ nd ทั่วไปโดยใช้ลูปเดียว ตัวอย่างเช่นหากเราต้องการยกกำลังสององค์ประกอบของ A (ใช่ฉันรู้ว่ามีวิธีที่ดีกว่านี้ในการทำเช่นนี้) วิธีหนึ่งอาจทำได้:

B = zeros(size(A));
for i = 1:numel(A)
  B(i) = A(i).^2;
end

B
B =
    64     1    36
     9    25    49
    16    81     4

มีหลายสถานการณ์ที่ดัชนีเชิงเส้นมีประโยชน์มากกว่า การแปลงระหว่างดัชนีเชิงเส้นและตัวห้อยมิติสองตัว (หรือสูงกว่า) สามารถทำได้ด้วยฟังก์ชัน sub2ind และ ind2sub

ดัชนีเชิงเส้นใช้โดยทั่วไปกับอาร์เรย์ใด ๆ ใน matlab ดังนั้นคุณสามารถใช้มันกับโครงสร้างอาร์เรย์เซลล์ ฯลฯ ปัญหาเดียวของดัชนีเชิงเส้นคือเมื่อมันมีขนาดใหญ่เกินไป MATLAB ใช้จำนวนเต็ม 32 บิตเพื่อจัดเก็บดัชนีเหล่านี้ ดังนั้นหากอาร์เรย์ของคุณมีองค์ประกอบทั้งหมด 2 ^ 32 ในนั้นดัชนีเชิงเส้นจะล้มเหลว เป็นเพียงปัญหาเท่านั้นหากคุณใช้เมทริกซ์แบบเบาบางบ่อยครั้งซึ่งบางครั้งอาจทำให้เกิดปัญหา (แม้ว่าฉันจะไม่ได้ใช้ MATLAB รุ่น 64 บิต แต่ฉันเชื่อว่าปัญหาได้รับการแก้ไขแล้วสำหรับผู้โชคดีที่ทำ)


การจัดทำดัชนีใน MATLAB 64 บิตทำให้สามารถใช้ตัวห้อย 64 บิตได้อย่างถูกต้อง ตัวอย่างเช่น: x = ones(1,2^33,'uint8'); x(2^33)ทำงานได้ตามที่คาดไว้
Edric

@Edric - แน่นอนว่านี่เป็นพฤติกรรมที่จะเปลี่ยนไปอย่างแน่นอนในช่วงหลายปีที่ผ่านมา (และหลาย ๆ รุ่น) นับตั้งแต่ที่ฉันแถลง ขอขอบคุณที่ตรวจสอบ

:) ฉันไม่รู้ว่าคำตอบนั้นเก่าแค่ไหนจนกระทั่งฉันแสดงความคิดเห็น - คำถามเพิ่งปรากฏในฟีด RSS ของฉันและฉันก็ไม่ได้สังเกตว่าฉันตอบมันด้วย!
Edric

15

ดังที่ได้ระบุไว้ในคำตอบอื่น ๆ คุณสามารถทำซ้ำองค์ประกอบทั้งหมดในเมทริกซ์A(ของมิติใดก็ได้) โดยใช้ดัชนีเชิงเส้นจาก1ถึงnumel(A)ในคำตอบเดียวสำหรับการวนซ้ำ นอกจากนี้ยังมีฟังก์ชันสองสามอย่างที่คุณสามารถใช้ได้: arrayfunและcellfun.

ก่อนอื่นสมมติว่าคุณมีฟังก์ชันที่คุณต้องการใช้กับแต่ละองค์ประกอบของA(เรียกว่าmy_func) คุณต้องสร้างตัวจัดการฟังก์ชันสำหรับฟังก์ชันนี้ก่อน:

fcn = @my_func;

หากAเป็นเมทริกซ์ (ประเภทคู่เดี่ยว ฯลฯ ) ของมิติที่กำหนดเองคุณสามารถใช้arrayfunเพื่อนำmy_funcไปใช้กับแต่ละองค์ประกอบ:

outArgs = arrayfun(fcn, A);

ถ้าAเป็นอาร์เรย์เซลล์ของมิติที่กำหนดเองคุณสามารถใช้cellfunเพื่อนำmy_funcไปใช้กับแต่ละเซลล์:

outArgs = cellfun(fcn, A);

ฟังก์ชันmy_funcต้องยอมรับAเป็นอินพุต หากมีผลใด ๆ จากmy_funcเหล่านี้จะอยู่ในoutArgsซึ่งจะเหมือนกันขนาด / Aมิติเป็น

หนึ่งในผลข้อแม้ ... ถ้าmy_funcผลตอบแทนที่แตกต่างกันขนาดและประเภทเมื่อมันดำเนินการในองค์ประกอบที่แตกต่างกันAแล้วoutArgsจะต้องมีการทำในอาร์เรย์เซลล์ ทำได้โดยการโทรarrayfunหรือcellfunคู่พารามิเตอร์ / ค่าเพิ่มเติม:

outArgs = arrayfun(fcn, A, 'UniformOutput', false);
outArgs = cellfun(fcn, A, 'UniformOutput', false);

13

หนึ่งเคล็ดลับอื่น ๆ คือการใช้และind2sub sub2indเมื่อใช้ร่วมกับnumelและsizeสิ่งนี้จะช่วยให้คุณทำสิ่งต่างๆดังต่อไปนี้ซึ่งจะสร้างอาร์เรย์ N มิติจากนั้นตั้งค่าองค์ประกอบทั้งหมดบน "เส้นทแยงมุม" เป็น 1

d = zeros( 3, 4, 5, 6 ); % Let's pretend this is a user input
nel = numel( d );
sz = size( d );
szargs = cell( 1, ndims( d ) ); % We'll use this with ind2sub in the loop
for ii=1:nel
    [ szargs{:} ] = ind2sub( sz, ii ); % Convert linear index back to subscripts
    if all( [szargs{2:end}] == szargs{1} ) % On the diagonal?
        d( ii ) = 1;
    end
end

+1 เพื่อแสดงตัวอย่างที่ดีว่า MATLAB แบ่งการพิมพ์แบบเป็ดได้อย่างไร
Phillip Cloud

1

คุณสามารถทำให้ฟังก์ชันเรียกซ้ำทำงานได้

  • ปล่อย L = size(M)
  • ปล่อย idx = zeros(L,1)
  • ใช้length(L)ความลึกสูงสุด
  • วน for idx(depth) = 1:L(depth)
  • หากความลึกของคุณคือlength(L)ดำเนินการองค์ประกอบอื่นเรียกฟังก์ชันอีกครั้งด้วยdepth+1

ไม่เร็วเท่าวิธีการแบบเวกเตอร์หากคุณต้องการตรวจสอบจุดทั้งหมด แต่ถ้าคุณไม่จำเป็นต้องประเมินส่วนใหญ่ก็สามารถประหยัดเวลาได้


1

โซลูชันเหล่านี้เร็วกว่าการใช้ (ประมาณ 11% numel)

for idx = reshape(array,1,[]),
     element = element + idx;
end

หรือ

for idx = array(:)',
    element = element + idx;
end

UPD. tnx @rayryeng สำหรับข้อผิดพลาดที่ตรวจพบในคำตอบสุดท้าย


ข้อจำกัดความรับผิดชอบ

ข้อมูลเวลาที่โพสต์นี้อ้างถึงไม่ถูกต้องและไม่ถูกต้องเนื่องจากมีการพิมพ์ผิดพื้นฐาน (ดูสตรีมความคิดเห็นด้านล่างรวมถึงประวัติการแก้ไข - ดูที่คำตอบนี้ในเวอร์ชันแรกโดยเฉพาะ) ข้อแม้ Emptor


1
1 : array(:)เทียบเท่ากับ1 : array(1). สิ่งนี้ไม่ได้ย้ำผ่านองค์ประกอบทั้งหมดซึ่งเป็นสาเหตุที่ทำให้เวลาทำงานของคุณรวดเร็ว นอกจากนี้ยังrandสร้างจุดลอยตัวตัวเลขและการทำเช่นนั้น1 : array(:)จะผลิตอาร์เรย์ที่ว่างเปล่าเป็นคำสั่งของคุณพยายามที่จะหาเวกเตอร์เพิ่มขึ้นกับค่าเริ่มต้นของการเป็นที่ 1 มีมูลค่าการสิ้นสุดเป็นจำนวนจุดลอยกับช่วงของ[0,1)พิเศษของ 1 ในการเพิ่ม ขั้นตอนที่ 1 ไม่มีเวกเตอร์ที่เป็นไปได้ดังกล่าวส่งผลให้เวกเตอร์ว่างเปล่า forลูปของคุณไม่ทำงานดังนั้นการอ้างสิทธิ์ของคุณจึงเป็นเท็จ -1 โหวต ขอโทษ.
rayryeng

@rayryeng คุณทำไม่ถูก อาร์เรย์ (:) ไม่เทียบเท่ากับ 1: array (1) reshape(...)มันชอบ
mathcow

@rayryeng matlab r2013a + linux - มันใช้งานได้! ;) ฉันเพิ่งรันโค้ดนั้นด้วย
mathcow

พิมพ์บนในพรอมต์คำสั่งของคุณหลังจากที่สร้าง1 : array(:) array คุณได้เมทริกซ์ว่างหรือไม่? ถ้าใช่แสดงว่ารหัสของคุณใช้ไม่ได้ ฉันออกจากการโหวตเนื่องจากคุณให้ข้อมูลเท็จ
rayryeng

@rayryeng ฉันเข้าใจ! ใช่คุณพูดถูกขอโทษสำหรับข้อพิพาทที่โง่เขลา
mathcow

-1

หากคุณมองลึกลงไปในการใช้งานอื่น ๆsizeคุณจะเห็นว่าคุณสามารถหาเวกเตอร์ขนาดของแต่ละมิติได้ ลิงค์นี้แสดงเอกสารประกอบ:

www.mathworks.com/access/helpdesk/help/techdoc/ref/size.html

หลังจากได้เวกเตอร์ขนาดแล้วให้ทำซ้ำบนเวกเตอร์นั้น อะไรทำนองนี้ (ขออภัยไวยากรณ์ของฉันเนื่องจากฉันไม่ได้ใช้ Matlab มาตั้งแต่สมัยเรียนมหาวิทยาลัย):

d = size(m);
dims = ndims(m);
for dimNumber = 1:dims
   for i = 1:d[dimNumber]
      ...

ทำให้เป็นไวยากรณ์ Matlab-legal จริงและฉันคิดว่ามันจะทำในสิ่งที่คุณต้องการ

นอกจากนี้คุณควรจะสามารถที่จะทำแบบลิเนียร์ทำดัชนีตามที่อธิบายไว้ที่นี่


ฉันไม่เห็นว่าการเรียงลำดับของลูปจะวนซ้ำองค์ประกอบทั้งหมดของเมทริกซ์ได้อย่างไร ตัวอย่างเช่นหากคุณมีเมทริกซ์ 3 คูณ 4 (มี 12 องค์ประกอบ) วงในของคุณจะวนซ้ำ 7 ครั้งเท่านั้น
gnovice

ควรวนซ้ำในแต่ละมิติของเมทริกซ์ วงนอกจะวนซ้ำเหนือมิติโดยวงในจะอยู่เหนือขนาดของมิตินั้น อย่างน้อยนั่นคือความคิด อย่างที่คนอื่น ๆ พูดกันถ้าสิ่งที่เขาต้องการคือแต่ละเซลล์การทำดัชนีซับจะดีที่สุด หากเขาต้องการที่จะวนซ้ำในแต่ละมิติเขาจะต้องทำสิ่งที่คล้ายกับสิ่งนี้
Erich Mirabal

ขอบคุณสำหรับการแก้ไข ลิงค์ของฉันค่อนข้างซับซ้อนและไม่สามารถทำงานได้อย่างถูกต้องโดยใช้วิธีการเชื่อมโยงตามปกติ นอกจากนี้เพื่อขยายคำแถลงของฉัน: เขายังคงต้องทำการติดตามดัชนีอื่น ๆ อีกมากมาย (ใช้เหมือนตัวนับหรืออะไรทำนองนั้น) ฉันคิดว่าแนวทางของคุณหรือแอนดรูว์น่าจะง่ายกว่าสำหรับสิ่งที่ฉันคิดว่าเขาพยายามทำ
Erich Mirabal

-1

คุณต้องการจำลอง n ที่ซ้อนกันสำหรับลูป

การวนซ้ำผ่านอาร์เรย์ n-dimmensional สามารถมองเห็นได้ว่าเป็นการเพิ่มจำนวน n- หลัก

ในแต่ละมิติเรามีตัวเลขมากพอ ๆ กับความยาวของมิติ

ตัวอย่าง:

สมมติว่าเรามีอาร์เรย์ (เมทริกซ์)

int[][][] T=new int[3][4][5];

ใน "สำหรับสัญกรณ์" เรามี:

for(int x=0;x<3;x++)
   for(int y=0;y<4;y++)
       for(int z=0;z<5;z++)
          T[x][y][z]=...

ในการจำลองสิ่งนี้คุณจะต้องใช้ "สัญกรณ์ตัวเลข n หลัก"

เรามีตัวเลข 3 หลักโดยมี 3 หลักสำหรับตัวแรก 4 ตัวที่สองและ 5 สำหรับหลักที่สาม

เราต้องเพิ่มจำนวนเราจึงจะได้ลำดับ

0 0 0
0 0 1
0 0 2    
0 0 3
0 0 4
0 1 0
0 1 1
0 1 2
0 1 3
0 1 4
0 2 0
0 2 1
0 2 2
0 2 3
0 2 4
0 3 0
0 3 1
0 3 2
0 3 3
0 3 4
and so on

คุณจึงเขียนโค้ดเพื่อเพิ่มจำนวน n หลักได้ คุณสามารถทำได้ในลักษณะที่คุณสามารถเริ่มต้นด้วยค่าใด ๆ ของตัวเลขและเพิ่ม / ลดตัวเลขด้วยตัวเลขใดก็ได้ ด้วยวิธีนี้คุณสามารถจำลองแบบซ้อนสำหรับลูปที่เริ่มต้นที่ไหนสักแห่งในตารางและไม่จบที่จุดสิ้นสุด

นี่ไม่ใช่เรื่องง่ายเลย ฉันไม่สามารถช่วยกับสัญกรณ์ matlab ได้

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.