Between two players the Thue-Morse Sequence suggests “taking turns taking turns”, so the start of an infinite turn sequence (over the first 5 recursions) between players a and b would look like this: abbabaabbaababbabaababbaabbabaab. Each recursion splits the entire sequence into two parts. The first part shifts into second position and the second position moves into first position. The new sequence is then added to the end of the original. Each time the length of the sequence doubles so, for two players the length of the sequence will be 2^{r} where r represents the number of recursions.

This is neat, but what if we wanted to generalize fair sharing across n players? Let’s first look at 3 players.

We’ll start the first round with abc. The first recursion is the original sequence. With two players the sequence was split into two pieces, so with three players the sequence is split into three pieces. We want our players to “take turns taking turns”, so each player is shifted backward one spot then that resulted is added to the original sequence (note that a player in the first spot shifts to the last spot). This gives us abc bca. This isn’t “fair” yet, since each player has yet to play in each spot, so let’s do it again, but this time we’ll shift the original sequence backwards two spots: (abc) (bca) (cab). Now each player has taken turns taking turns. Note that the first recursion is 3 characters long and the second is 9 characters long. To continue with this pattern, we slice the new sequence into three units and add shifted versions of the slices to the tail: (abc bca cab) (bca cab abc) (cab abc bca). This third recursion is 27 characters long or 3^{r}.

The graphs below show the data points and average data points for the fourth recursion. Each first pick is worth 15 points, each second pick is worth 10 points, and each third pick is worth 5 points. It is clear that there is a fair distribution of points among each player and that over several turns the average fairness converges.

A proposed generalization for this sequence follows the following pattern: take a set of unique players {a,b,…,n}, identify all unique combinations by shifting each player 1 position. Including the original order this can be done n times, resulting in {a, b, …, n}, {b, c, …, n, a}, … {n, a, b,…, n -1}. Counting the original set as the first recursion, we can see that the second recursion will have n^{2} characters. Further each recursion can be sliced into n even parts, so the number of characters at any given recursion will be n^{r}.

A C# method of this generalization is shown below:

static List ThueMorseMorseThue(string sequence, int numberOfRecursions) { if (!AllUniqueCharacters(sequence)) return null; var playersLists = new List(); playersLists.Add(sequence); var initialCount = sequence.Length; int index = 0; do { var thisSequence = playersLists[index]; var thisCount = thisSequence.Length; var segmentLength = thisCount / initialCount; var newSequence = ""; for (int n = 0; n < initialCount; ++n) { for (int i = n * segmentLength; i < thisCount + n * segmentLength; ++i) { int j = i % thisCount; newSequence += thisSequence[j]; } } playersLists.Add(newSequence); index++; } while (playersLists.Count < numberOfRecursions); return playersLists; }

While writing this post I found myself caught up considering the possible implications of this sequence when taking turns for a game where not only is turn order valued, but the relative position to other players is considered: ie with players abc, b always follows a. With a consideration of relative positioning a and b would take turns following each other.

]]>

This is a simple example and the differences between the two flow charts may seem inconsequential. On the contrary, the first flow required about three times as much written code and would have been much more challenging to maintain. I was also able to reduce the number of unit tests because the problem was much simpler than I originally thought.

The simplification of this flow chart did not come to me by searching aggressively for it. In fact, I thought the first solution was just fine. It was in a moment of pause that a flash of intuition hit me: “Every task has an Opened Date and any Completed Date must be greater than or equal to the Opened Date. Why am I checking for a Completed Date first, when I could already have useful information about it from the Opened Date?” This question was enough for me to pull up Visio for some experimentation.

The definition of intuition is “the ability to understand something immediately, without the need for conscious reasoning.” The bustle of daily life can drown out the voice of our intuition often leading us to move from thing to thing to thing to thing without consideration. Meditation is a practice that helps us access our inner intuition on an increasingly regular basis. Novices start to gain access to their inner intuition after only a short span of regular practice.

Some meditation practitioners have spiritual beliefs that are used in conjunction with their practice, but the act itself is inherently secular and can be executed without threat to religious beliefs. It can be and is often used as a tool to help people tap into their intuition, achieve greater focus, improve relaxation, acquire relief from distraction, and live in an overall more peaceful manner.

