跳至內容

Octave 程式設計教程/迴圈和條件

來自 Wikibooks,開放世界中的開放書籍

迴圈用於重複執行一段程式碼,具體次數根據迴圈型別而定,可能是已知的,也可能是未知的。使用迴圈,你可以繪製一些漂亮的圖案,例如分形和隨機點繪製的形狀。

for 迴圈

[編輯 | 編輯原始碼]

我們使用 for 迴圈來重複執行一段程式碼,針對已知值列表。例如,我們將計算一個值列表的平均值。平均值由下式計算得出

我們設定一個包含一些值的向量

octave:1> x = [1.2, 6.3, 7.8, 3.6];

並使用以下程式碼計算平均值

octave:2> sum = 0;
octave:3> for entry = x,
octave:4>   sum = sum + entry;
octave:5> end;
octave:6> x_mean = sum / length(x)

第 2 行:將 sum 設定為 0。
第 3 行:對於 x 中的每個值,將其分配給 entry。
第 4 行:將 sum 增加 entry。
第 5 行:當 x 中沒有更多成員時,結束 for 迴圈。
第 6 行:將 sum 的最終值除以 x 的長度,並將結果分配給 x_mean。

待辦:提供一個更好的示例並解釋程式碼。


一般來說,我們編寫 for 迴圈的格式如下

for variable = vector
   ...
end

... 代表程式碼塊,該程式碼塊對於 vector 中的每個值都會執行一次。

示例:謝爾賓斯基三角形

[編輯 | 編輯原始碼]

謝爾賓斯基三角形是一種分形,可以透過一個非常簡單的演算法生成。

  1. 從等邊三角形的頂點開始。
  2. 隨機選擇三角形的一個頂點。
  3. 移動到當前位置與所選頂點之間中點的點。
  4. 從步驟 2 開始重複。

透過遵循此過程繪製你訪問的點,可以生成以下影像。

生成該分形的程式碼如下所示。請注意,此程式碼使用一個非常簡單的 for 迴圈來生成分形(for i = 1:N ; ... ; end)

axis ([-1, 1, -0.75, 1.25], "square");
figure(1, "visible", "off");                          % no plotting window
hold on;

% defining the vertices of an equilateral triangle (symmetric to y-axis)
V = [ 0, 1;                                           % top vertex
     cos( (pi/2)+(2*pi/3) ), sin( (pi/2)+(2*pi/3) );  % left vertex
     cos( (pi/2)-(2*pi/3) ), sin( (pi/2)-(2*pi/3) )   % right vertex
     ];

r = floor(3*rand(1)+0.9999999999);                    % integer random number in [1:3]
x = [ V(r,1), V(r,2) ];                               % initializing x on random vertex

for i = 1:1000 % !!! 100000 => time=130m57.343s
  r = floor(3*rand(1)+0.9999999999);                  % integer random number in [1:3]
  x = ( x+V(r,[1:2]) )./2;                            % halfway, current to random vertex
  plot(x(1),x(2), ".");
endfor

print -dpng "sierpinski_m.png";

注意事項

[編輯 | 編輯原始碼]

出於效能原因,最好在 'for' 迴圈中執行儘可能少的任務,並且應儘可能將 plot 這樣的圖形操作移到迴圈之外。透過簡單地將所有 x 值儲存在矩陣中,然後像下面這樣繪製,程式碼生成此圖的執行時間會縮短到幾秒,即使迭代超過 100,000 個元素(上面的程式碼警告不要這樣做)。

axis ([-1, 1, -0.75, 1.25], "square");
figure(1, "visible", "off");                          % no plotting window
hold on;

% defining the vertices of an equilateral triangle (symmetric to y-axis)
V = [ 0, 1;                                           % top vertex
     cos( (pi/2)+(2*pi/3) ), sin( (pi/2)+(2*pi/3) );  % left vertex
     cos( (pi/2)-(2*pi/3) ), sin( (pi/2)-(2*pi/3) )   % right vertex
     ];

