You might consider to use a tuple of directions, something like this:
directions = ((1,0), (0,1), (-1,0), (0,-1))
In your while loop, you can do:
direction = directions[np.random.randint(4)]
A += direction[0]
B += direction[1]
If you switch from tuple to numpy arrays (direction and your current position), you could also use numpy.add which might be faster. If this really improve performence you have to measure. To do this, you might use a seed for your random generator, to get reproducible code.
The border check might be faster with NumPy too, using abslolut - if its ok, to change the behavior that way (your praticles wont "clue" at the border, but bounce back).
Precalculation
You could go a step further, by precalculating all posible paths for several steps.
# each direction has same propability - making things easier
# you could simply create a list with an entry for each path of a tree diagram,
# as each path has the same probability
def add_one_step(steps: list):
random_step = [(1,0), (0,1), (-1,0), (0,-1)]
if steps == []:
return random_step
result = []
for i, steps_entry in enumerate(steps):
for step in random_step:
a = steps_entry[0]+step[0]
b = steps_entry[1]+step[1]
result.append((a, b))
return result
def get_multiple_steps(n=5):
final_directions = []
while n > 0:
final_directions = add_one_step(final_directions)
n -= 1
return final_directions
# be careful about how many steps to precalculate! The list lengths go with 4**n
precalculated_steps = []
for i in range(12):
precalculated_steps.append(get_multiple_steps(i))
You could use such precalculated values, to do several steps in one go.
n = 10
direction = precalculated_steps[n][np.random.randint(4**n)]
A += direction[0]
B += direction[1]
Thats the most simple aproach. If you want to go to higher n values, you have to think about how to reduce precalculation time (this is just a simple brute force calculation of all paths), and how to safe each result only once (and how many times it occurs).
The tricky part is your border and the stick. You have to chose a fitting matrix size depending on your current distance to border and stick.
Outlook - binomial distribution and minimal distance matrix
If you understood the above and really want to speed up your code, I would suggest to look at:
- Binomial distribution
- you can split stepping in two seperate movements (x and y) - than your radom walk over n steps is simply a binominal distribution
- with this, you could precalc a quadratic matrix for each n = 1 .. 249 with the probability to reach a position
- precalc minimal distance from sticking and border for each position; you have to update this only when a particle finally sticks - and you only need to check for each position, if this stick-position is closer than the previous value; This matrix is used to get the max number of steps you can do in one go.
By using the minimal distance matrix to determine the maximal number of steps allowed to do, and then using the fitting precalculated stepping matrix the solution should be ready in minutes or seconds. If you can be more lax with the border condition, it would speed up the algorithm even more.
Errors
After understanding what your code is intended to do, I think your for-loop over all paricles is broken.
First:
pos = np.random.randint(0, len(arr)) # will set pos to 0 or 1
# should be:
pos = np.random.randint(0, arr[0])
# or better
random_pos_x = np.random.randint(0, arr[0])
random_pos_y = np.random.randint(0, arr[1])
Also, you only start particles at the upper and left border?
walk(pos,-1,canvas) # why -1?
...
walk(-1,pos,canvas) # why -1?
# should be:
walk(random_pos_x ,arr[1]-1, canvas)
walk(arr[0]-1, random_pos_y, canvas)
The name arr is an example for a bad name, because it hints to "array" (thus it was hard to spot the mistake above). Better would be samething like area. Or use h and w (personally I would prefer to name thus as height and width; thats easier to read in most cases)