Meditation can come in many forms. Contrary what we see in popular media, it is not a practice reserved solely for mystics seated in a full lotus or bald dudes in orange robes living in solitude on mountain-tops. The physical expression of meditation looks like different things for different people. Some prefer to be seated, others prefer to lay down, some prefer yogic postures, some even walk. The list goes on indefinitely, but the purpose is usually the same: to come to a full experience of this moment.

How does this help my programming, you ask? As ambitious programmers, we are looking for a challenge to propel us into next-level problem solving. We juggle abstract representations, complex objects, and nests of composed algorithms. We translate seemingly straightforward language into logical constructs. Often the language of our requirements seems benign, but if we’ve been doing this a while we’re not so arrogant to believe translating it will be a simple task. We have to consider the ilities: flexibility, scalability, readability, maintainability, and testibility (among several others). The adjacent comic by Jason Heeris is a perfect illustration of our daily struggle to get and stay on task – so why would we choose to interrupt that?

Meditation is a tool that can be used to spot check a situation – i.e. relax into to the present, remove distraction, increase clarity, etc. However, it is the aggregate of the meditation practice that presents us with long term benefits we want regular access to. It’s not a coincidence that we experience many of our Eureka moments while in the shower (or attached to other porcelain devices). It’s in these places that we have the most opportunity to be detached from working on our problems and have a single simple task for that moment. This basically sums up what we are trying to achieve with meditation. With intention, we practice a single simple task: being present with the immediacy of our experience (so that we learn to continually access the simplicity of now). Spot check meditations certainly have value, but that value is compounded by the aggregate of the intentional practice.

A common reason not to meditate is the time commitment. This should be less and less a concern as there is an increasing amount of research that suggests regular mindfulness (or meditation) practices increase overall well-being and decrease the need for sleep. (A quick search on reddit shows the majority of users in this thread experience better sleep and need less of it to function optimally.) My personal experience is that I have fewer sleep disturbances, fall asleep faster, and wake up easier with an intact practice. My meditation routine is the first thing I do – I wake up with enough time built in to get it done so that I have no excuses for not doing it. This is the same tactic that self help coaches and athletic coaches all prescribe in order to increase the chance of success for a healthy practice.

You don’t have to meditate for hours at a time to get the benefits. In fact, you can start with just a few minutes to see how it goes. As you start to access the benefits of meditation you might change how long you practice, how often you practice, and what methods you practice with.

If you aren’t sure how to start meditating, keep it simple. Most practitioners start with a basic observation of their breath. Here are the first instructions that were given to me when I started:

- Find a comfortable seated upright posture
- Set a timer
- Relax the body, but do not slouch
- Relax the eyelids in an almost shut position (a small sliver of light might be visible)
- Relax the jaw and tongue
- Notice your breath and starting with 1 count up for each inhale and exhale (you might only get to 2 or 3 at first – this is normal!)
- If the mind wanders, restart the count
- If the count reaches 10, restart the count

Guided meditations can also be very useful for learning about different styles to discover what works for you. The author Sam Harris offers a couple great guided meditations. Here is his 9 minute guided meditation and his 26 minute guided meditation. Tara Brach also has an extensive library of guided meditations. This is a basic meditation by Tara for meditators of all levels.

Many novice meditators find brainwave entrainment to be a very useful tool to supplement the meditation experience. The basic idea is to use audio and visual stimulus to induce progressively deeper meditative states. Listening to binaural beats with stereo headphones is one example of entrainment (note that you only need to listen to these just loud enough to hear a wavering). Isochronic tones are similar to binaural beats (these don’t require stereo headphones). The Kasina by Mindplace is a very neat (but expensive!) device that combines audio and visual entrainment techniques. This device is really cool and helped me enter some very deep states. I have mixed feelings about entrainment, but since I’ve used it extensively it deserves a place in this post. Entrainment is a great way to get started with meditation, but does not seem to be a long term solution. If the purpose of meditation is to practice accessing the immediacy of my experience then I should eventually learn to do so without outside help. Don’t get me wrong: entrainment meditation is vastly superior to no meditation, but my current preference is to do without it.

