Creating A Module

The Story So Far

You are a budding mathematician, and have been struck by a particular problem:

Suppose 3 random points are chosen uniformly on the unit circle. What is the probability that the center of the circle is within the triangle?

This problem is fairly challenging. As a computer-assisted mathematician, you know a good way to build insight about a problem is to run examples - a bunch of examples - to help you notice patterns of convergence or recurrence.

As a computer-assisted mathematician, you know your computations are only useful if they represent roughly what you think they represent - so you will also be writing tests for each of the functions described in this exercise.

Triangles

Step 1: Create a file named InscribedTriangleExplorations.py in your course directory.

We are going to go through the steps in reverse order. First, let's start creating objects. For points, we can just use tuples - they support equality checking.

Define a class Triangle implementing the provided docstring. Import doctest as described in class.

"""
A triangle on the real plane.

Attributes
----------
points : list
    List of tuples (x,y) for the points of the triangle.

Methods
-------

Examples
-------

>>> Triangle([(1,0),(0,1),(-1,0)])
Triangle([(1, 0), (0, 1), (-1, 0)])

>>> Triangle([(1.0,0.0),(0.0,1.0),(-1.0,0.0)])
Triangle([(1.0, 0.0), (0.0, 1.0), (-1.0, 0.0)])

>>> Triangle([(1,0),(0,1),(1,0)])
Traceback (most recent call last):
...
ValueError: Cannot construct triangle: points (1, 0), (1, 0) equal

>>> Triangle([(2.5,1.0),(2.5,1),(5,0)])
Traceback (most recent call last):
...
ValueError: Cannot construct triangle: points (2.5, 1.0), (2.5, 1) equal

"""

(note: it is good to include more checking to make sure you are passed values you can use, but to keep the exercise short, we leave out checking the type and geometry of the input - and checking for collinearity, which might throw a wrench in later computations.)

You may find - for checking equality of points - there are useful functions around. For example, to get all pairs from a list:

from itertools import combinations
[i for i in combinations([1,2,3],2)]

Checking the Inside

Next, we need to find out whether the origin is inside or outside of our given triangle. I provide one way you can do that as a hint, but if you would rather think it through yourself, you can write whatever works.

Hint: Checking Inside/outside

The simplest way to find out if a point $P$ is on the inside a triangle with vertices $A,B,C$, is whether it is on the same side of the line $\overline{AB}$ as $C$, on the same side of $\overline{AC}$ as $B$, and on the same side of $\overline{BC}$ as $A$.

An easy way to do that is with a Exterior Product of vectors. The exterior product $\overrightarrow{AB} \wedge \overrightarrow{AC}$ tells us whether C is on the "left" or "right" of AB - so if $\overrightarrow{AB} \wedge \overrightarrow{AP}$ is the same sign, it is on the same side.

The exterior product of two 2-dimensional vectors $V \wedge S$ is the determinant of $[V S]$, $v_1 s_2 - v_2 s_1$.

A,B,C,P = [(2,1),(3,1),(0,-2),(-1,-1)]
AB = [i-j for i,j in zip(A,B)]
AC = [i-j for i,j in zip(A,C)]
AP = [i-j for i,j in zip(A,P)]

if (AB[0]*AC[1]-AB[1]*AC[0] > 0) == (AB[0]*AP[1]-AB[1]*AP[0] > 0):
    print("P is on the same side of AB as C.")
else:
    print("P is not on the same side of AB as C.")

Write a loop to check this for all three arrangements, and if any of them differ, the point isn't in the middle of the triangle!

What happens if the point is on the edge? How can you check for that as part of this loop?

Define a new method Triangle.contains_point(point), with its own docstring and tests, which checks for a point being in the provided triangle - including on the edge or within the body of the triangle. It should return True if the point is on the inside and False if not - test each case, including an example on each edge, in the middle, and outside. Throw a TypeError if provided an unknown type, and test this case as well.

Randomness

Python gives you access to a pseudorandom number generator, and a couple helper functions, in the random module. Relevant to us right now is the random.random function, which gives you a pseudorandom floating point number (roughly) evenly distributed in the range [0,1).

Create a docstring for a function random_unit_circle_point() which takes no arguments and returns a random point on the unit sphere. Then implement the function.

you may notice that random_unit_circle_point() is harder to test. Fortunately, random has an answer for us: random.seed(hashable) seeds the pseudorandom number generator with the provided object, allowing us to do repeated checks.

After each call of random.seed(0), for example, the next values of random are fixed -

random.seed(0)
assert random.random() == 0.8444218515250481

Using this, write some doctests for random_unit_circle_point.

One last thing to do is to make sure you are using minimal imports. It is almost always worthwhile to just take what you need.

Go over your file and make sure that every function you are importing is one you are using. If you are importing a module, and only using one or two functions from it, then change those to explicit from module import function calls.

Run the file to make sure it is still passing all your tests. Then:

Create a second .py file importing your functions. In the second file, run at least 1000 iterations of checking whether the point $(0,0)$ is within the triangle defined by three random points. Use statistics.mean to find the average of your 1000+ attempts.

One last thing to do:

Submit the two .py files to Brightspace.

The Inspiration Problem - Higher Dimensions Four points are chosen at random on the surface of a sphere. What is the probability that the center of the sphere lies inside the tetrahedron whose vertices are at the four points? (It is understood that each point is in-dependently chosen relative to a uniform distribution on the sphere.)

Extend your code to select points correctly on the unit sphere - uniformly.

Extend to even more dimensions.

(A useful - and interesting - fact is that normal distributions in variables are spherically symmetric - so if you choose cartesian coorrdinates from a normal distribution, then project to the sphere, you will get an even distribution. numpy.random.normal is a good PRNG normal distribution.)

See 3 Blue 1 Brown for an excellent explanation of some out-of-the-box ways to solve it.

Department of Mathematics, Purdue University
150 N. University Street, West Lafayette, IN 47907-2067
Phone: (765) 494-1901 - FAX: (765) 494-0548
Contact the Webmaster for technical and content concerns about this webpage.
Copyright© 2018, Purdue University, all rights reserved.
West Lafayette, IN 47907 USA, 765-494-4600
An equal access/equal opportunity university
Accessibility issues? Contact the Web Editor (webeditor@math.purdue.edu).