Why are the variables declared in this WHILE loop not getting modified in the FOR loop?

4 views (last 30 days)
So, I'm helping a friend put together a function that essentially carries out a tour-de-wino style random walk. What it should do is run "walks" number of random walks, and in each walk, take "steps" number of steps of step size "step" until it hits the boundaries (the lines that form a square that starts at (0,0) and ends at (1,1). Unfortunately, it does not do this--in fact, it doesn't seem to modify the coordinates of the wino at all! Why is that?
the code is as follows (slightly reformatted to display properly in the post):
function [count] = random_walk( step, walks, steps )
% Only works for domains from 0 to 1, in steps of powers of 10
count = zeros(1/step, 4);
fail = 0;
j = 0;
while j < walks
locumx = 0.5;
locumy = 0.5;
directions = randi(4,1, steps);
for i = [1:steps+1]
if (i == (steps+1)) && ((locumx ~= 1) || (locumx ~= 0)) || ((locumy ~= 1) || (locumy ~= 0))
%j = j-1;
fail = fail +1;
break
elseif (i == (steps+1)) && ((locumx == 1) || (locumx == 0)) || ((locumy == 1) || (locumy == 0))
display(num2str(j+1), 'th walk completed successfully')
break
else
if directions(i) == 1
locumy = locumy + step;
if locumy == 1
count(locumx/step,1) = count(locumx/step,1) + 1;
break
end
elseif directions(i) == 2
locumy = locumy - step;
if locumy == 0
count(locumx/step,2) = count(locumx/step,2) + 1;
break
end
elseif directions(i) == 3
locumx = locumx + step;
if locumx == 1
count(locumy/step,3) = count(locumy/step,3) + 1;
break
end
elseif directions(i) == 4;
locumx = locumx - step;
if locumx == 0
count(locumy/step,4) = count(locumy/step,4) + 1;
break
end
else
display('drats')
end
end
end
j = j+1;
end
dumb = num2str(fail);
display(strcat('This function failed this many walks: ', dumb))
display(locumx, 'x')
display(locumy,'y')
end
  2 Comments
Jan
Jan on 6 Apr 2015
count = zeros(1/step, 4);
This works only if step|is smaller equal 1 and if |1/step is an integer without rounding.
for i = [1:steps+1]
This loop has 1 iteration only with i==1, because step must be smaller then 1.

Sign in to comment.

Accepted Answer

Stephen23
Stephen23 on 4 Apr 2015
Edited: Stephen23 on 6 Apr 2015
Based on the comment given to my first answer: "take steps in 1 of 4 directions", it seems that the direction should be limited to orthogonal directions only:
mxS = 20;
wlk = 10;
stp = 2.5 - randi(4,mxS,wlk);
drn = abs(stp)>1;
poX = cumsum([zeros(1,wlk); drn.*sign(stp)]);
poY = cumsum([zeros(1,wlk);~drn.*sign(stp)]);
plot(poX,poY,'x-')
axis equal
Produces this figure:
And of course we can still find out which steps are inside the boundary:
>> box = 5;
>> idx = abs(poX)<box & abs(poY)<box
>> idx(~cumprod(+idx,1)) = false
and which walks reached the boundary:
>> any(~idx)
ans =
1 0 0 0 0 0 0 1 0 0
and in how many steps:
>> sum(idx,1)
ans =
15 21 21 21 21 21 21 12 21 21
Which tells us that only two of the ten walks reached the boundary, and the shortest walk was twelve steps. And of course we can easily print summaries, etc:
>> fprintf('%i paths did not reach the boundary\n',sum(any(~idx)))
2 paths did not reach the boundary
No loops: Faster, Neater, Easier!

More Answers (2)

Michael Haderlein
Michael Haderlein on 1 Apr 2015
Edited: Michael Haderlein on 1 Apr 2015
Check line 11:
if (i == (steps+1)) && ((locumx ~= 1) || (locumx ~= 0)) || ((locumy ~= 1) || (locumy ~= 0))
You have 5 conditions here which group to 3 conditions:
(i == (steps+1))
&&
((locumx ~= 1) || (locumx ~= 0))
||
((locumy ~= 1) || (locumy ~= 0))
Of course, conditions 2 and 3 are always true (locumx cannot be both 1 and 0). So you have something like false && true | | true. Such statements are evaluated left to right, thus you have
false && true -> false;
false | | true -> true.
Therefore, it will always fail in the very first step. I guess you wanted to have the brackets a bit different.
Another remark, you always compare the positions locumx/locumy with zero and one. However, most likely the step will not exactly hit zero/one. Better compare with >=1 and <=0.
  3 Comments