I’d like to address the phrase “just clear your mind.” It is not possible to actively engage in clearing the mind. This requires thinking, which is the opposite of the intention behind it. A consistent and intentional practice will lead to moments of still mind, but even the most famous gurus will tell you that you cannot escape the thinking nature of the brain. That’s just what it does. Pema Chödrön suggests that when we notice that we’re thinking during meditation, that we do just that: notice it. We give it a label “thinking” and we might move on from the thoughts. If we don’t, we continue to label the thoughts “thinking.” The most important part of this is to do so in a non-judgemental / compassionate way. We don’t chastise ourselves for doing the thing we’ve been conditioned to do since birth. We just notice it and maybe move on.

]]>

One of my personality traits is that I don’t like to stay in the same place for too long. This is a curse and a blessing. I enjoy a constant flow of new changes and challenges, but I’m also inherently lazy. Without pressure or clearly defined goals I stagnate and have a difficult time completing simple tasks. Consulting has been the perfect cure for these symptoms – except when it’s not. The consistent flow of new challenges is engaging, but spans of bench time, on-boarding periods, and immature SDLC can add up to a lot of time without much to do. Here are some of my tactics to stay afloat when there is seemingly nothing to engage in.

Don’t just keep a list of things. Keep it ordered. Keep it updated. Keep it visible. Know exactly what you’re going to do the next time you get bored and know exactly what you can do when you’re finished. For me, ideas flow in and out on a regular basis and I can’t trust my brain to remember anything without the help of IntelliSense.

Maintaining momentum can be tough – recapturing it can be tougher. If you want more, use the momentum you have instead and build it into the momentum you want. Show the list to your colleagues – if you’re lucky they might hold you accountable.

First off, I’m putting this here because its true, but more importantly, **you don’t need to be bored to ask for training.** Last week Corinna Cohn tweeted about this topic on the same day a Jr. Developer told me he was afraid to ask his boss to expense a book that he couldn’t afford. I didn’t push him, but I really wanted to know the source of his fear. Is the issue his employer or is it him? Employers that don’t invest in their employees are not employers to stick with. Employees that don’t ask for training likely don’t want training, don’t understand the value of training, or don’t have career ambitions. Good employers seek employees with ambition. Good employees look for employers that enable them to grow their career while adding value to their organization.

You are an asset – without growth you will become a liability.

Ever gone back to your code from a year or even a week ago, only to cringe and close IDE as fast as possible? Yeah, no. Leave it open. Fork it. Practice refactoring on a code base you are intimately acquainted with. Good refactoring practice is challenging enough against unfamiliar code. Now is your opportunity to tidy this mess up. Even if no one ever sees it, you’ve just made yourself a better programmer.

This falls under training, but I wanted to make a separate point about reading books in general. One of the characteristics I’ve observed in all great programmers is that they read. Videos, blogs, meetups, and stackoverflow can get you going, but books take you to a tailored level of detail and, if they have the seal of an established publisher, the quality of the information received is guaranteed. (You can’t go wrong with Manning, O’Reilly, or Apress.)

Recently I had coffee with Jamie Kurtz and I asked him a very pointed question about developing my career. The question isn’t important but the answer he gave me was. One of the most important lessons he’s ever learned in life and software is that it’s not about him or his career, but service. A simple change in attitude is all that is needed to take the focus off of me so that I can apply my skills to a greater cause. Sometimes a candid conversation with your boss starting with the question, “So, what’s keeping you up at night?” is enough to find that pet project that will enable you to use your problem solving skills, cure your boredom, __and__ make your boss very happy.

In the post Investigating Basic Combinatorics, I showed that, without any intentional programming, we could not evaluate all ^{n}C_{n/2} for n >= 21 without exceeding ulong.MaxValue. Even with intentional programming, we saw that ^{n}C_{n/2} starts to break the VLN barrier when n > 67.

One strategy to get around these constraints is to construct methods that deal with strings instead of numerical data types. Since String.Length has data type Int32, the theoretical character limit of Strings in C# is 2^{31}. This means that the theoretical maximum of a number in base 10 that can be expressed by a string is 10^{231} – 1. If that scale isn’t impressive, consider for a moment that the observable universe contains somewhere between 10^{79} and 10^{80} atoms. Such a scale for significant digits would cost an unprecedented amount of time in processing. For the sake of simplicity, we will generalize these limits and assume that strings do not constrain us to an upper limit for integer values.

