I'm doing an experiment trying to freshen up my unit-testing and learn dynamic programming. Every test passes but I'm curious of the result of some of them and worried if I'm doing the testing correctly. Currently I just got the one scenario for each method, with the input of 40, the goal of this test is to test which method is the most efficient for large numbers.
Test result are as follow:
BottomUp: 7 ms
DynamicFibonacci: 5 sec
DynamicFibonacci2: 8 ms
MatrixFibonacci: 7 ms
EfficientMatrix: 8 ms
PiVersion: 7 ms
RecursiveFibonacci: 4 sec
I understand the slowness of the recursive, that one makes sense since it does not store the value. The efficientmatrix is however only 1 ms faster and in some test the time even seem to be the same. DynamicFibonacci1 I don't get at all why it's so slow, it should be very similiar to version 2 but the time difference is huge. It's even slower than the recursive one where it does not memoize.
I ran all test individually since I noticed the result differed wildly otherwise, I believe the array value was stored when running them all at once, but that did not seem to be the case when debugging the test individually.
My question is, how can I improved my tests, both method wise and the testing process. It feels like I get slightly different test result just running them once, is there a better way to run several time in a for loop maybe? It just seems like I can't really compare the results this way.
Which method is generally the best to use when using large numbers/heavy calculations like this? I'm originally a mobile Xamarin developer and just trying to learn a bit more on how to speed up some of my other work.
TEST Class // sample method, all of them look the same except calling the different methods.
[TestClass]
public class FibonacciSequenceTest
{
private const long Number = 40;
private const long Result = 102334155;
private readonly FibonacciSequence fibonacciSequence;
public FibonacciSequenceTest()
{
fibonacciSequence = new FibonacciSequence();
}
[TestMethod]
public void MatrixFibonacciCalculatorTest()
{
// Act
var returnValue = fibonacciSequence.MatrixFibonacciCalculator(Number);
// Assert
long actual = returnValue;
Assert.AreEqual(actual, Result);
}
}
Class and methods
public class FibonacciSequence
{
private readonly long max = 1000;
private readonly long[] memoizedFibonacciNumbers;
public FibonacciSequence()
{
memoizedFibonacciNumbers = new[] { max };
}
#region MatrixFibonnaciCalculator
public long MatrixFibonacciCalculator(long n)
{
long[,] f = { { 1, 1 }, { 1, 0 } };
if (n == 0)
return 0;
PowerMatrix1(f, n - 1);
return f[0, 0];
}
/* Helper function that multiplies 2
matrices F and M of size 2*2, and puts
the multiplication result back to F[][] */
public void MultiplyMatrix1(long[,] F, long[,] M)
{
long x = F[0, 0] * M[0, 0] + F[0, 1] * M[1, 0];
long y = F[0, 0] * M[0, 1] + F[0, 1] * M[1, 1];
long z = F[1, 0] * M[0, 0] + F[1, 1] * M[1, 0];
long w = F[1, 0] * M[0, 1] + F[1, 1] * M[1, 1];
F[0, 0] = x;
F[0, 1] = y;
F[1, 0] = z;
F[1, 1] = w;
}
/* Helper function that calculates F[][]
raise to the power n and puts the result
in F[][] Note that this function is designed
only for fib() and won't work as general
power function */
public void PowerMatrix1(long[,] F, long n)
{
long i;
var M = new long[,] { { 1, 1 }, { 1, 0 } };
// n - 1 times multiply the matrix to
// {{1,0},{0,1}}
for (i = 2; i <= n; i++)
MultiplyMatrix1(F, M);
}
#endregion
#region EfficentMatrixFibonacciCalculator
public long EfficientMatrixFibonacciCalculator(long n)
{
var f = new long[,] { { 1, 1 }, { 1, 0 } };
if (n == 0)
return 0;
EfficientPowerMatrix(f, n - 1);
return f[0, 0];
}
public void EfficientPowerMatrix(long[,] F, long n)
{
if (n == 0 || n == 1)
return;
var M = new long[,] { { 1, 1 }, { 1, 0 } };
EfficientPowerMatrix(F, n / 2);
EfficientMultiplyMatrix(F, F);
if (n % 2 != 0)
EfficientMultiplyMatrix(F, M);
}
public void EfficientMultiplyMatrix(long[,] f, long[,] m)
{
long x = f[0, 0] * m[0, 0] + f[0, 1] * m[1, 0];
long y = f[0, 0] * m[0, 1] + f[0, 1] * m[1, 1];
long z = f[1, 0] * m[0, 0] + f[1, 1] * m[1, 0];
long w = f[1, 0] * m[0, 1] + f[1, 1] * m[1, 1];
f[0, 0] = x;
f[0, 1] = y;
f[1, 0] = z;
f[1, 1] = w;
}
#endregion
public int IterativeFibonacciCalculator(long number)
{
int firstNumber = 0, secondNumber = 1, result = 0;
if (number == 0) return 0; // To return the first Fibonacci number
if (number == 1) return 1; // To return the second Fibonacci number
for (var i = 2; i <= number; i++)
{
result = firstNumber + secondNumber;
firstNumber = secondNumber;
secondNumber = result;
}
return result;
}
public long RecursiveFibonacciCalculator(long number)
{
if (number <= 1)
{
return number;
}
return RecursiveFibonacciCalculator(number - 1) + RecursiveFibonacciCalculator(number - 2);
}
public long DynamicFibonacciCalculator(long number)
{
long result;
var memoArrays = new long[number + 1];
if (memoArrays[number] != 0) return memoArrays[number];
if (number == 1 || number == 2)
{
result = 1;
}
else
{
result = DynamicFibonacciCalculator(number - 1) + DynamicFibonacciCalculator(number - 2);
memoArrays[number] = result;
}
return result;
}
public long DynamicFibonacciCalculator2(long n)
{
// Declare an array to
// store Fibonacci numbers.
// 1 extra to handle
// case, n = 0
var f = new long[n + 2];
long i;
/* 0th and 1st number of the
series are 0 and 1 */
f[0] = 0;
f[1] = 1;
for (i = 2; i <= n; i++)
/* Add the previous 2 numbers
in the series and store it */
f[i] = f[i - 1] + f[i - 2];
return f[n];
}
// Helper method for PiCalculator
public long PiFibonacciCalculator(long n)
{
double phi = (1 + Math.Sqrt(5)) / 2;
return (long)Math.Round(Math.Pow(phi, n) / Math.Sqrt(5));
}
public long BottomUpFibonacciCalculator(long n)
{
long a = 0, b = 1;
// To return the first Fibonacci number
if (n == 0) return a;
for (long i = 2; i <= n; i++)
{
long c = a + b;
a = b;
b = c;
}
return b;
}
}