To begin with, you're now computing fp as the total of all particles that have crossed the trap. This number must be asymptotic to n at some point. The derivative of the cumulative total, which is the number of particles traversing the trap per unit time, is what you're looking for.

In the second loop, a very simple adjustment is required. Change the condition below.

if x[i, j] == -10:
fp[i, j] = fp[i, j - 1] + 1
else:
fp[i, j] = fp[i, j - 1]

till

fp[i, j] = int(x[i, j] == -10)

Because booleans are already subclasses of int, and you only want 1 or 0 recorded at each step, this works. It's the same as eliminating fp[i, j - 1] from the RHS of both branches of the if statement's assignments.

The plot is as follows:

This appears odd, but hopefully you can already see a glimmer of the plot you're looking for. The low density of particles crossing the trap is what makes it odd. You may improve the appearance by increasing particle density or smoothing the curve with a running average, for example.

Let's start with the smoothing method using np.convolve:

y = np.convolve(fp.sum(0), np.full(11, 1/11), 'same')
z = np.convolve(fp.sum(1), np.full(101, 1/101), 'same')
plt.plot(s, y)
plt.plot(s, z)
plt.legend(['Raw', 'Window Size 11', 'Window Size 101'])

Except for some normalization concerns, this is starting to resemble the curve you're looking for. Smoothing the curve is certainly useful for estimating the plot's form, but it's definitely not the greatest method for actually displaying the simulation. One issue you may see is that the averaging causes the values at the left end of the curve to become very skewed. You can significantly alleviate this by modifying how the window is interpreted or by using a different convolution kernel, but edge effects will always exist.

You'll want to increase the number of samples to significantly improve the quality of your results. I would propose that you optimize your code a little before proceeding.

The design of the trap allows you to decouple the two directions, therefore optimization #1, as mentioned in the comments, is that you don't need to create both x and y coordinates for this problem. Instead, you have a 1/5 chance of stepping in the -x direction and a 1/5 chance of stepping in the +x direction.

The second optimization is solely for speed. You can do everything in a purely vectorized manner rather than running multiple for loops. I'll also provide an example of the new RNG API, which I've found to be far faster than the legacy API.

The third optimization is to increase readability. Without extensive documentation, names like n simulations, n, and fp are not very informative. To make the code self-documenting, I'll rename a few things in the example below:

particle_count = 1000000
step_count = 1000
# -1 always floor divides to -1, +3 floor divides to +1, the rest zero
random_walk = np.random.default_rng().integers(-1, 3, endpoint=True, size=(step_count, particle_count), dtype=np.int16)
random_walk //= 3 # Do the division in-place for efficiency
random_walk.cumsum(axis=0, out=random_walk)

This excerpt calculates random walk as a sequence of steps, first utilizing the smart floor division method to verify that the ratios for each step are exactly 1/5. Cumsum is then used to integrate the stages in place.

Using masking, it's simple to locate the spot when the walk first crosses -10:

steps = (random_walk == -10).argmax(axis=0)

The first occurrence of a maximum is returned by argmax. Because the array (random walk == -10) is made up of booleans, the index of the first occurrence of -10 in each column will be returned. Particles that never reach -10 within simulation count steps will have all False values in their column, resulting in argmax returning 0. This is simple to filter out because 0 is never a valid number of steps.

A histogram showing the number of steps will provide you with the information you require. The fastest approach to compute a histogram with integer data is to use np.bincount:

histogram = np.bincount(steps)
plt.plot(np.arange(2, histogram.size + 1), hist[1:] / particle_count)

The number of particles that never made it to -10 within step count steps is the first element of the histogram. Unless argmax is used, the first 9 entries of the histogram should always be zero. Because histogram[0] nominally shows the count after one step, the display range is shifted by one.

Ignite Your Future with Machine Learning Training!