In this post, I intend to address a strategy for dealing with the addition of integer type VLNs. Future posts will show how to extend the functionality to work with non-integer rational numbers and address multiplication, division, exponential, and combinatoric operations.

**Arithmetic of VLNs is no different than arithmetic of any other set of expressible numbers and we only need to know how to sum two numbers in order to sum several numbers.** Most of us learned how to deal with these problems when we were young children. When adding two numbers we start with the units digit and add them together. When the sum of the digits exceeds 9, we keep only units part of the sum and carry over the resulting tens digit. Next, the tens digits get added in the same manner, except that the previous carry over is added to the sum. This process continues until all units have been added together in this manner. If the last summation would have a carry over unit, the carry over is treated as the final sum. (Note: when adding two numbers the carry over can only be 1 or 0.)

We can apply a programmatic approach to the pseudo-algorithm above. If we write an integer as a string, then the last digit of the string will represent the units digit. Since C# uses 0 based indexing, the maximum index is the Length property less 1. We will loop through all the digits of both numbers, starting at with length – 1 index (unit) of each. We have to account for different lengths of numbers, so we will capture the maximum length of the numbers. During iteration, if the count of the current digit exceeds the length of the smaller number, 0 will be assigned to the smaller number’s digit. The carry over will be assigned a value of 1 or 0 for each set of digits depending on if the current sum is greater than 9.

A proposed method looks like the following. Some liberties with verbosity have been taken to ensure readability.

static string AddVeryLongNumbers(string x, string y) { string result = ""; int xMaxIndex = x.Length - 1; int yMaxIndex = y.Length - 1; int maxLength = x.Length > y.Length ? x.Length : y.Length; int carryOver = 0; for (int i = 0; i < maxLength; i++) { var xIndex = xMaxIndex - i; var yIndex = yMaxIndex - i; var xUnit = xIndex >= 0 ? getDigitFromXAtIndex(x, i) : 0; var yUnit = yIndex >= 0 ? getDigitFromXAtIndex(y, i) : 0; var naturalSum = carryOver + xUnit + yUnit; if (i < maxLength - 1) { result = (naturalSum % 10).ToString() + result; carryOver = (naturalSum / 10) > 0 ? 1 : 0; } else { result = naturalSum.ToString() + result; } } return result; } private static int getDigitFromXAtIndex(string x, int y) { return Convert.ToInt32(x[x.Length - 1 - y].ToString()); }

100,000 tries of adding two numbers with 61 significant digits with this method executes in 2.6432 seconds. The next post in this series will focus on multiplication and exponential operations of very large integers. Armed with such operations, we will be able to explore combinatorics on a more interesting scale without the need for advanced knowledge of mathematics or programming.

]]>Let’s start with a basic numerical problem: What are all the combinations of the set of all digits, tens, and hundreds values of numbers? In other words, what are all the combinations of {0, 1, 2, … , 9}, {0, 10, 20, … , 90}, and {0, 100, 200, … , 900 }?

An approach in C# might look something like this:

class Program { static void Main(string[] args) { var digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; var tens = new List<int> { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 }; var hundreds = new List<int> { 0, 100, 200, 300, 400, 500, 600, 700, 800, 900 }; var combinations = AllCombinationsOf(digits, tens, hundreds); foreach (int number in combinations) { Console.WriteLine(number.ToString()); } Console.ReadLine(); } static List<int> AllCombinationsOf(List<int> digits, List<int> tens, List<int> hundreds) { var retVal = new List<int>(); for (int i = 0; i < 10; ++i) { for (int j = 0; j < 10; ++j) { for (int k = 0; k < 10; ++k) { retVal.Add(hundreds[k] + tens[j] + digits[i]); } } } return retVal; } }

0

1

2

3

.

.

.

998

999

Notice that there are 1000 results. Thinking about this from a permutative perspective, this makes sense, there are 10 digits, 10 tens, and 10 hundreds to choose from... ie 10 * 10 * 10 = 1000.

