Sunday, July 10, 2011

Project Euler - Problem 16

Welcome to week 16 of my Project Euler series.

Problem 16:
What is the sum of the digits of the number 21000?

Details:
215 = 32768 and the sum of its digits is 3 + 2 + 7 + 6 + 8 = 26.
What is the sum of the digits of the number 21000?


Code:
//What is the sum of the digits of the number 21000?
class Problem0016
{
static void Main(string[] args)
{
const int numberBase = 10; // The base that we are going to be using (2, 10, and 16 are the most common)
const int power = 1000; // The power that will be used.

int arraySize = power / (numberBase * 3); // The size of the array can be approximated to the power / base * 3
int[] bigNumber = new int[arraySize + 1];
bigNumber[0] = 2; // Our big number will start as 2.

for (int i = 2; i <= power; i++)
{
DoubleNumber(bigNumber);
}

var numberAsString = NumberToString(bigNumber);
Console.WriteLine("The sum of digits of 2^" + power + " is " + SumTheDigits(numberAsString));

Console.Read();
}

private static void DoubleNumber(int[] n)
{
int carry = 0;
for (int i = 0; i < n.Length; i++)
{
n[i] <<= 1;
n[i] += carry;
if (n[i] >= 1000000000)
{
carry = 1;
n[i] -= 1000000000;
}
else
{
carry = 0;
}
}
}

private static String NumberToString(int[] n)
{
int i = n.Length;
while (i > 0 && n[--i] == 0)
;
String ret = "" + n[i--];
while (i >= 0)
{
ret += String.Format("{0:000000000}", n[i--]);
}
return ret;
}

private static int SumTheDigits(string text)
{
int returnValue = 0;
foreach (var number in text)
returnValue += int.Parse(number.ToString());
return returnValue;
}
}

Solution:
This one requires a little more digging into what was learned long long ago in math.  It also involves thinking about more than just "int" or "long" to get it figured out. We have once again a number that is going to be well beyond the scale of what is available to us for numeric data types.  That hasn't stood in our way before and it won't this time either. We are going to take a, once again, different approach to things.  This time we are going to work with the number as if it were an array.  The trick is that we are going to work with an array of 9 digit numbers (numbers under 1 billion).

As we all might or might not know a signed int has a value right around +- 2 billion. That would be a bit awkward to work with if we are going to be using the various elements of the array for continuous calculations.  We can however put a cap on the number that we use where ever we want to. If we wanted to work with over sized arrays, we could just as easily have worked with the 3 digit numbers (under 1 thousand).  For simplicity though, we went with the largest number that .NET data types would allow.

If you check out the DoubleNumber method you will get a better idea of what I mean. I am doing binary addition to each number in the array. If there is something that goes over our 9 digit cap that we self imposed, then it is used as a carry over into the next element of the array.  At the end we get a truly impressive sized number to work with.  That is also why we have to transfer it into a string in order to add all the digits.  I suppose technically it would have worked to just pace through the array and do it that way, but I had implemented the method to turn it into a string in order to check my output during testing and solving of it, so I just left it in and used it here.

This brings me to another good point. I decided to start breaking this code up into methods for two reasons. First off, I was having some issues when I was trouble shooting the code and wanted to get things a little more spread out and easier to work with. Once I was able to get the return value I wanted and expected from a method then I knew that that chunk of code worked.  I could safely move on and let the method enjoy a long and happy life.  That actually ties directly into the second reason; Readability. To really prove this point, I will put some extra code onto my Google code repository.  I could have very easily squeezed all this code into the main method and not had to worry about making sure my method calls were right and my return values were what I wanted. The problem is that I would have then had a very hard task ahead of me when I started trying to isolate code for debugging purposes. It would have been possible (and shorter) but the readability and future maintenance would have suffered. I like to consider the rule of thumb that a method should fit comfortably on your screen.  Of course buying a bigger screen is not the solution if it doesn't but rather trying to take out some code that both, logically fits together, and is capable of standing alone without causing issues. Since it is a rule of thumb it is not necessary to do, but rather nice if possible.  It allows you to focus one line of thought on the screen at a time.

Until next time...

Enjoy!

Sunday, July 3, 2011

Project Euler - Problem 15

Welcome to week 15 of my Project Euler series.

