On graphical analysis of an asymmetric prime function
Let’s define two binary operators:
1)
The “at” operator “@”: it takes as input two
integers A and B, multiplies them together, and returns the next prime number
greater than the result. Hence, A@B = NextLargerPrimeNumber(A*B).
2)
The “dash” operator “#”: it takes as input two
integers A and B, divides A by B, and returns the previous prime number equal
to or less than the result. Hence, A#B =
PreviousSmallerOrEqualPrimeNumber(A/B). Oh, two more things:
a.
First, in case A/B is undefined, simply return 0
b.
Second, in case there is no such previous prime
number less than or equal to A/B, simply return 1
With these two binary operators in place, one can easily
come up with any function or even equations. For example, what would the values
of X be such that ((X^2)#(X-1)) @ ((X^3-X)#(X+1)) = 13? It seems like a laborious
exercise to try, and it is it. Instead of analyzing equations, one can analyze
few functions and their properties related to these two operators. Let’s then
define the following function:
F(x) = ((x+C)@(Cx)) @ ((Cx)#(x+C))
Where C is a constant. In order to
analyze it we first need to write an algorithm to evaluate such an expression.
The algorithm uses a standard Eval
implementation: recursive dynamic evaluation tree, starting from the lower
priority operations to the higher ones. The IsPrime
method is not optimized, but should do the job for the task in hands. Code is down
below. When plotting the graph for C=13, the following pattern appears:
The picture above shows two striking characteristics of this function: first is its asymmetric nature as we can see from the red box. The second is the different "jumps" and "gaps" in the function. It is certainly not a smooth pattern. But another experiment, removing the prime conditions for instance (which would instantly turn the function into a "regular" one), also shows similar behaviors as the above:
The similar jumps and gaps exist, but the function becomes a little more symmetric, although not perfectly so.
A more interesting behaviors takes place when we set C to a negative parameter, such as -99:
It is striking that the pattern abruptly drops off a cliff into a constant - most likely the code overflowed. If we look at the same curve ignoring the prime conditions it becomes way more smoother than previous patterns:
Likely the abrupt drop is the effect of a division by zero. In general, it is interesting to come up with variations of the normal arithmetic operators infusing into them prime number properties and analyze graphically the effect of such changes.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FunPrimeOperators
{
class Program
{
static void Main(string[] args)
{
if (args.Length != 4)
{
Console.WriteLine("Usage: FunPrimeOperators.exe <constant>
<from> <to> [0=normal; 1=inverted]");
return;
}
int c = Convert.ToInt32(args[0]);
int from = Convert.ToInt32(args[1]);
int to = Convert.ToInt32(args[2]);
int func = Convert.ToInt32(args[3]);
if (func == 0)
{
BuildFunctionAndEval(c, from, to);
}
else
{
BuildInvFunctionAndEval(c, from, to);
}
}
public static void BuildFunctionAndEval(int c,
int from,
int to)
{
for (int i = from; i <= to; i++)
{
int op1 = i + c;
int op2 = c * i;
string str = "(" + op1.ToString() + "@" + op2.ToString() + ")";
str += "@";
str += "(" + op2.ToString() + "#" + op1.ToString() + ")";
long fn = Eval(str);
Console.WriteLine("{0};{1}", i, fn);
}
}
public static void BuildInvFunctionAndEval(int c,
int from,
int to)
{
for (int i = from; i <= to; i++)
{
int op1 = i + c;
int op2 = c * i;
string str = "(" + op1.ToString() + "@" + op2.ToString() + ")";
str += "#";
str += "(" + op2.ToString() + "#" + op1.ToString() + ")";
long fn = Eval(str);
Console.WriteLine("{0};{1}", i, fn);
}
}
public static long Eval(string str)
{
if (str == null) return 0;
long n = 0;
if (Int64.TryParse(str, out n))
{
return n;
}
int nBrackets = 0;
for (int i = 0; i < str.Length; i++)
{
if (str[i] == '(') nBrackets++;
if (str[i] == ')') nBrackets--;
if (str[i] == '#' && nBrackets == 0)
{
long n1 = Eval(str.Substring(0, i));
long n2 = Eval(str.Substring(i + 1));
if (n2 == 0) return 0;
long result = PreviousPrime(n1 / n2);
return result;
}
}
nBrackets = 0;
for (int i = 0; i < str.Length; i++)
{
if (str[i] == '(') nBrackets++;
if (str[i] == ')') nBrackets--;
if (str[i] == '@' && nBrackets == 0)
{
long n1 = Eval(str.Substring(0, i));
long n2 = Eval(str.Substring(i + 1));
long result = NextPrime(n1 * n2);
return result;
}
}
if (str[0] == '(' && str[str.Length - 1] == ')')
{
return Eval(str.Substring(1, str.Length - 2));
}
return 0L;
}
public static long NextPrime(long n)
{
n++;
while (!IsPrime(n))
{
n++;
}
return n;
}
public static long PreviousPrime(long n)
{
if (n < 2) return 1L;
while (n >= 2 && !IsPrime(n))
{
n--;
}
return n;
}
public static bool IsPrime(long n)
{
if (n < 2) return false;
if (n < 4) return true;
long limit = (long)Math.Ceiling(Math.Sqrt(n));
for (long i = 2; i <= limit; i++)
{
if (n % i == 0) return false;
}
return true;
}
}
}
Comments
Post a Comment