Recently I’ve gotten interested in experimental mathematics – finding mathematical relations using computational methods, rather than a mathematicians insight. While I won’t discuss the philosophical ramifications of this, I do want to detail a method for finding near-integers using experimental mathematics.
A near-integer is a number which is “close” to being a integer, within some . Finding near-integers is easy, the number is almost equal to 1, but it’s not interesting. The fun part about near-integers is that a seemingly random relation somehow almost equals an integer.
For instance, , which is arguably close to being -1. While there is no rigorous definition of what is a near-integer, we will be satisfied with finding the relation which is “closest” to being an integer (more on this later).
In the first part of this post we’ll deal with the basic principles of the algorithm, and further on we’ll get more technical.
So, the basic principle is that we’ll generate a random expression, and check whether or not it’s close to being an integer. Then we’ll repeat this process for 100.000 random expressions and choose the expression which was closest to being an integer. Put in bullet points:
- Set iterator , and so far best expression .
- Generate random expression .
- If is closer to being an integer than , set .
- Increment by one.
- If go to step 2.
- Display expression .
The process of generating expressions sounds simple, but it is, in fact, not that simple to generate a random expression, as we’ll discuss later on.
One of the more prominent results from my program is the following:
In fact, this number is closer to being an integer than .
Another interesting result is:
For more results check the bottom of this post.
Modelling the expressions
To be able to use our clever new algorithm, we need to be able to generate expressions. But what is an expression? An expression is a mathematical statement which returns some value. In practice, we’ll model an expression as an abstract class in Java. An abstract class is a class, which contains some fundamental, abstract functions and methods that can be inherited by other subclasses.
This is useful, because it allows us to model an abstract class called Expression, and have subclasses of this, which perform specific functions. Their common function is getValue();, so all subclasses of Expression needs to be able to return a numeric value. The most basic expression is Constant, which returns a single number. An example of Constant could be the number 1, the number 2, or .
Next up we could have a class called Addition, which takes two other Expression‘s and returns their sum when getValue(); is called. Similarily, we have Subtraction, Multiplication, Division, and Exponentation. It’s also easy to implement other functions, but as of now they won’t be necessary.
The reason we use abstract classes is so we can have more complicated, nested expressions inside each other. So, an instance of Addition contains two expressions, which might also be other Addition classes.
To make things more explicit, let’s take a look at the structure of the expression . This expression is an instance of Subtraction, containing the two other expressions (being an instance of Exponentation), and (being an instance of Constant).
We can also implement other functions than getValue();. In my implementation, I have also made getLaTeX();, which returns the LaTeX-code for the expression, and getString();, which returns a more readable string representing the expression.
Generating random expressions
To make a random expression, we’ll use recursion. We have a function randomExpression(int depth); which returns a new random expression. This function has two special cases:
Depth = 0
When the depth is 0, we’ll simply return a new Constant from some alphabet of constants. In practice we’ll use the alphabet .
Depth > 0
When the depth is larger than zero, we’ll return a new random binary expression (for instance Addition, which contains two other expressions.) with the arguments randomExpression(depth-1), randomExpression(depth-1). This ensures that we’ll have a nested expression. A special clause is added for Exponentation, which returns an instance of Exponentation, with the arguments randomExpression(0), randomExpression(0) to avoid ugly towers of exponents.
This is the basic gist of it. This will generate a random expression of some depth.
While this method proves successful in finding interesting near-integers, most often it finds rather tedious near-integers. For instance, one of the near-integers I often encounter are like the following:
The program claims this is a near-integer close to 3 (obviously). This is unfortunate, but there is little to be done with it without some really sophisticated methods.
Gallery of near-integers
Here is a list of all the most interesting near-integers I’ve found using this method. Some of the expressions have been simplified using Wolfram|Alpha.