r = floor(3*rand(1)+0.9999999999);                    % integer random number in [1:3]
x(1,:) = [ V(r,1), V(r,2) ];                               % initializing x as a matrix this time.

for i = 2:100000 % Safe now: 100000 => time=7.85346s
  r = floor(3*rand(1)+0.9999999999);                  % integer random number in [1:3]
  x(i,:) = ( x(i-1,:)+V(r,[1:2]) )./2;                % Add each newly calculated x value to the matrix
endfor

plot(x(:,1),x(:,2), ".");                             % plot the entire matrix just once
print -dpng "sierpinski_m.png";

透過提前將 x 初始化為完整最終大小的矩陣,可以在現代硬體上將處理時間進一步縮短到 1 或 2 秒。一般來說,如果迴圈可以用向量化替換,那麼就應該這樣做。

  1. 編寫一個指令碼,對前 N 個整數求和。你可以使用公式 檢查你的結果。
  2. 編寫一個指令碼,執行與 linspace 函式相同的功能。它應該從某個值 xstart 開始,在 xstop 處結束,並建立一個向量,其中包含 N 個從 xstartxstop 均勻分佈的值。你可以使用 zeros 函式來建立一個填充了零的正確大小的向量。使用 help zeros 瞭解該函式的工作原理。

while 迴圈

[編輯 | 編輯原始碼]

while 迴圈也會重複執行一段程式碼多次,但停止執行取決於一個邏輯條件。例如

x = 1.0;
while x < 1000
   disp(x);
   x = x*2;
endwhile

x 乘以 2,直到其值超過 1000。這裡,x < 1000 是迴圈的條件。只要條件成立(為真),迴圈就會繼續執行。一旦條件不成立,迴圈就會終止,並執行迴圈後的第一條指令。

while 迴圈的一般形式如下

while condition
   ...
endwhile
  1. 編寫一個指令碼,計算最小的正整數 n,使得 對於某些實數 ab。(也就是說,找到 a 的最小冪,該冪至少為 b。)使用 log 函式被認為作弊。

示例:曼德勃羅特分形

[編輯 | 編輯原始碼]

曼德勃羅特集合是另一個分形,它是透過檢查複數變大的速度來生成的。對於每個複數 c

  1. 開始。
  2. 找到第一個 i,使得 .

我們將記錄所有這些i值,併為每個值分配一種顏色。這用於生成類似於此的影像。

您可以從Mandelbrot.m下載生成此分形的程式碼。請注意,有一個 while 迴圈(在一些 for 迴圈內),用於測試複數z的模量是否小於2

while (count < maxcount) & (abs(z) < 2)
   ...
endwhile

while 迴圈中的第一個條件檢查我們是否沒有執行太多迭代。對於一些c值,如果我們讓它繼續,迭代將永遠持續下去。

另請參閱Christopher Wellons的另一個版本

do...until語句

[edit | edit source]

這些迴圈與 while 迴圈非常相似,因為它們根據給定條件為真還是假來繼續執行。但是,whiledo...until 迴圈之間有一些重要的區別。

  1. while 迴圈的條件位於迴圈的開頭;
    do...until 迴圈的條件位於迴圈的結尾。
  2. while 迴圈只要條件為真就重複;
    do...until 迴圈只要條件為假就繼續。
  3. while 將執行 0 次或更多次(因為條件位於開頭);
    do...until 迴圈將執行 1 次或更多次(因為條件位於結尾)。

do...until 迴圈的一般形式是

do
   ...
until condition

練習

[edit | edit source]

編寫一個指令碼,計算兩個正整數的最大公約數(GCD)。您可以使用歐幾里得演算法來實現。

挑戰

[edit | edit source]

編寫一個指令碼,生成均勻分佈的隨機數對(a,b)

  1. 在圓盤(下面的第一個影像);
  2. 如下面的第二個影像所示
