Project Euler Problem 2 instructs:
Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, …
By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.
They’ve already given you the definition of the Fibonacci sequence (except it is usually written as starting with 0,1 or 1,1). So the first thing to figure out is how do we find the sequence of even-valued terms.
Luckily, there is a pretty easy pattern here. Let’s start by writing the Fibonacci sequence as a formula:
where
So starting by substituting we get . Since both and are odd valued, we’ll replace them with . So now we have:
Two odd numbers always add to an even number, so let’s replace with . Resulting in:
So this is the start of our pattern. Now, let’s find the next equation in the pattern.
Substitute and :
An even and and odd number will always add to an odd number. So we have:
Repeat for and we have:
Once more for results in:
Which is where we started with . Since each Fibonacci number only depends on the previous 2 numbers, this sequence of even and odd numbers will repeat forever.
Every third term is an even number starting with .
The most straight-forward way to implement this in C# would be to calculate each Fibonacci number and add up every third number until we reach our limit:
public static long SolveA() { long n1 = 1; long n2 = 2; long total = 0; while (n2 <= 4000000) { total += n2; long n3 = n1 + n2; long n4 = n2 + n3; long n5 = n3 + n4; n1 = n4; n2 = n5; } return total; }
Now we’re going to discuss the first optimizaiton. Since we only need to add up every third Fibonacci number, we don’t actually need the other two in the sequence.
Once again, let’s start with our Fibonacci sequence formula:
Instead of being based on the previous two numbers, which we don’t care about, let’s see if we can rewrite it in terms of the numbers we do care about. That would be the other even numbers, which occur in every third place. So we’d like to express in terms of and .
Let’s start by substituting
Simplify:
is useful, but we still need to break down :
Simplify:
This part gets a bit tricky. We’re going to substitute only one and leave the other one to use later:
Now we substitute back in place of to get:
Simplify:
There you have it. We now have our Fibonacci sequence, except that it only spits out the even terms.
Here is the C# code:
public static long SolveB() { long n1 = 2; long n2 = 8; long n3 = 34; long total = 10; while (n3 <= 4000000) { total += n3; n1 = n2; n2 = n3; n3 = (n2 << 2) + n1; } return total; }
Note: the n2 << 2
is just a more efficient way to multiply by 4.
Ok, so there's actually a way to do this without even keeping a running total. We still have to calculate the Fibonacci sequence, but we don't have to explicitly add up the even terms. We'll do this with a trick similar to the one in Project Euler Problem 1.
What we're trying to solve is the total that we'll call :
Let's start by taking the end of Solution B:
This will be much easier to work with if we add 6 to the indices and get:
And then solve for :
Now let's expand the values of that we want to sum for our solution:
Note: I know that is equal to zero, but having it here it will make things easier later.
First, you can see that the left side of these equations add up to the total we are looking for. They're the list of all the even Fibonacci numbers. And we can also see a pattern on the right side. The terms in the middle partially cancel each other out leaving negative three of each term. So it ends up looking like this:
We can simplify this with a summation expression as:
Substitute the summation for :
And solve for :
We now have the sum of our Fibonacci sequence in terms of the sequence itself.
In code form:
public static long SolveC() { long n1 = 2; long n2 = 8; long n3 = 34; while (n3 <= 4000000) { n1 = n2; n2 = n3; n3 = (n2 << 2) + n1; } n1 = (n3 << 2) + n2; long total = (n1 - 3 * n3 - 2) >> 2; return total; }
The solutions we've developed so far have all had a linear time complexity with respect to the maximum sequence value (4000000 in our case). However, it is possible to do better than that. I'm not going to go into a lot of detail, because this is probably beyond the skill level expected just for problem 2 on Project Euler.
It is actually possible to find the nth Fibonacci number with the following matrix formula:
And there are tricks to do the matrix exponent in logarithmic time, which I won't get into yet. But that's not the whole problem. Our problem statement doesn't actually want us to get the nth Fibonacci number. It wants us to sum up to the number whose value does not exceed a limit. So you'd have to use this matrix exponentiation to do a binary search to find which value of n does not exceed the limit where n + 3 does. You could then use this in our Solution C.
Inspiration for this solution came from this link.
Another improvement on the previous solution can find the nth Fibonacci number in constant time. It's called Binet's formula. In fact, any constant-recursive sequence can be solved this way. However, the binary search from the matrix solution would still be needed to find the index in the Fibonacci sequence that doesn't exceed the limit.
Here is the formula:
This could also be used in Solution C to find the desired sum.
You can find the code on GitHub (complete with unit tests) at https://github.com/josiahwood/ProjectEuler/tree/master/Problem2.
]]>Project Euler Problem 1 instructs:
If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.
This ends up being a basic math problem, so no code is needed yet.
I think the easiest way to think of this is as a variation on triangular numbers. Triangular numbers are the sums of the first natural numbers. Which is a very similar description to our problem statement. The initial formula for triangular numbers is:
Expanded, this looks like:
If we do a little trick here, then we can simplify this expression considerably. Take the above expansion, reverse it, and add it to the original expansion term by term.
If we simplify the repeated terms we get:
And then solve for :
Great! Now we really have something that we can use in our solution. Since the problem asked for the sum of natural numbers below 1000 let us subtract 1 to get 999. Then divide that by (which will represent the increment being summed, either 3 or 5 in this problem). Last, scale by so that we are adding up multiples of the increment instead of just sequential natural numbers. So if represents the upper limit provided (1000), then we get this equation:
Now substitute the final equation for :
However, we need the sum of numbers that are either multiples of 3 or 5. So we have to add both of those results and then subtract the intersection that would otherwise be counted twice.
It would have arguably been simpler to just write a quick program that loops through all natural numbers less than 1000 and total any value that is a multiple of 3 or 5. However, thinking about this problem mathematically is good practice for what is up ahead. In future problems you will not have the luxury of using an algorithm that takes linear time instead of constant time.
However, if you’d really like to see this in code, then here is my C# example:
using System; namespace Problem1 { public class Program { static void Main(string[] args) { int result = Solve(1000); Console.WriteLine(result); } public static int Solve(int limit) { return Sum(limit, 3) + Sum(limit, 5) - Sum(limit, 15); } static int Sum(int limit, int increment) { int n = (limit - 1) / increment; return increment * n * (n + 1) / 2; } } }
You can find the code on GitHub (complete with a unit test) at https://github.com/josiahwood/ProjectEuler/tree/master/Problem1.
]]>There are twelve men on an island. Eleven weigh exactly the same amount, but one of them is slightly lighter or heavier. You must figure out which. The island has no scales, but there is a seesaw. You can only use it three times.
This isn’t exactly a programming post, but it does demonstrate the same kind of problem solving that is often found in devising efficient algorithms.
I decided to represent each islander with a letter from A to L. This makes it easy to unambiguously and concisely represent combinations of islanders. I show each trial with the seesaw as a column. The “Test” column shows what islanders are put on the seesaw as left vs. right. The “Result” column shows “=” if both sides were equal, “<” if the left side weighed less than the right, or “>” if the left side weighed more than the right. Then the “Possibilities” column shows what possible solutions remain given the result of the trial. As we move from the 1st trial towards the 3rd we can see the remaining possibilities reduce as more results eliminate solutions.
When solving this puzzle we know right away that we cannot rely on the seesaw always tilting to the left or right. This seems to be people’s initial attempt at solving this problem. Just put six on one side and six on the other and work your way down. However, 3 trials and only 2 possible results would only give us up to 8 solutions because 2 x 2 x 2 = 8. So we know that we have to rely some trials being balanced to find all the solutions. Therefore, it is probably not a good idea to put six vs. six because there is no way that seesaw could be balanced since the different islander would always be on the seesaw giving only two possible results which doesn’t tell us which side the different islander is on.
The first trial that gives the most information is to do four vs. four (ABCD vs. EFGH). No matter what the result we have reduced the possible solutions from 24 to 8.
Possibilities | Test | Result | Possibilities |
---|---|---|---|
A lighter A heavier B lighter B heavier C lighter C heavier D lighter D heavier E lighter E heavier F lighter F heavier G lighter G heavier H lighter H heavier I lighter I heavier J lighter J heavier K lighter K heavier L lighter L heavier |
ABCD vs. EFGH | = |
I lighter I heavier J lighter J heavier K lighter K heavier L lighter L heavier |
< |
A lighter B lighter C lighter D lighter E heavier F heavier G heavier H heavier |
||
> |
A heavier B heavier C heavier D heavier E lighter F lighter G lighter H lighter |
What we do for the second trial depends on the type of result for the first trial because the pattern of remaining possibilities is different.
Possibilities | Test | Result | Possibilities |
---|---|---|---|
I lighter I heavier J lighter J heavier K lighter K heavier L lighter L heavier |
IJK vs. ABC | = |
L lighter L heavier |
< |
I lighter J lighter K lighter |
||
> |
I heavier J heavier K heavier |
Possibilities | Test | Result | Possibilities |
---|---|---|---|
I lighter J lighter K lighter |
I vs. J | = | K lighter |
< | I lighter | ||
> | J lighter |
Possibilities | Test | Result | Possibilities |
---|---|---|---|
A lighter B lighter C lighter D lighter E heavier F heavier G heavier H heavier |
ABE vs. CDF | = |
G heavier H heavier |
< |
A lighter B lighter F heavier |
||
> |
C lighter D lighter E heavier |
Here is the full solution:
1st Trial | 2nd Trial | 3rd Trial | ||||||
---|---|---|---|---|---|---|---|---|
Test | Result | Possibilities | Test | Result | Possibilities | Test | Result | Possibilities |
ABCD vs. EFGH | = | I lighter I heavier J lighter J heavier K lighter K heavier L lighter L heavier |
IJK vs. ABC | = |
L lighter L heavier |
L vs. A | = | Impossible |
< | L lighter | |||||||
> | L heavier | |||||||
< | I lighter J lighter K lighter |
I vs. J | = | K lighter | ||||
< | I lighter | |||||||
> | J lighter | |||||||
> | I heavier J heavier K heavier |
I vs. J | = | K heavier | ||||
< | J heavier | |||||||
> | I heavier | |||||||
< | A lighter B lighter C lighter D lighter E heavier F heavier G heavier H heavier |
ABE vs. CDF | = | G heavier H heavier |
G vs. H | = | Impossible | |
< | H heavier | |||||||
> | G heavier | |||||||
< | A lighter B lighter F heavier |
A vs. B | = | F heavier | ||||
< | A lighter | |||||||
> | B lighter | |||||||
> | C lighter D lighter E heavier |
C vs. D | = | E heavier | ||||
< | C lighter | |||||||
> | D lighter | |||||||
> | A heavier B heavier C heavier D heavier E lighter F lighter G lighter H lighter |
ABE vs. CDF | = | G lighter H lighter |
G vs. H | = | Impossible | |
< | G lighter | |||||||
> | H lighter | |||||||
< | C heavier D heavier E lighter |
C vs. D | = | E lighter | ||||
< | D heavier | |||||||
> | C heavier | |||||||
> | A heavier B heavier F lighter |
A vs. B | = | F lighter | ||||
< | B heavier | |||||||
> | A heavier |
After creating your Cloud9 workspace run this script (we’ll break it down afterward):
#!/bin/bash # pyethereum cd ~ git clone https://github.com/ethereum/pyethereum/ cd pyethereum sudo python setup.py install # eth-testrpc sudo pip install eth-testrpc # nodejs v5.0.0 . ~/.nvm/nvm.sh nvm use v5.0.0 nvm alias default 5.0.0 # truffle sudo npm install -g truffle
You can find the latest version of this script in my Futarchy project at https://github.com/josiahwood/futarchy/blob/master/cloud9setup.sh.
# pyethereum cd ~ git clone https://github.com/ethereum/pyethereum/ cd pyethereum sudo python setup.py install
Lines 5-7 are just copy/pasted from the installation section of https://github.com/ethereum/pyethereum/. It works perfectly as advertised. This is required for testrpc
to work in the next section.
# eth-testrpc sudo pip install eth-testrpc
Line 10 is all you need to install testrpc
according to https://github.com/ConsenSys/eth-testrpc. I’ve found Testrpc to be the quickest way to get up and going with writing contracts.
# nodejs v5.0.0 . ~/.nvm/nvm.sh nvm use v5.0.0 nvm alias default 5.0.0
Since Truffle uses npm
you’ll want to put Nodejs at your desired version first. I found version 5.0.0 worked very well with truffle
. Lines 13-15 were the most consistent way I found to update Nodejs and make the new version the default. Line 15 is important to make sure all new Cloud9 terminals are using the correct version of Nodejs by default. This also affects what happens to Cloud9 terminals if you’ve been logged off for a while. Cloud9 advertises that your environment will be exactly the way you left it, but the terminals do reset after some timeout.
# truffle sudo npm install -g truffle
At this point line 18 works exactly as documented in the installation section of Truffle at https://github.com/ConsenSys/truffle.
Ok, this part took me a while to get working on Cloud9. I had no problems running on a local Ubuntu virtual machine, but I wasn’t so lucky on Cloud9. You can see how I worked out the problem on StackOverflow at Ethereum Test RPC working in Cloud9 with Truffle. Credit for the final answer goes to XMLHttpRequest cannot load cloud 9 io.
In order to make it possible for testrpc
to bind to the public IP address of your Cloud9 environment you’ll need to click on the “Share” button in the upper right-hand corner of the Cloud9 IDE and then make the Application link public. The StackOverflow answer indicated that this was due to a recent bug in Cloud9, so this may not be necessary in the future.
Once you have some code/contracts you want to try out you’ll need to start testrpc
first. Do this by executing testrpc -p 8081 -d 0.0.0.0
in a new Cloud9 terminal. The -p 8081
argument tells testrpc
to run on port 8081. Cloud9 only opens a few ports for public access according to https://docs.c9.io/docs/multiple-ports so we can’t use the testrpc
default of 8545. Port 8080 will be used later by the truffle serve
command, so 8081 is the next open port. The -d 0.0.0.0
argument tells testrpc
to bind to all available IP addresses, including the public one.
Now that you have testrpc
up and running, you’ll need to configure your Truffle project properly. You can do this a few ways but the key is to get this part in your config file:
"rpc": { "host": "project-user.c9users.io", "port": 8081 }
Replace project-user.c9users.io
with the Application URL you made public in the “Cloud9 Environment” section above.
You can either update this in your config/app.json
file (to make it the default for all environments), one of the existing config/[environment]/config.json
files, or make a new environment just for Cloud9.
After deploying your contracts with truffle deploy
you can now run truffle serve
and your HTML/JavaScript will be visible on port 8080 of your Application URL. Since this is conveniently the default port for Cloud9 it automatically gets redirected from port 80. This allows you to see your project directly at your Application URL like “http://project-user.c9users.io”.
Thanks for reading! Get coding!
]]>