A solution like this is useful for doing some quick calculations in a console, but beyond that it's severely limited. First off the method AllCombinationsOf(...) assumes we are always going to have three List

A better approach will meet the following criteria:

-the number of collections and the magnitude of each collection can be variable

-the implementation of the combinations will be independent of constructing the combinations

-the data type being combined will not be restricted to int

static void Main(string[] args) { var digits = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; var tens = new List<int> { 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 }; var hundreds = new List<int> { 0, 100, 200, 300, 400, 500, 600, 700, 800, 900 }; var unknownLists = AllCombinationsOf(new List<List<int>> { digits, tens, hundreds }); foreach(List<int> combinations in unknownLists) { Console.WriteLine((hundreds[combinations[0]] + tens[combinations[1]] + digits[combinations[2]]).ToString()); } Console.ReadLine(); } static List<List<int>> AllCombinationsOf<T>(List<List<T>> listOfLists) { var indexCombinations = new List<List<int>>(); for(int i = 0; i < listOfLists[0].Count(); ++i) { var indexes = new List<int>(); indexes.Add(i); GetNextIndexes(listOfLists, indexes, indexCombinations); } return indexCombinations; } static void GetNextIndexes<T>(List<List<T>> listOfLists, List<int> indexes, List<List<int>> indexCombinations) { var currentIndexesCount = indexes.Count(); if (currentIndexesCount < listOfLists.Count()) { for(int i = 0; i < listOfLists[currentIndexesCount - 1].Count(); ++i) { var newIndexes = new List<int>(); newIndexes.AddRange(indexes); newIndexes.Add(i); GetNextIndexes(listOfLists, newIndexes, indexCombinations); } } else { indexCombinations.Add(indexes); } }

The Main method hasn't changed much, except that we are letting it do the summing. In a real-world scenario we would likely have a separate method for this implementation. The real substance of this solution starts with AllCombinationsOf>. For this method I've chosen to return lists of indexes instead of lists of objects. That this method is generic and that we are only interested in combining indexes further removes us from the objects, their implementations, or the implementations of the combinations of the objects.

AllCombinationsOf> called indexCombinations. Next we use a for-loop to iterate through each member of the first List

First, we'll get the count of the current combination of indexes. If the currentIndexesCount is less than the listOfLists count, then we will iterate through next List

The recursion is terminated when the count of the current indexes equals the count of the listOfLists. When this condition is met, the current indexes combination is added to indexCombinations. Since each list is iterated fully against each other list the return value gives us every combination possible of each index from each list.

As anticipated the output in the console is the same as before, except now we have a strategy that offers flexibility and reuse.

]]>The formula is verbally stated as “N choose R”. An C# algorithm we might use to evaluate ^{n}C_{r} could look like this:

static long CombinationsOf(long n, long r) { long combinations = 1; if ( r < n) { for (long i = 1; i <= n; i++) { combinations *= i; } for (long i = 1; i <= r; ++i) { combinations /= i; } for (long i = 1; i <= (n - r); ++i) { combinations /= i; } } else { throw new NotImplementedException(); } return combinations; }

In the first for loop we will find the value of n!, the second for loop will divide by r!, and finally the third will divide by (n-r!). Let's use the function in a console application to test the example problem: "Ignoring order, how many 5-hand cards can be made from a 52-card deck?" In this case, we have 52 unique cards (n=52) and we want to see how many 5 card combinations are possible (r=5).

static void Main(string[] args) { var stopwatch = new Stopwatch(); stopwatch.Start(); int n= 52; int r = 5; var nCr = CombinationsOf(n, r); stopwatch.Stop(); Console.WriteLine("Time Elapsed: " + stopwatch.Elapsed); Console.Write(n.ToString() + " choose " + r.ToString() + " = " +nCr.ToString() ); Console.ReadLine(); }

Time Elapsed: 00:00:00.0004409

52 choose 5 = 0

Yikes. Something's not right. So where did we go wrong? As it turns out our function is correct but it can only evaluate up to n = 20. If n≥21 then the product resulting from the first for-loop will exceed long.MaxValue, but the bit wise operations will continue without consideration for this scenario. (Note that it is possible to get a positive, negative, or zero result that is incorrect using this choice of algorithm.)

