WriteUp — Fantastic times tables… and how to draw them

WriteUp — Fantastic times tables… and how to draw them

Welcome back everyone! Today we’ll move away a bit from the usual coding problem challenges to look at something I used to play with when I started to learn to code. It’s a bit of a silly game really, but also a nice opportunity to explore some times tables math, coding and…turtles!

Ready? Let’s begin then.


Times tables

Times tables are probably the first concept everyone learns at elementary school after basic arithmetic. I still remember my teacher’s method: she used to teach us by learning them by memory, and then we had small competitions in class. We were given a piece of paper with a table and some missing results and we had to fill all the times tables learnt up to that point: whoever finished first was given a prize. It was really funny, back in the days.

Besides being a tool for the every-day life and a school exercise though, if we take a closer look to times tables we can discover that there is?a lot more?hidden behind them. For example: what happens if we try to draw them?


Drawing the tables

The idea behind this little program came to me by a video by Prof. Polster, who runs the brilliant YT channel?Mathologer. You can take a look at his content?here: he delves a lot more into the concept, talking about Mandelbrot sets and Riemann Z function and he’s just way too good at explaining those concepts in a fun and easy to understand way.


We will limit our game to the pictures of the times tables tough, specifically writing some code that will draw them for us.

Non è stato fornito nessun testo alternativo per questa immagine

The idea is pretty simple: we draw a circle first, and we enumerate some points on the circumference, leaving the same distance between them. After that we start calculating the results for a given times table and connect the results. For example, for the 6-times table:

and so on. What happens if we connect a greater number of points? Let’s find out.


A quick refresh

Before starting, a quick refresh about the concepts we are going to see here. A?circumference?is the?set of the points equidistant from its center, also called?origin (O). That distance is called?radius (r).


On the plane, assuming we have our origin point O with coordinate?x?and?y,?we can find the coordinates of the point?A?on the circumference with the following equations:

where θ is the angle created by the point A, the origin and the y axis of the plane, adjusted by the?x?and?y?coordinates. For example, this circle has?O?in?(4,4)?and?r = 3. The point?A?is drawn on its circumference, creating an angle a of?30°. We can calculate the coordinates of?A?like

You can verify with the drawing below.

Non è stato fornito nessun testo alternativo per questa immagine

Another important concept is the difference between?degrees?and?radian:

  • The degree (°) is a unit of measure of angles referred to the full rotation of the circle. For example, knowing that the full rotation is 360°, we conclude that half of the area of the circle must create an angle of 180°;
  • The radian (Rad) is also a unit of measure for angles, but referred to the length of the semicircle from one point to another on the circle. Basically, it’s the distance traveled on the circumference and the angle created by the starting point and the end point.

Without going too deep, we can calculate the Rad by the following equation:

Rad = (π*Degree)/180

We’ll also use a bit of?modular arithmetic?to calculate our results on the circle. Simply put,?the modulo operator gives us the remainder of a division: for instance, A%B = C (red as “A modulo B equals C”), will mean that the remainder of A/B equals C.

That’s enough info for our mission today. We’ll make wide use of these concepts below so check them out carefully.

Now for the fun part, let’s start drawing!


Turtle

When I started learning Python, I used to play a lot with this library,?turtle, which is a great tool to draw shapes and geometries. It’s often skipped while learning the language, mainly because it has no real purpose and it’s usage is really limited. However I think it’s a great library, well documented and easy to use, which is really worthy to explore and have fun with.

The library is really simple: we will “spawn” a turtle (which we’ll call Bob, as it’s often named in tutorials around the web), represented by an arrow on the screen, and move it giving directions and turning points. Here’s an example of some basic commands:

import turtle

bob = turtle.Turtle()

bob.fd(50)          # Go 50 pts forwars
bob.left(90)        # Turn 90° left
bob.back(50)        # Go 50 pts backwards
bob.right(90)       # Turn 90° right

bob.pensize(2)          # Change line size
bob.pencolor("red")     # Change line color

bob.fd(50)
bob.left(90)
bob.fd(100)

bob.pensize(3)
bob.pencolor("green")
bob.left(90)
bob.fd(100)

turtle.mainloop()        
Non è stato fornito nessun testo alternativo per questa immagine

Let’s start drawing then!


The circle

import turtle
	

	# Draws a circle from the origin with a given radius
	def drawCircle ( turtle :turtle.Turtle, radius :int, origin :tuple=(0,0), penSize :int=1, penColor :str="black", writeLabel :bool=False ) :
	    turtle.pensize(penSize)
	    turtle.pencolor(penColor)
	    turtle.up()
	    turtle.goto(origin[0],origin[1]-radius)
	    turtle.down()
	    turtle.circle(radius)
	    turtle.up()
	    if writeLabel:
	        turtle.goto(origin[0],origin[1])
	        turtle.down()
	        turtle.dot()
	        turtle.write("O",font=("CourierNew",8,"italic"))
	        turtle.up()
	    turtle.goto(origin[0],origin[1])
	    turtle.down()
	

	bob = turtle.Turtle()
	drawCircle(bob,150)
	turtle.mainloop()        

