Week 11 - Programming IV Today 1. Vectorization to simplify programs 2. Structured software Definition: array operations versus loops 1.masks (indicator arrays) math with masks addressing with masks 2.array functions
Structured Programming Manage complexity Readable Testable Reusable Maintainable
Structured Programming Hierarchical Modules are divided into smaller and smaller submodules (functions work well as modules) functions contain functions Modular Simple independent modules with well defined inputs and outputs Locality Minimize inputs and outputs Generate values inside the module where possible
Example 1 – clipping a time plot to [-1,1]: Time (sec.)
Method 1 – using a loop and branch: for k = 1:length(y) if y(k) > 1 y(k) = 1; elseif y(k) < -1 y(k) = -1; end
Method 2 – using mask math: p = y>1; n = y<-1; y = (1-p).*(1-n).*y + p - n; or p = y>1; n = y<-1; y = ~p.*~n.*y + p - n; “mask” arrays to indicate locations with relevant characteristics use masks either mathematically or logically
Method 3 – using mask addressing: p = y>1; or n = y<-1; y(p) = 1;y(y>1) = 1; y(n) = -1;y(y<-1) = -1; mask determines which elements to change
Example 2 – reorder quiz scores (by row):
Method 1 – loop and branch: for k = 1:size(score,1) if score(k,1) < score(k,2) score(k,:) = score(k,[2 1]); end size with second argument 1 returns the number of rows
Method 2 – using mask addressing: flip = score(:,1)<score(:,2); score(flip,[1 2]) = score(flip,[2 1]) identify which rows to swap and reverse them
Example 3: sorting (last week) Given N numbers (x 1,…x N ), sort them into increasing order
start done Original program employed nested loops:
function array = simpsort(array) % function to sort from smallest to largest for k = 1:length(array)-1 loc = k; for k2 = k:length(array) if array(k2)<array(loc) loc = k2; end if loc ~= k array([k,loc ]) = array([loc,k]); end locate the index (loc) of the smallest value swap values, if necessary
Method 2 – using array functions function array = simpsort(array) % vectorized version of simpsort for k = 1:length(array)-1 [ val, loc ] = min( array(k:end) ); array([k,loc+k-1]) = array([loc+k-1,k]); end note offset to loc of k-1 since min( ) works on a subarray
Example 4: matrix sorting (HW 7-15) Sort a 4 by 7 matrix
Method 1 – brute force loop and branch: for r1 = 1:4 for c1 = 1:7 val = inf; for r2 = 1:4 for c2 = 1:7 end result(r1,c1) = val; mat(rloc,cloc) = inf; end if mat(r2,c2) < val val = mat(r2,c2); rloc = r2; cloc = c2; end outer loops (r1,c1) build result inner loops (r2,c2) locate minimum after finding minimum (val, rloc, cloc) “erase” it using infinity
Method 2 – simpler loop and branch using min operator: for r = 1:size(mat,1) for c = 1:size(mat,2) [val,loc] = min(mat(:)); result(r,c) = val; mat(loc) = inf; end loops (r,c) still build result after finding minimum at (loc) erase it using infinity Note scalar addressing into mat minimum value and location identified by array operation Note mat(:)
Example 5: tic-tac-toe winner Find the winner in a 3-by-3 tic-tac-toe board. Assume representations: –Empty cell = 0 –X = +1 –O = – 1
Method 1 – brute force condition testing if board(1,:)==ones(1,3) disp('X won') elseif board(1,:)==-ones(1,3) disp('O won') elseif board(2,:)==ones(1,3) disp('X won') … elseif [board(1,1),board(2,2),board(3,3)] == … … end 16 checks in all Problems: tedious not directly expandable no help on next move
Method 2 – use loops: for k = 1:3 if board(k,:)==ones(1,3) | board(:,k)==ones(3,1) disp('X won') elseif board(k,:)== -ones(1,3) | board(:,k)== -ones(3,1) disp('O won') end plus tests for diagonals (no loop advantage here)…
Method 3 – use mask and array operations: cols = sum(board); rows = sum(board'); diag1 = trace(board); diag2 = trace(board(:,[3 2 1])) checks = [ cols, rows, diag1, diag2 ]; if any(checks == 3) disp('X won') elseif any(checks == -3) disp('O won') end –1,0,+1 representation is very useful with summing!
Other array functions simplify the diagonal tests: diag1 = sum(sum(board.*eye(3,3))); diag2 = sum(sum(board.*[0 0 1; 0 1 0; 1 0 0])); diag1 = sum(diag(board)); diag2 = sum(diag(fliplr(board))); fliplr = flips the array left to right (also see flipud) diag grabs just the diagonal elements
Summing allows looking for a potential win: if any(checks == 3) disp('X won') elseif any(checks == -3) disp('O won') elseif any(checks == -2) disp('X could win now!') elseif any(checks == 2) disp('O could win now') end
function drawgame %DRAWGAME draws the tic-tac-toe board x = [ ]; y = [ ]; fill(x,y, 'w'); % Use a white box background. hold on hx1 = [ 0 3 ]; hy1 = [ 1 1 ]; % Horizontal line at y = 1 hx2 = [ 0 3 ]; hy2 = [ 2 2 ]; % Horizontal line at y = 2 vx1 = [ 1 1 ]; vy1 = [ 0 3 ]; % Vertical line at x = 1 vx2 = [ 2 2 ]; vy2 = [ 0 3 ]; % Vertical line at x = 2 plot(hx1,hy1,'k',hx2,hy2,'k',vx1,vy1,'k',vx2,vy2,'k') axis off, axis square return
function [n, m] = getmove(player) %GETMOVE asks a tic-tac-toe player to pick a square. %GETMOVE draws an X on the tic-tac-toe square chosen if player == 1 and a O if player == 2. %GETMOVE returns the indices of the board square selected. [x y]= ginput(1); % Use cursor to pick a square. m = ceil(x); % m is the column index. It corresponds to x. n = 4 - ceil(y); % n is the row index. It corresponds to 4 - y. % Shift and invert so board does not appear up side down if player == 1 % 1 in the array board, X in figure text(ceil(x)-.5, ceil(y)-0.5, 'X', 'fontsize',20,'horizontalalignment','center', 'color',[0 1 0]) end if player == 2 % -1 in the array board, O in figure text(ceil(x)-0.5, ceil(y)-0.5, 'O', 'fontsize',20,'horizontalalignment','center', 'color',[1 0 0]) end return [n, m] = getmove(player)
% Lecture 11 demo board = zeros(3); drawboard player = 1; while any(~all(board)) % Keep going as long as any 0s remain. [n, m] = getmove(player) player = 3 - player; % player toggles between 1 and 2. board(n,m) = 3 - 2*player; % 3-2*player = either -1 or +1 % 3-2 = 1 if player = 1 and 3-4 = -1 if player = 2 end board
function winner = check4win(board) % CHECK4WIN checks the tic-tac-toe board for a win. winner = 0; if any(sum(board)==3) |any(sum(board')==3) | trace(board)==3 |trace(board(:,[3 2 1]))==3 winner = 1; end if any(sum(board)==-3) |any(sum(board')==-3) | trace(board)==-3 |trace(board(:,[3 2 1]))==-3 winner = 2; end return winner = check4win(board)