Let's plug in n = 10 and r = 3 into the formula and solve by hand.

n! = 10! = 10 * 9 * 8 * 7 * 6 * 5 * 4 * 3 * 2

(n-r)! = 7! = 7 * 6 * 5 * 4 * 3 * 2

r! = 3! = 3 * 27!3! = (7 * 6 * 5 * 4 * 3 * 2)(3 * 2)

10! / (7!3!) = (10 * 9 * 8) / (3 * 2) = 10 * 3 * 4 = 120

Now, let's plug in n = 52 and r = 5 into the formula and solve by hand.

n! = 52! = 52 * 51 * .... 3 * 2

(n - r)!r! = 47! = (47 * 46 * ... 3 * 2)(5 * 4 * 3 * 2)52! / (47!5!) = 52 * 51 * 50 * 49 * 48 / (5 * 4 * 3 * 2) = 52 * 51 * 10 * 49 * 2

The final calculation is fed into a calculator and the result is 2,598,960, which is well within the long data type limits. In both cases we can see that n! is significantly reduced when divided by the terms of the denominator. (In fact the closer as r->n or r -> 1, the smaller ^{n}C_{r} will be. The converse of this is that ^{n}C_{r} is increases as r->n/2 and is maximized when r = n/2 for even n and when r = n/2 ±.5 for odd n. This also implies ^{n}C_{r} = ^{n}C_{n - r}).

In order to maximize the number of possible permutations returned from standard .NET data types (not including System.Numerics.BigInteger) we will want to iterate through division by the denominator in real-time as we multiple through the terms in the numerator. A strategy for this approach might look like this:

static ulong CombinationsOf(ulong n, ulong r) { double combinations = 1; ulong bigDenominator = n - r >= r ? n - r : r; ulong smallDenominator = n - r < r ? n - r : r; ulong j = 1; for (ulong i = n; i > 0; --i) { if(i > bigDenominator) { combinations *= i; } if(j <= smallDenominator) { combinations /= j; } j++; } return (ulong)combinations; }

Some things to note about this approach: Since we know a valid value will be positive we've chosen ulong as the return type. This gives us a max_value twice that of long. The double data type is used to account for fractional values of combinations that will result prior to the completion of the algorithm. It is possible to change the algorithm to hold values until they can be evenly divided, but this approach suits this demonstration.

When we iterate through ^{n}C_{n/2}, we don't run into any issues until n = 68. A future post will address calculating permutations beyond the limits of standard .NET data types.

A divisor (D) of a number (N) is an integer that divides another integer with no remainder. In other words N mod D = 0.

Sometimes, we are interested in finding all divisors of a number. If we are considering a programming solution to this problem, chances are we are dealing with very large numbers or very large collections of numbers. In either case, we want to use an algorithm that is correct and efficient.

An appropriate instinctual approach in C# would look something like this:

static List GetDivisors(int number) { var divisors = new List(); for(int i = 2; i < number; ++i) { if(number % i == 0) { divisors.Add(i); } } return divisors; }

If you read my post on Prime Number Testing, you’ll know that Int32.Max_Value is prime. To test the efficiency of GetDivisors, I am going to use Int32.Max_Value since it is big and at least has one even divisor. I will use the following console app to test the GetDivisors method:

static void Main(string[] args) { var stopwatch = new Stopwatch(); stopwatch.Start(); int number = int.MaxValue - 1; var divisors = GetDivisors(number); stopwatch.Stop(); Console.WriteLine("Time Elapsed: " + stopwatch.Elapsed); Console.Write( number.ToString() + " has " + divisors.Count.ToString() + " divisors."); Console.ReadLine(); }

Time Elapsed: 00:00:10.0889733

2147483646 has 190 divisors.

10 seconds hurts. If we were using Int64.Max_Value – 1, it would take several thousand years to complete this computing. This blog will look at numbers that exceed Int64.Max_Value, so we are going to need to tune this method.