First we define the function?drawCircle?which accepts the following parameters:

  • turtle, the turtle we are moving;
  • radius, the radius of the circle;
  • origin, the origin of the circle given as a tuple of coordinates x and y, set by default on (0,0);
  • penSize,?penColor?and?writeLabel, which are graphics parameters to set the line width, color and the label on the origin

Notice that turtle starts drawing from the lowest point of the circumference, so the function just moves the turtle down by the radius length, draws the circle and then moves back up by the radius.

Non è stato fornito nessun testo alternativo per questa immagine

Adding the points

We first need to decide how many points to add on the circle. Let’s start simple and try with 12, like on the clock! We need to put 12 points on the circle, with the same distance between one another.

Knowing that the angle of a full circular rotation it’s 360°, we can simply split this rotation in 12 parts and get an angle of 360°/12 = 30° for each point. Also, we’ll be working with?math.sin?and?math.cos?from the Python?math?library, which use radians as measure, so it’s better to convert our angle right now. Let’s write a function to wrap this up:

def calculateAngle (numberOfPoints :int):
    angle = 360/numberOfPoints
    return angle*math.pi/180        

And now let’s put the points on the circle:

coordinates = {

def drawCircumferencePoints (turtle :turtle.Turtle, radius :int,coordinates :dict, numberOfPoints :int=1000, penSize :int=1,penColor :str="black",writeLabel :bool=False):
    turtle.pensize(penSize)
    turtle.pencolor(penColor)

    def singlePointDraw (angle):
        turtle.up()
        turtle.goto(
            radius*math.sin(angle),
            radius*math.cos(angle)
        )
        turtle.down()
        turtle.dot()
    
    angle = calculateAngle(numberOfPoints)

    for i in range(numberOfPoints):
        singlePointDraw(i*angle)
        coordinates[i] = turtle.pos(),i*angle
        if writeLabel:
            turtle.write(i)}        

We define the function?drawCircumferencePoints?, that accepts the usual parameters and a new one,?coordinates?, which is a dictionary to collect the points and their coordinates. This way we can avoid recalculating all the points in the next step, which comes really in handy while working with greater numbers of points.

The inner function?singlePointDraw?simply moves the turtle to the coordinates for a given?angle?and places a dot.

With this function, we range over our points and drop a point with?singlePointDraw?: the angle will always be 30°, so for each point in the range we drop a point at?i*angle?: for point 0 we’ll place it at?0*30° = 0°?, for point 1 at?1*30° = 30°?, for point 2 at?2*30° = 60°?and so on.

At this point we also collect the position of our turtle and the angle in the?coordinates?dictionary. It’s not really necessary to collect the angle: I used it for testing and left it there (unintentionally ;D) !

Finally, if the parameter?writeLabel?is set to?True?, we’ll write the point number aside each point.

Calling?drawCircle?and?drawCircumferencePoints?you should now have something like this:

Non è stato fornito nessun testo alternativo per questa immagine

Also note that the points start at 0 and end at 11: this is why modular arithmetic will come in help pretty soon.


Time tables!

At last, let’s put down our times-table! Let’s pick the number 3, for example, and draw it’s time table.

def drawTable(turtle :turtle.Turtle,timesTable :float, coordinates :dict, radius :int, numberOfPoints :int,penSize :int=1,penColor :str="black")

    turtle.pensize(penSize)
    turtle.pencolor(penColor)
    
    angle = calculateAngle(numberOfPoints)

    for n in range(numberOfPoints):
        turtle.up()
        startingPoint = (
            coordinates[n][0][0],
            coordinates[n][0][1]
        )
        result = (n * timesTable)%len(coordinates)
        endPoint = (
            radius*math.sin(result*angle),
            radius*math.cos(result*angle)
        )
        turtle.goto(startingPoint[0],startingPoint[1])
        turtle.down()
        turtle.goto(endPoint[0],endPoint[1]):        

We define another function to do this,?drawTable?. After that we calculate the base angle to have a reference in this function and store it in the variable?angle?.

Then, we start looping over our points. For each point we define:

  • a starting point, taken from the coordinates we listed before;
  • an end point, calculated as the value of the starting point multiplied by the chosen times table. This is where the modulo operator comes in action: multiplying, for example, 3 * 11 gives 33 as result, so we should move to point 33 on the circle. We have only 12 points on the circle, though, so we can use the modulo to calculate 33%12 = 9, which is exactly the point we end up on if we go around the circle 33 times.

Finally, we simply draw a line between the starting dot and the end dot, and start the loop again with the next number.

At this point, you should have something like this:

