We have a 3D cylinder with a radius of $r = 1$ and a height of $h = 2$. 

[![enter image description here](https://i.stack.imgur.com/roZ2V.jpg)](https://i.stack.imgur.com/roZ2V.jpg)

Now, we pick two random points on the edges of the cylinder - one for the top and one for the bottom circle. By connecting the two dots with a line what we can do next is calculate the angle between the line and the vertical z-axis.

We repeat this procedure $N$-times and collect all the random angles. 

What can we say for sure? 

* The minimum angle should be $0$°, the case where we pick two points laying on top of each other giving us a parallel line to the z-axis.
* The maximum angle should be $45$° - which is decided for us by $r$ and $h$.

---

My question here is what distribution of angles we should get. Running an experiment will give me the following distribution:

[![enter image description here](https://i.stack.imgur.com/yoEEA.png)](https://i.stack.imgur.com/yoEEA.png)

Which is a bit counter intuitive - at least to me - I expected a uniform distribution to be honest. So, I do hope I did not make a mistake in my code, which is why I provide the experiment code below.

In any case I would be interested in whether this distribution makes sense and if it's possible to explain it.

One thing that surely is important: `np.random.rand()` draws uniform values $x \in [0,1]$ so the points on the edges (`v` and `w`) should be uniform too.

```
%matplotlib inline  # Jupyter Notebooks
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib.cm as cm
import seaborn as sns
import numpy as np
import pandas as pd

def angle(a, b):    
    a_u = a / np.linalg.norm(a)
    b_u = b / np.linalg.norm(b)    
    c = np.clip(np.dot(a_u, b_u), -1, 1)    
    return np.degrees(np.arccos(c))

# Create the random points on the cylinder's edges
N = 5000
a = 2 * np.pi * np.random.rand(N) 
b = 2 * np.pi * np.random.rand(N)
v = np.vstack([np.cos(a), np.sin(a), 2*np.ones(N)]).T
w = np.vstack([np.cos(b), np.sin(b), np.zeros(N)]).T
df_v = pd.DataFrame(v, columns=['x', 'y', 'z'])
df_w = pd.DataFrame(w, columns=['x', 'y', 'z'])
# Get the connecting lines
df_u = df_v - df_w

# Calculate the angles of the lines w.r.t. the z-axis
e3 = np.asarray([0, 0, 1])
ds_angles = df_u.apply(lambda x: angle(x, e3), 1)

# Plotting the distribution of angles
ax = plt.figure(figsize=(8, 4.5)).gca()
sns.distplot(df_u.apply(lambda x: angle(x, e3), 1), ax=ax, kde=False)
plt.xlabel('Degree')
plt.show()

```