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

Popular posts from this blog

Changing the root of a binary tree

ProjectEuler Problem 719 (some hints, but no spoilers)

The Power Sum, a recursive problem by HackerRank