I'm writing code for an epidemiology class in which I'm supposed to have a random walker take 100 steps and determine the probability that it will become infected by walking over a contaminant. The grid on which the walker walks is 100x100. The walker is to begin at a random location on the grid.

My partner has done the bulk of the work so I'm trying to finish it up but I just can't seem to figure out how to limit the grid size. How do I make it so that the walker will either not make a move if the step would land it outside of the grid, or roll it over the other other side of the grid if it steps over the border?

Please don't think I'm asking you to do my work for me. I want to learn how to do this as I may use this again in the future.

# use the perl open function to open the files open(FILE_OUT, $file_out) or die "Could not open write $file_out, program halting."; open(CONT_OUT, $cont_out) or die "Could not open $cont_out, program halting.";

# Number of steps in each walk $NO_OF_STEPS = 100;

# # Number of runs or times the random walk is done $NO_OF_RUNS = 100;

I have not had a chance to look at your code yet. I like the idea of "roll over". In that case your walker would be an ant walking on a contaminated doughnut. (Really, imagine your number grid laid out on the doughnut. One coordinate goes from 99 back to zero when he steps through the hole. The other coordinate goes from 99 back to zero when he finishes a complete lap around the doughnut without going through the hole.)

Your original code has serious errors. First, lets agree on exactly what we mean by a "random walk" on a two dimensional grid. I believe that each step should be to one of the four positions (forward, backward, right, or left) with equal probability for each. Diagonal steps are not allowed. It is not clear what you intend.

Your use of hashes( %hash_walker_pos and %hash_cont_pos) to store lists of positions will not work. Whenever an x-position is repeated, the existing key will be reused. The existing y-value will be overwritten. The proper data structure depends in part on the expected length of these lists. Good Luck, Bill

Thanks, Chris. This is way better than what my partner and I wrote. I'll see what I can do to implement it in a way that I understand. The only problem I see with the application of your code to our "experiment" is that it isn't repeatable. That's the reason I was using srand. So I see that you randomly assigned 1s and 0s to each grid position and made the 1s contaminated spaces. Where is the code for the walker? Finally, how would one make this repeatable? Thanks again!

Quote

Your original code has serious errors. First, lets agree on exactly what we mean by a "random walk" on a two dimensional grid. I believe that each step should be to one of the four positions (forward, backward, right, or left) with equal probability for each. Diagonal steps are not allowed. It is not clear what you intend.

Your use of hashes( %hash_walker_pos and %hash_cont_pos) to store lists of positions will not work. Whenever an x-position is repeated, the existing key will be reused. The existing y-value will be overwritten. The proper data structure depends in part on the expected length of these lists. Good Luck, Bill

Correct. We only have up, down, left and right. The original code that our TA gave as an example included some code, which I think you are referring to. I added it at the bottom of this reply.

Someone else (at least I think it is someone else, perhaps it is you afterall) posted a question on exactly (100x100 grid, random walker, probability of contamination) in the last 3 to 4 days on this forum.

The variable @grid could have more accurately named @path. If I understand the problem correctly, you want a (100 x 100) grid that remains constant from run to run and have random walks through that grid. (The @path var is @grid in the original code).

push @walks, scalar grep $_, @path[0 .. $steps - 1];

The line above takes $steps steps. The grid in effect remains constant, its only the path that changes for each run.

I did a similar problem in school in C++ and I can't find it now. It used a grid of letters and I had to find a (given) list of words in it. We could move in any direction, even diagonally, to find the words.

Have you tried it and does it give reasonable results? I would have to think about this more and then post back if I discover the error or correctness in the program.

I think it is how you look at it. The path is changing from run to run - it is random. The number of contaminated cells does not change from run to run.

Hope that helps and that I've understood the problem.

Just doing the math, if there are 100 contaminated cells out of 10,000 (in a 100 X 100 grid), then 1% of them are contaminated. The probability of taking 100 steps without stepping on a contaminated cell would be .99 to the hundreth power, (.99 ^ 100). That gives ~0.36 and I get results close to that when running my program (when using those parameters).

Update: My model solution doesn't account for the possibilty of revisiting a cell. I think the answer lies in creating a grid to randomly walk upon - as your problem originally suggested.

BillKSmith pointed out the problem with the way you are using the hashes. A possible solution would be to use a compound key made from the xpos and ypos.

Code

my $key = "$xpos_cont$;$ypos_cont";

The 'separator' between the xpos and ypos is '$;'. It is safe to use that perl builtin variable for your problem because it is rarely if ever used by perl.

Then you could create a hash element like this.

Code

my $key = "$xpos_cont$;$ypos_cont"; $hash_cont_pos{$key}++;

Using srand in a program is almost always wrong as it influences all rand calls. I understand that you want to fix the grid the same from run to run. I think a solution would be to create the grid in one program, store it and then in the random_walk program, recall the hash from where it is stored and do the random walks with the grid that won't change from run to run. Only the steps taken would then be random. A Perl module that would store the hash is Storable. It is included in newer perl distributions (since perl version 5.7.3), so you would not need to install it if you are using a newer perl.

The grid creator could be like the following program. (redo starts at the first statement in the for block without incrementing the next '$k'). It 'redoes' the loop if the compound key has already been seen. If you have 100 contanimated cells, then this would generate all 100. Without the redo, you could have a duplicate compound key, (same xpos and ypos) and would not the have 100 infected cells.

The modulo operator here, (($xpos - $steps) % $range_contx), is to 'roll over' the position when they would otherwise stray out of the grid (when adding or subtracting from the current xpos ypos). (Can the steps be 1 to 6? That's how this is setup.)

There are a lot of issues here, but I think you can make your approach doable with the suggestions I have given.

The code in the original post already writes the contamination data to a file. Why not read this file back in to reuse the contamination map.

Rather than the hash, I would recommend implementing the map as a 100x100 array of logic values. If memory were an issue, we could use an array of bit vectors, but an array of ordinary scalars is much easier to implement. Should we really care that the matrix is quite sparse?

The second hash is not needed at all because there is no need to store all the steps of a walk. If a future need arises, the data is in the other output file. Good Luck, Bill