Non è stato fornito nessun testo alternativo per questa immagine

Wrapping everything up

Let’s wrap up all the code in a single function:

def timeTablesDrawer (
        radius :int,
        origin :tuple,
        numberOfPoints :int,
        timesTable :float,
        penSize :int = 1,
        penColor :str = "blue",
        writeLabel :bool = False,
        speed :str = "fastest"
):
    bob = turtle.Turtle()
    bob.speed(speed)
    coordinates = {}
    drawCircle(bob,radius,origin,penSize,penColor,writeLabel)
    drawCircumferencePoints(bob,radius,coordinates,numberOfPoints,penSize,penColor,writeLabel)
    drawTable(bob,timesTable,coordinates,radius,numberOfPoints,penSize,penColor)
    turtle.mainloop()        

This function basically calls the other function created above in the right order, nothing more really. It adds the parameter?speed?to set the speed of the turtle, so we can control how fast the animation is (trying with higher number of points takes a bit of time!). It also holds the variable?coordinates?, which was outside the?drawCircle?function before, and now it’s contained by the outertimesTablesDrawer?function.


Some more points, please?

The image resulting with the number 3 was ok, but not that much of a masterpiece: it was just a bunch of lines inside a circle. That is because there are too few points on the circle. Let’s try adding some more: 200 points for example!

Non è stato fornito nessun testo alternativo per questa immagine

That’s way cuter! How strange is the shape drawn, right?

These types of curves are called?cardioids, for their shape remembering a heart. It’s pretty clear if you look at the one in the cover image, which is the 2-times table cardioid. Generally speaking, these kind of shapes has a number of “bumps” equal to the chosen times-table minus one: in this example, the time table was 3 and in fact the image has 2 round bumpy shapes in it.

These cardioids pop up a little everywhere around math, physics, chemistry and nature. In microphones and apples, for example…and also in your cup of tea!

Non è stato fornito nessun testo alternativo per questa immagine
CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=1767270; https://shop19002.classiclightingdevices.net/content?c=cardioid+mic&id=29; https://i0.wp.com/thesmarthappyproject.com/wp-content/uploads/2019/01/celandine-hearts.png?fit=1024%2C972; https://study.com/learn/lesson/cardioid-graph-equation-stucture.html

And there is more! If we try to draw higher times-tables, we can see tons of different shapes coming up: some of them has nothing to do with cardioids, like the 99 or the 66 times-table!

Here some examples with greater numbers:

Non è stato fornito nessun testo alternativo per questa immagine
Non è stato fornito nessun testo alternativo per questa immagine

Give it a try with different number of points and times-tables: I can assure you it’s so much fun to look at the little turtle running around to draw beautiful shapes!


Intermediate results

We can also draw intermediate results, meaning times-tables with decimal places. It’s also really cool to see intermediate results because it gives us an idea about how the image gets created and changes from one result to another. Here’s an animation of the times-tables from 2 to 3 (sorry for the bad quality!) :

Non è stato fornito nessun testo alternativo per questa immagine

You can see how the shape basically keeps rotating and enveloping on itself, almost shifting to the left, to create the next cardioid. How awesome!


Conclusions

That’s it! A long journey (as most of the times here). It’s a bit different from the usual articles about coding problems solving, but I recently saw that video again and it instantly made me want to share my try to it.

I also wrote a simple program to draw these shapes, you can download it from my Github page,?here?(click on ‘Raw’ to download it!): feel free to use it whenever and wherever you like.

I hope you enjoyed it and find it interesting! If you did, please leave a clap or two or share it with someone who could enjoy it too: it would make my day!

As always, thanks for reading.

Nicola

要查看或添加评论,请登录

Nicola Moro的更多文章

  • Huge factorials! (Digit by digit)

    Huge factorials! (Digit by digit)

    Welcome everyone with another problem to solve! Today we have some nice math to work with: factorials! We are going to…

  • Ciphers — Mono-alphabetic ciphers (Pt. 1)

    Ciphers — Mono-alphabetic ciphers (Pt. 1)

    Hello everyone! I’ve recently finished a really cool course about the math behind cryptography and I thought it could…

  • VPNs - Una recensione onesta (Parte 2)

    VPNs - Una recensione onesta (Parte 2)

    Questo articolo è la seconda parte dell'articolo "VPN - Una recesione onesta (Parte 1)", che da una breve base tecnica…

  • VPNs - Una recensione onesta (Parte 1)

    VPNs - Una recensione onesta (Parte 1)

    Disclaimer: Con questo articolo non intendo dar vita a discussioni con chi usa VPN frequentemente e ne apprezza il…

  • WriteUp — Inventory sequence

    WriteUp — Inventory sequence

    Hello everyone and welcome back! Today I’m moving away from the usual coding problem to show you a really interesting…

社区洞察

其他会员也浏览了