Problem 15:
Starting in the top left corner in a 20 by 20 grid, how many routes are there to the bottom right corner?

Details:
Starting in the top left corner of a 2×2 grid, there are 6 routes (without backtracking) to the bottom right corner.
How many routes are there through a 20×20 grid?


Code:
// Starting in the top left corner in a 20 by 20 grid, how many routes are there to the bottom right corner?
class Problem0015
{
static void Main(string[] args)
{
Console.WriteLine(binomialCoefficient(40,20));

Console.Read();
}

public static long binomialCoefficient(int n, int k)
{
long c = 1;

for (int i = 0; i < k; i++)
{
c = c * (n - i);
c = c / (i + 1);
}

return c;
}
}

Solution:
The code for this one is short, sweet, and to the point.  The journey there was not however. This one required a lot of research on my part to come to the answer. Once I was there and it all started to come back however it was painfully obvious. I really hate how hindsight has 20/20 vision. It would be so much more useful to have that kind of clarity before hand.

What I ended up going with was the equation for a simple Binomial Equation as it was the fastest of the options available and the most straight forward. I simply had to implement {n \choose k+1} = \frac{n-k}{k+1} {n \choose k}  in C#.  Simple right?  Once you have a grasp of C# and binomial coefficients it really is.  The only real question that was left over was, what are n and k in this example?  In reading some interesting articles about path finding, I found people using this equation to solve the number of paths from one side of a 2d map to the other.  k was the length of on of the sides and n was the number of steps from one side to the other.  Plop those in the proper place in your equation and you have yourself an answer.

Until next time...

Enjoy!

Wednesday, June 29, 2011

Variable names are overly important

As you might or might not have read in my previous post (Project Euler Problem 14) I value a good variable name above a great many other things in life.  That might seem a bit exaggerated, but really it isn't. If you need proof, then break out some of your old code (by old I mean more than 12 months without having looked at it) and try to remember what you were doing and why.  It is not nearly as easy as you might think. The problem is, projects come and go for programs pretty rapidly.  Those past projects, no matter how brilliant they were at the time, are simply that. They are past projects.  Most programmers don't take the time, when they stop work on a project, to document what exactly they were planning for it, working on, struggling with, etc.  Most projects are abandoned for something bigger and better. It is the nature of working in a cutting edge field. There is always that allure to the bigger and better. That is where the value of variable names can really shine through (if properly implemented with other good practices that I will mention at the end of this).

decimal x = 100.00m;
decimal y = 2.34m;
x*=y;

What are those three (perfectly legal) C# statements doing?  Could be anything really and if a method has a chunk like that with some error checking and a return statement you are going to be left in the dark.

decimal capital = 100.00m;
decimal interest = 2.34m;
capital *= interest;

What are those three (perfectly legal) C# statements doing?  Well as you can plainly see they are implementing a gross simplification of interest calculations. The example might not be the best, but the principle stands.  With proper variable names the logic of the code can make more sense.

Aside from just helping improve the logic, well thought out variable names can also cut back on the number of  comments that you have to write. In the second example that I gave above I could have added in comments stating that I was calculating interest, but they are not at all needed. The vast majority of people that can program have had enough math that they would recognize that.  I would even go so far as to say that it is more often true, rather than not, that properly improved variable names will reduce the amount of commenting that is needed.

This practice of using well thought out variable names can actual lead to better comments that are actually read instead of just skipped over. If you are replacing good naming conventions with comments that means that the person reading your code has to take a break from the code and read the comments to see what this particular variable is supposed to do or supposed to represent.  I think it is pretty obvious why this is a bad thing.

Aside from good variable names, the readability of your code can be improved by including comments where needed to clarify what is not evident from the code itself, keeping method size down so it can all be viewed on the screen at once, and experience. I know the last one is kind of mean, but it is true. Sometimes only experience can teach the student what the teacher knows.

Until next time...

Enjoy!

Sunday, June 26, 2011

Project Euler - Problem 14

Welcome to week 14 of my Project Euler series.

Problem 14:
Find the longest sequence using a starting number under one million.

Details:
The following iterative sequence is defined for the set of positive integers:

n n/2 (n is even)
n 3n + 1 (n is odd)

Using the rule above and starting with 13, we generate the following sequence:
13 40 20 10 5 16 8 4 2 1