We know from Prime Number Testing that the greatest possible divisor (GPD) of a number cannot exceed that number divided by its least possible divisor (LPD). Armed with this information, we can change the algorithm, so that GetDivisors() does not check numbers greater than the GPD.

static List GetDivisors(int number) { var divisors = new List(); int GPD = number; bool foundGPD = false; for(int i = 2; i <= GPD; ++i) { if(number % i == 0) { divisors.Add(i); if (!foundGPD) { GPD = number / i; foundGPD = true; } } } return divisors; }

We’ll start by setting the GPD = N / 1. We’ll add a false condition that the GPD has been identified. When we find the first divisor, we’ll set the GPD.

Time Elapsed: 00:00:05.1988065

2147483646 has 190 divisors.

This is an improvement, but we can still do better. Whenever we find a divisor, we’ve also identified another divisor. In the case of discovering an LPD, we’ve also found a GPD.

For example, the number here are the divisors for 20 written in ascending order: {2, 4, 5, 10}. Since we know 20 mod 2 = 0 then we also know that 20 mod 10 = 0 (ie 20 mod (20 /2) = 0)! Visually we can see that each pair of divisors is the same number of elements from the middle of the set. So we can improve our algorithm to add 2 divisors for every 1 divisor found. Further, for each divisor found, we can decrease the maximum number to check in our for loop.

static List GetDivisors(int number) { var divisors = new List(); int newMax = number; for (int i = 2; i < newMax; ++i) { if (number % i == 0) { divisors.Add(i); divisors.Add(number / i); newMax = number / i; } } return divisors; }

Time Elapsed: 00:00:00.0006397

2147483646 has 190 divisors.

We’ve improved the efficiency of the algorithm for Int32 – 1 from over 5s to less than 1ms. But wait, we missed something. What happens if we run the number 36 through GetDivisors()?

Time Elapsed: 00:00:00.0003113

36 has 8 divisors.

Let’s check this out. The divisors of 36 are (2, 3, 4, 6, 9, 12, 18). That’s only 7, so what gives? 36 is a perfect square, so when we add it i = 6, we’re also adding number / i = 6. Adding a quick check on this condition will fix us up.

static List GetDivisors(int number) { var divisors = new List(); int newMax = number; for (int i = 2; i < newMax; ++i) { if (number % i == 0) { int divisor2 = number / i; divisors.Add(i); if(divisor2 != i) { divisors.Add(number / i); } newMax = number / i; } } return divisors; }

Time Elapsed: 00:00:00.0003326

36 has 7 divisors.

The prime factorization of a positive integer is defined as a list of the integer’s prime factors, together with their multiplicities.

Some examples of prime factorization are:

20 = 2² * 5

27 = 3³

88 = 2³ * 11

156 = 2² * 3 * 13

A trivial example of prime factorization is a prime, which is equal to itself.

We can use the GetDivisors method as a starting point for GetPrimeFactors, however GetPrimeFactors will need to account for multiplicities – ie For a prime factor p of N, the multiplicity of p is the largest exponent a for which p&supa; divides N exactly. The multiplicity of a number will be greater than one when the number has a divisor that is a perfect square. For example 4 is a divisor of 20 and 2² is a member of its prime factorization.

Before we write a method for GetPrimeFactors, lets look at a common approach done by hand. We’ll look for the prime factors of 20 and we’ll start with the lowest prime, 2.

20 mod 2 = 0 ✓

20 / 2 = 1010 mod 2 = 0 ✓

10 / 2 = 55 mod 2 = 1 x

5 mod 3 = 2 x

5 mod 5 = 0 ✓

5 / 5 = 1

The trick is to keep checking if a prime evenly divides our number. If the number divides without a remainder, record the result, then check again that it can be divided by the prime. Unsurprisingly the three modulo checks that result in 0 are the prime factors of 20. Also take note that we don’t actually need to know if a number is prime or not to use this approach. As long as we start with 2 and we exhaust the modulo checks for each number, non-prime numbers will not divide without remainder since their prime factors were exhausted in earlier tries.

The trick to this algorithm is using a while loop. While the number is divisible by p, we’ll add p to the PrimeFactors list and we’ll divide our number by p. As more ps are found the number gets smaller and as a result the for loop gets smaller.