Michael Haderlein
Michael Haderlein on 1 Apr 2015
Well, I doubt it will ever hit 0 or 1. See my following example:
>> stepsize=1e-4;
>> pos=.5;
>> while pos(end)<=1
pos(end+1)=pos(end)+stepsize;
end
>> pos([end-2:end])
ans =
0.9999 1.0000 1.0001
>> 1-pos(end-1)
ans =
5.5067e-14
>> any(pos==1)
ans =
0
(Please don't take this as good programming style, I just want to keep the example simple as possible)
You see, I just add 1e-4 in a loop and store all positions. Also, the second-last value seems to be 1. However, it's not and none of the pos is exactly 1. In floating point arithmetics, you better don't compare with equality. If equality is necessary, use a domain (x>1-1e-4 && x<1+1e-4 for instance).
Keane Sanders
Keane Sanders on 4 Apr 2015
Ah, I had forgotten about floating point error. I still can't use just "> 1"--I need it to stop at 1, so I guess I'll have to have something like "> 1 && < 1.01"
But until I can figure out why locumx et locumy aren't being modified and stored, we'll never know.

Sign in to comment.


Stephen23
Stephen23 on 1 Apr 2015
Edited: Stephen23 on 22 May 2015
Rather than doing this in a loop, which will be very slow and inefficient use of MATLAB, you should learn how to write vectorized code, which will be much faster, neater, and less buggy than doing this in a loop.
For example this is the complete code needed to generate and plot some random walks with a constant step-size and a random direction:
mxS = 20;
wlk = 10;
ang = 2*pi*rand(mxS,wlk);
poX = cumsum([zeros(1,wlk);cos(ang)]);
poY = cumsum([zeros(1,wlk);sin(ang)]);
plot(poX,poY,'x-')
axis equal
Where each column of poX and poY are the x and y positions for one walk. The plot look like this:
This has a step-size of one and the walks all start at the origin: doing this makes detecting the bounding box-intersection much easier as the bounding box can then be a simple positive value allowing a basic logical conditional to detect that the path is still inside the bounding box:
>> box = 5;
>> idx = abs(poX)<box & abs(poY)<box;
>> idx(~cumprod(+idx,1)) = false;
where the values of idx indicate whether the step is inside the box, and again each column corresponds to one walk. We can then use any to check which walks reached the bounding box:
>> any(~idx,1)
ans =
1 1 0 0 0 0 1 1 0 1
This tell shows clearly that five of these ten random trials trials reached the box. You can count how many steps were required by summing these indices:
>> sum(idx,1)
ans =
12 11 21 21 21 21 15 20 21 18
The shortest path to the boundary was only eleven steps. Note how the values correspond to the logical values above.
No loops: Faster, Neater, Easier!
  4 Comments
Keane Sanders
Keane Sanders on 4 Apr 2015
Not to sound ungrateful, but your code doesn't do what I need it to do, and at the moment, I'm not seeing a good way to vectorize the code I have to both A) take steps in 1 of 4 directions, and B) stop at the specified boundaries. It's a useful suggestion, but it doesn't seem to help me with the problem I have
Stephen23
Stephen23 on 4 Apr 2015
Edited: Stephen23 on 4 Apr 2015
@Keane Sanders: as you only provided some non-functioning code and no specifications I had to make some assumptions about what you were actually trying to achieve. If you provided some clear specifications then it would be possible to show you a neat and robust way to code this problem. Saying that it "doesn't do what I need it to do" is fine, but in order to be able to give you the best advice we also need a description of what your aim is: you know what the code is supposed to do, but we don't (as we can't read minds).
Your comment indicates that you only wish to consider directions parallel to the axes, in this case you could consider my second answer...

Sign in to comment.

Categories

Find more on Graphics Performance in Help Center and File Exchange

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!