It can be seen that this sequence (starting at 13 and finishing at 1) contains 10 terms. Although it has not been proved yet (Collatz Problem), it is thought that all starting numbers finish at 1.

Which starting number, under one million, produces the longest chain?
NOTE: Once the chain starts the terms are allowed to go above one million.



Code:
// Find the longest sequence using a starting number under one million.
class Problem0014
{
static void Main(string[] args)
{
int startOfLongest = 0;
int longestChain = 0;

for (int i = 1; i < 1000000; i++)
{
int chainLength = 0;
long n = i;
while (n > 1)
{
if (n % 2 == 0)
n /= 2;
else
n = (3 * n) + 1;
chainLength++;
}
if (chainLength > longestChain)
{
longestChain = chainLength;
startOfLongest = i;
}
}

Console.WriteLine(startOfLongest);
Console.Read();
}
}

Solution:
This is an interesting problem. We have the solution given to us, we just have to use it to find right answer. The algorithm that we are following is that we will divide an even number by 2 and triple an odd number then add one.  We will continue this pattern until we get the number down to one. This seems easy enough, except that we are looking for the number with the longest chain.  Theoretically every number will end up at one so we are looking for the one that takes the longest to get there.

We got our starting point (one) and our ending point (999,999) and our route.  Now to implement it.  We obviously need a loop (in the shape of a "while" since we don't actually have a set number of iterations to complete) and a couple of "if" statements in the middle to handle each situation. The rest is simply translating the algorithm into source code.  This is a very important skill to master, because most people that are going to tell you what they want from programs are not going to tell you in source code, or even easily translated pseudo code.  If they were already that far along in the software development cycle they would most likely finish it themselves!

One final note before I finish this off for today.  n is a terrible variable name! I used it simply to conform to the question and make it easier for people who are struggling to understand the logic to link it to the question. I can not stress enough the need for proper, logical, understandable, and valuable variable names. These little things can make or break the readability, and by extension the maintainability, of your code.

Until next time...

Enjoy!

Sunday, June 19, 2011

Project Euler - Problem 13

Welcome to week 13 of my Project Euler series.

Problem 13:
Find the first ten digits of the sum of one-hundred 50-digit numbers.

Details:
Work out the first ten digits of the sum of the following one-hundred 50-digit numbers.

37107287533902102798797998220837590246510135740250
46376937677490009712648124896970078050417018260538
74324986199524741059474233309513058123726617309629
91942213363574161572522430563301811072406154908250
23067588207539346171171980310421047513778063246676
89261670696623633820136378418383684178734361726757
28112879812849979408065481931592621691275889832738
44274228917432520321923589422876796487670272189318
47451445736001306439091167216856844588711603153276
70386486105843025439939619828917593665686757934951
62176457141856560629502157223196586755079324193331
64906352462741904929101432445813822663347944758178
92575867718337217661963751590579239728245598838407
58203565325359399008402633568948830189458628227828
80181199384826282014278194139940567587151170094390
35398664372827112653829987240784473053190104293586
86515506006295864861532075273371959191420517255829
71693888707715466499115593487603532921714970056938
54370070576826684624621495650076471787294438377604
53282654108756828443191190634694037855217779295145
36123272525000296071075082563815656710885258350721
45876576172410976447339110607218265236877223636045
17423706905851860660448207621209813287860733969412
81142660418086830619328460811191061556940512689692
51934325451728388641918047049293215058642563049483
62467221648435076201727918039944693004732956340691
15732444386908125794514089057706229429197107928209
55037687525678773091862540744969844508330393682126
18336384825330154686196124348767681297534375946515
80386287592878490201521685554828717201219257766954
78182833757993103614740356856449095527097864797581
16726320100436897842553539920931837441497806860984
48403098129077791799088218795327364475675590848030
87086987551392711854517078544161852424320693150332
59959406895756536782107074926966537676326235447210
69793950679652694742597709739166693763042633987085
41052684708299085211399427365734116182760315001271
65378607361501080857009149939512557028198746004375
35829035317434717326932123578154982629742552737307
94953759765105305946966067683156574377167401875275
88902802571733229619176668713819931811048770190271
25267680276078003013678680992525463401061632866526
36270218540497705585629946580636237993140746255962
24074486908231174977792365466257246923322810917141
91430288197103288597806669760892938638285025333403
34413065578016127815921815005561868836468420090470
23053081172816430487623791969842487255036638784583
11487696932154902810424020138335124462181441773470
63783299490636259666498587618221225225512486764533
67720186971698544312419572409913959008952310058822
95548255300263520781532296796249481641953868218774
76085327132285723110424803456124867697064507995236
37774242535411291684276865538926205024910326572967
23701913275725675285653248258265463092207058596522
29798860272258331913126375147341994889534765745501
18495701454879288984856827726077713721403798879715
38298203783031473527721580348144513491373226651381
34829543829199918180278916522431027392251122869539
40957953066405232632538044100059654939159879593635
29746152185502371307642255121183693803580388584903
41698116222072977186158236678424689157993532961922
62467957194401269043877107275048102390895523597457
23189706772547915061505504953922979530901129967519
86188088225875314529584099251203829009407770775672
11306739708304724483816533873502340845647058077308
82959174767140363198008187129011875491310547126581
97623331044818386269515456334926366572897563400500
42846280183517070527831839425882145521227251250327
55121603546981200581762165212827652751691296897789
32238195734329339946437501907836945765883352399886
75506164965184775180738168837861091527357929701337
62177842752192623401942399639168044983993173312731
32924185707147349566916674687634660915035914677504
99518671430235219628894890102423325116913619626622
73267460800591547471830798392868535206946944540724
76841822524674417161514036427982273348055556214818
97142617910342598647204516893989422179826088076852
87783646182799346313767754307809363333018982642090
10848802521674670883215120185883543223812876952786
71329612474782464538636993009049310363619763878039
62184073572399794223406235393808339651327408011116
66627891981488087797941876876144230030984490851411
60661826293682836764744779239180335110989069790714
85786944089552990653640447425576083659976645795096
66024396409905389607120198219976047599490197230297
64913982680032973156037120041377903785566085089252
16730939319872750275468906903707539413042652315011
94809377245048795150954100921645863754710598436791
78639167021187492431995700641917969777599028300699
15368713711936614952811305876380278410754449733078
40789923115535562561142322423255033685442488917353
44889911501440648020369068063960672322193204149535
41503128880339536053299340368006977710650566631954
81234880673210146739058568557934581403627822703280
82616570773948327592232845941706525094512325230608
22918802058777319719839450180888072429661980811197
77158542502016545090413245809786882778948721859617
72107838435069186155435662884062257473692284509516
20849603980134001723930671666823555245252804609722
53503534226472524250874054075591789781264330331690

Code:
// Find the first ten digits of the sum of one-hundred 50-digit numbers.
class Problem0013
{
static void Main(string[] args)
{
var numbers = GetInput();
var list = from num in numbers
select double.Parse(num.Insert(25, "."));

var sum = list.Sum();
string answer = sum.ToString().Replace(".", "").Substring(0, 11);

Console.WriteLine(answer);
Console.Read();
}

static string[] GetInput()
{
return @"37107287533902102798797998220837590246510135740250
46376937677490009712648124896970078050417018260538
74324986199524741059474233309513058123726617309629
91942213363574161572522430563301811072406154908250
23067588207539346171171980310421047513778063246676
89261670696623633820136378418383684178734361726757
28112879812849979408065481931592621691275889832738
44274228917432520321923589422876796487670272189318
47451445736001306439091167216856844588711603153276
70386486105843025439939619828917593665686757934951
62176457141856560629502157223196586755079324193331
64906352462741904929101432445813822663347944758178
92575867718337217661963751590579239728245598838407
58203565325359399008402633568948830189458628227828
80181199384826282014278194139940567587151170094390
35398664372827112653829987240784473053190104293586
86515506006295864861532075273371959191420517255829
71693888707715466499115593487603532921714970056938
54370070576826684624621495650076471787294438377604
53282654108756828443191190634694037855217779295145
36123272525000296071075082563815656710885258350721
45876576172410976447339110607218265236877223636045
17423706905851860660448207621209813287860733969412
81142660418086830619328460811191061556940512689692
51934325451728388641918047049293215058642563049483
62467221648435076201727918039944693004732956340691
15732444386908125794514089057706229429197107928209
55037687525678773091862540744969844508330393682126
18336384825330154686196124348767681297534375946515
80386287592878490201521685554828717201219257766954
78182833757993103614740356856449095527097864797581
16726320100436897842553539920931837441497806860984
48403098129077791799088218795327364475675590848030
87086987551392711854517078544161852424320693150332
59959406895756536782107074926966537676326235447210
69793950679652694742597709739166693763042633987085
41052684708299085211399427365734116182760315001271
65378607361501080857009149939512557028198746004375
35829035317434717326932123578154982629742552737307
94953759765105305946966067683156574377167401875275
88902802571733229619176668713819931811048770190271
25267680276078003013678680992525463401061632866526
36270218540497705585629946580636237993140746255962
24074486908231174977792365466257246923322810917141
91430288197103288597806669760892938638285025333403
34413065578016127815921815005561868836468420090470
23053081172816430487623791969842487255036638784583
11487696932154902810424020138335124462181441773470
63783299490636259666498587618221225225512486764533
67720186971698544312419572409913959008952310058822
95548255300263520781532296796249481641953868218774
76085327132285723110424803456124867697064507995236
37774242535411291684276865538926205024910326572967
23701913275725675285653248258265463092207058596522
29798860272258331913126375147341994889534765745501
18495701454879288984856827726077713721403798879715
38298203783031473527721580348144513491373226651381
34829543829199918180278916522431027392251122869539
40957953066405232632538044100059654939159879593635
29746152185502371307642255121183693803580388584903
41698116222072977186158236678424689157993532961922
62467957194401269043877107275048102390895523597457
23189706772547915061505504953922979530901129967519
86188088225875314529584099251203829009407770775672
11306739708304724483816533873502340845647058077308
82959174767140363198008187129011875491310547126581
97623331044818386269515456334926366572897563400500
42846280183517070527831839425882145521227251250327
55121603546981200581762165212827652751691296897789
32238195734329339946437501907836945765883352399886
75506164965184775180738168837861091527357929701337
62177842752192623401942399639168044983993173312731
32924185707147349566916674687634660915035914677504
99518671430235219628894890102423325116913619626622
73267460800591547471830798392868535206946944540724
76841822524674417161514036427982273348055556214818
97142617910342598647204516893989422179826088076852
87783646182799346313767754307809363333018982642090
10848802521674670883215120185883543223812876952786
71329612474782464538636993009049310363619763878039
62184073572399794223406235393808339651327408011116
66627891981488087797941876876144230030984490851411
60661826293682836764744779239180335110989069790714
85786944089552990653640447425576083659976645795096
66024396409905389607120198219976047599490197230297
64913982680032973156037120041377903785566085089252
16730939319872750275468906903707539413042652315011
94809377245048795150954100921645863754710598436791
78639167021187492431995700641917969777599028300699
15368713711936614952811305876380278410754449733078
40789923115535562561142322423255033685442488917353
44889911501440648020369068063960672322193204149535
41503128880339536053299340368006977710650566631954
81234880673210146739058568557934581403627822703280
82616570773948327592232845941706525094512325230608
22918802058777319719839450180888072429661980811197
77158542502016545090413245809786882778948721859617
72107838435069186155435662884062257473692284509516
20849603980134001723930671666823555245252804609722
53503534226472524250874054075591789781264330331690".Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
}
}

Solution:
As imposing as this one looks, it is really not that bad at all. Granted if I would have been using .NET Framework 4.0 it would have been extremely easy.  In .NET 4.0 they decided to implement a new number type called BigInteger. That would have allowed me to simply add the numbers together and find the part that I wanted as if it were any other supported data type. Since I am using .NET Framework 3.5 I was without this ability so it took a little more work.  Not a lot though.

Even without BigInteger I was able to let the framework do the work for me. It was through a nuance that programming makes available to us. I converted these seemingly clear integers into doubles. I placed a decimal after the first 25 numbers, thereby "shrinking" the number down to usable size. I summed the list of "doubles" and took the part required by the problem.  Since we are dealing with addition here it didn't change the answer in the least if it was split in half by a decimal point, completely behind the decimal point, or left as an integer.  Sometimes it really pays off to take a step back and think before letting your fingers start coding.

Until next time...

Enjoy!