File:Octave uniform random circle.png File:Octave uniform random challenge.png

breakcontinue 語句

[edit | edit source]

有時需要在迴圈執行的中間某個位置停止迴圈,或者繼續到 for 迴圈中的下一個值,而無需為當前值執行迴圈程式碼的其餘部分。這就是 breakcontinue 語句有用的地方。

以下程式碼演示了 break 語句的工作原理。

total = 0;
while true
   x = input('Value to add (enter 0 to stop): ');
   if x == 0
      break;
   endif
   total = total+x;
   disp(['Total: ', num2str(total)]);
endwhile

如果沒有 break 語句,迴圈將永遠繼續執行,因為 while 迴圈的條件始終為真。break 允許您跳過迴圈的末尾(到 endwhile 之後的語句)。

break 語句可以用於任何迴圈:forwhiledo...until

continue 語句也從迴圈內部跳轉,但返回到迴圈的開頭,而不是到迴圈的末尾。在一個

  1. for 迴圈中,向量中的下一個值將被分配給 for 變數(如果有的話),並使用該值重新啟動迴圈;
  2. while 迴圈中,將重新測試迴圈開頭的條件,如果條件仍然為真,則繼續迴圈;
  3. do...until 迴圈中,將測試迴圈末尾的條件,如果條件仍然為假,則從開頭繼續迴圈。

例如,以下程式碼將用 1 填充方陣的下三角部分,其餘部分用 0 填充。

N = 5;
A = zeros(N); % Create an N x N matrix filled with 0s

for row = 1:N
   for column = 1:N
      if column > row
         continue;
      endif
      A(row, column) = 1;
   endfor
endfor

disp(A);

請注意,內部 for 跳過(繼續)為 A 的條目分配 1 的程式碼,只要列索引大於行索引。

if 語句

[edit | edit source]

if 語句的一般形式是

if condition1
   ...
elseif condition2
   ...
else
   ...
endif

如果 condition1 評估為真,則執行緊隨 if 之後的塊中的語句。如果 condition1 為假,則檢查下一個條件(elseif 中的 condition2),如果為真則執行其語句。您可以擁有任意數量的 elseif 語句。如果所有條件都評估為假,則執行 else 之後的最後一組語句。請注意,if 語句的 elseifelse 部分是可選的。

以下都是有效的 if 語句

% Take the log of the absolute value of x
if x > 0
   y = log(x);
elseif x < 0
   y = log(-x);
else
   disp("Cannot take the log of zero.");
endif

x = input("Enter a value: ");
if x > 0
   disp("The number is positive");
endif

if x < 0
   disp("The number is negative");
endif

if x == 0
   disp("The number is zero");
endif

示例:分形蕨

[edit | edit source]

此演算法還不完整。請檢視可從http://www.mathworks.com/matlabcentral/fileexchange/loadFile.do?objectId=4372&objectType=file獲得的 .m 檔案。

右側的影像可以使用以下演算法生成

  1. Let x1 and y1 be random values between 0 and 1.
  2. Choose one of the linear transformations below to calculate (xi+1, yi+1) from (xi, yi):
        1. xi+1 = 0
           yi+1 = 0.16yi
        2. xi+1 = 0.20xi − 0.26yi
           yi+1 = 0.23xi + 0.22yi + 1.6
        3. xi+1 = −0.15xi + 0.28yi
           yi+1 = 0.26xi + 0.24yi + 0.44
        4. xi+1 = 0.85xi + 0.04yi
           yi+1 = −0.04xi + 0.85yi + 1.6
     The first transformation is chosen if probability 0.01, the second and third with probability 0.07 each and the fourth with probability 0.85.
  3. Calculate these values for i up to at least 10,000.

您可以下載生成此分形的程式碼為 fracfern.m(目前已停用)。


返回Octave 程式設計教程索引

華夏公益教科書