static List GetPrimeFactors(int number) { var primeFactors = new List(); for (int p = 2; p < number; ++p) { while (number % p == 0) { primeFactors.Add(p); number = number / p; } } return primeFactors; }

A test of the int.Max_Value – 1 yields a result in less than 1 ms.

]]>Time Elapsed: 00:00:00.0002345

P(2147483646) = {2, 3, 3, 7, 11, 31, 151, 331}

static bool IsPrime(int number) { for(int i = 2; i < number; ++i) { if(number % i == 0) { return false; } } return true; }

There are a couple problems with this approach. First off the trivial test for IsPrime(2) yields false. Also the C# int data type has positive and negative numbers, so there will need to be a check that the number is positive, but since we know 2 is the smallest prime, we can just check if the number is less than 2. (For more sophisticated use cases, exception handling might be needed for negative numbers.)

static bool IsPrime(int number) { if (number < 2) return false; if (number == 2) return true; for(int i = 2; i < number; ++i) { if(number % i == 0) { return false; } } return true; }

So, this function is pretty good, right? By the mathematical definition of prime numbers there is nothing wrong with this function, but a simple test shows us that it is very slow to handle large numbers.

static void Main(string[] args) { var stopwatch = new Stopwatch(); stopwatch.Start(); for(int i = int.MaxValue; i>0; i--) { if (IsPrime(i) == true) { stopwatch.Stop(); Console.WriteLine(i.ToString() + " found in a time of " + stopwatch.Elapsed); Console.WriteLine("Total numbers tested: " + (int.MaxValue - (i - 1)).ToString()); } } }

2147483647 found in a time of 00:00:08.5264074

Total numbers tested: 1

Done

Ouch. Now imagine if we chose to use long instead of int. (Protip: With modern processing, it would take about 6000 years to run this algorithm against the largest Int64 prime. Don’t try this at home, kids.) This is not a function I would add to a deliverable! But fear not! We can math this up a bit and runtime will thank us.

We’ll start by looking at the number 20.

Other than the trivial divisors, 20 can be evenly divided by four numbers listed in ascending order: 2, 4, 5, 10.

Now let’s look at 45: 3, 5, 9, 15.

Finally, observe 77: 7, 11.

Note that the product of the outside divisors is the original number. This implies that a number (N) divided by its least possible divisor (LPD) equals it’s greatest divisor (GPD). In other words the greatest possible divisor of a number cannot exceed that number divided by its least possible divisor.

What this tells us is that if a number is not prime that we have information about it’s greatest divisor – namely that: GPD=N/LPD. **As we iterate through numbers to identify the LPD-GPD pair, the pool of numbers that could contain the GPD gets smaller.** Right away it’s clear that since an odd number is not divisible by 2 that its GPD must be less than N/2. Look at 45. (Note that for the illustration decimal values are used. Integer values are rounded down and just as correct.)

45 mod 2 = 1 (ie 45 / 2 = 22 with **1** remaining)

The GPD must be less than 22.5.

Try 3.

45 mod 3 = 0

45/3 = 15. GD = 15.

Look at 77.

77 mod 2 = 1

The GPD must be less than 38.5.

77 mod 3 = 2

The GPD must be less than 25.6667.

77 mod 4 = 1

The GPD must be less than 19.25.

77 mod 5 = 2

The GPD must be less than 15.4

77 mod 6 = 5

The GPD must be less than 12.8333

77 mod 7 = 0

The GPD is 11.

If a number is prime we can apply the same algorithm to reduce the number of tries. For every try at identifying an LPD the maximum GPD is N / LPD. We will start off by defining GPD as the number, ie the trivial N/1. As LPDs increase, GPDs will decrease.

static bool IsPrime(int number) { int GPD = number; if (number < 2) return false; if (number == 2) return true; for(int LPD = 2; LPD < GPD; ++LPD) { if(number % LPD == 0) { return false; } GPD = number / LPD; } return true; }

2147483647 found in a time of 00:00:00.0004447

Total numbers tested: 1

Done

This is much better. We reduced the GPD from 2,147,483,647 to 46,341 and the efficiency of the algorithm for the maximum integer has improved from over 8s to less than 1ms.

]]>