Showing posts with label python. Show all posts
Showing posts with label python. Show all posts

Sunday, 31 March 2013

Fibonacci numbers in Python and Java

Fibonacci numbers,  introduced by Leonardo Pisano Bigollo nearly a thousand years ago, were used to solve a fascinating puzzle about the growth of an idealised rabbit population. Below are the first 11 Fibonacci numbers Fn where n=0, 1, 2,..., 10:

sequenceF0F1F2F3F4F5F6F7F8F9F10
number011235813213455

or we can describe the Fibonacci numbers as
Fn = Fn-1 + Fn-2 (when n>=2)
To write a program to return a Fibonacci number given a sequence, there are many possible implementations. I shall present 2 solutions implemented in both Python and Java, where the first one is using recursion, and the second one is using a while loop, however, the performances of these 2 solutions are drastically different when the sequence is bigger.
Below are the results when running a Python test script. The recursion way is already showing very poor performance comparing to the while loop way.
> python test_fib.py 
.Working out Fibonacci(40)=102334155 recursively took 67.310534 seconds
.Working out Fibonacci(40)=102334155 with while loop took 1.8e-05 seconds
Working out Fibonacci(50)=12586269025 with while loop took 1.6e-05 seconds
Working out Fibonacci(60)=1548008755920 with while loop took 1.8e-05 seconds
..
----------------------------------------------------------------------
Ran 4 tests in 67.312s

OK
If we look at the results when running a similar timed test in Java, the same recursion is not as bad as in Python, though still far poorer than the while loop implementation.
> /somewhere/jdk1.6.0_33/bin/java -classpath ".:" fun.Fib
Working out Fibonacci(40)=102334155 recursively took 408 ms
Working out Fibonacci(40)=102334155 with while loop took 2033 ns
Working out Fibonacci(50)=12586269025 with while loop took 2115 ns
Working out Fibonacci(60)=1548008755920 with while loop took 2368 ns
The reason why recursion is slow in this case is because there are a lot of duplicated operations as the sequence grows. For example, to solve F(5), we need to solve F(4) once, F(3) twice, F(2) three times, and F(1) twice, as shown below

or the number of recursion required given a sequence n is
[1+(n-2)]*(n-2)/2 + (n-3) or n2/2-n/2-2  (when n>=4)
The complexity of using recursion is then actually O(n2), where, on the other hand, the while loop is O(n).
Below are the source code that are used in the test.
fib.py
import datetime


class Fib:
    def recursively(self, n):
        if n == 0:
            return 0
        elif n <= 2:
            return 1
        else:
            return self.recursively(n - 1) + self.recursively(n - 2)

    def whileLoop(self, n):
        if n == 0:
            return 0
        previous, fib, currentIndex = 0, 1, 1
        while currentIndex < n:
            previous, fib = fib, previous + fib
            currentIndex += 1

        return fib

    def timedRecursively(self, n):
        start = datetime.datetime.now()
        result = self.recursively(n)
        end = datetime.datetime.now()
        elapsed = end - start
        print "Working out Fibonacci({})={} recursively took {} seconds".format(str(n), str(result), str(elapsed.total_seconds()))
        return result

    def timedWhileLoop(self, n):
        start = datetime.datetime.now()
        result = self.whileLoop(n)
        end = datetime.datetime.now()
        elapsed = end - start
        print "Working out Fibonacci({})={} with while loop took {} seconds".format(str(n), str(result), str(elapsed.total_seconds()))
        return result

test_fib.py
import unittest
from fib import Fib


class TestFib(unittest.TestCase):
    def setUp(self):
        self.fib = Fib()

    def test_recursively(self):
        self.assertEqual(self.fib.recursively(0), 0)
        self.assertEqual(self.fib.recursively(1), 1)
        self.assertEqual(self.fib.recursively(2), 1)
        self.assertEqual(self.fib.recursively(3), 2)
        self.assertEqual(self.fib.recursively(4), 3)
        self.assertEqual(self.fib.recursively(5), 5)
        self.assertEqual(self.fib.recursively(6), 8)
        self.assertEqual(self.fib.recursively(7), 13)
        self.assertEqual(self.fib.recursively(8), 21)
        self.assertEqual(self.fib.recursively(9), 34)
        self.assertEqual(self.fib.recursively(10), 55)

    def test_whileLoop(self):
        self.assertEqual(self.fib.whileLoop(0), 0)
        self.assertEqual(self.fib.whileLoop(1), 1)
        self.assertEqual(self.fib.whileLoop(2), 1)
        self.assertEqual(self.fib.whileLoop(3), 2)
        self.assertEqual(self.fib.whileLoop(4), 3)
        self.assertEqual(self.fib.whileLoop(5), 5)
        self.assertEqual(self.fib.whileLoop(6), 8)
        self.assertEqual(self.fib.whileLoop(7), 13)
        self.assertEqual(self.fib.whileLoop(8), 21)
        self.assertEqual(self.fib.whileLoop(9), 34)
        self.assertEqual(self.fib.whileLoop(10), 55)

    def test_timedRecursively(self):
        self.assertEqual(self.fib.timedRecursively(40), 102334155)

    def test_timedWhileLoop(self):
        self.assertEqual(self.fib.timedWhileLoop(40), 102334155)
        self.assertEqual(self.fib.timedWhileLoop(50), 12586269025)
        self.assertEqual(self.fib.timedWhileLoop(60), 1548008755920)

unittest.main()

Fib.java
package fun;

public class Fib {


    public static void main(String[] args) {
        recursively(1);
        whileLoop(1);

        long start = System.nanoTime();

        long result = recursively(40);
        long elapsed = (System.nanoTime() - start) / 1000000;
        System.out.println(String.format("Working out Fibonacci(%s)=%s recursively took %s ms", 40, result, elapsed));

        start = System.nanoTime();
        result = whileLoop(40);
        elapsed = (System.nanoTime() - start);
        System.out.println(String.format("Working out Fibonacci(%s)=%s with while loop took %s ns", 40, result, elapsed));

        start = System.nanoTime();
        result = whileLoop(50);
        elapsed = (System.nanoTime() - start);
        System.out.println(String.format("Working out Fibonacci(%s)=%s with while loop took %s ns", 50, result, elapsed));

        start = System.nanoTime();
        result = whileLoop(60);
        elapsed = (System.nanoTime() - start);
        System.out.println(String.format("Working out Fibonacci(%s)=%s with while loop took %s ns", 60, result, elapsed));
    }

    static long recursively(long i) {
        if (i == 0) return 0;
        if (i <= 2) return 1;
        else return recursively(i - 1) + recursively(i - 2);
    }

    static long whileLoop(long i) {
        long previous = 0, fib = 1, currentIndex = 1;
        while (currentIndex < i) {
            long newFib = previous + fib;
            previous = fib;
            fib = newFib;
            currentIndex++;
        }
        return fib;
    }
}

Saturday, 30 March 2013

First impression of Python

Born some two decades ago, Python has certainly accumulated enough interests to become one of the most popular programming languages used in the financial industry.

Designed to be a write-less-do-more language, Python has certain aspects that differ from Java:
  1. It is an interpreted language, which means no compilation is required. 
  2. It is not a strongly typed language
  3. Its statement grouping is done by indentation instead of open and close braces
  4. It does not require declaration of variable or argument
  5. It supports complex number, for example (using Python interactive interpreter)
  6. >>> 1j + 2J
    (-2+0j)
    >>> (1+2j) * complex(2,3)
    (-4+7j)
    
  7. Strings can be subscripted. For example, the first character of a string has index 0.
  8. >>> word="HelloWorld"
    >>> word[4]
    'o'
    >>> word[1:3]   # using the slice notation, returning characters from index 1 to 2
    'el'
    
  9. It supports multiple assignment, for example, 'a,b=b,a+b', where the expression on the right-hand side 'b,a+b' are evaluated first before any of the assignments take place
  10. Not surprisingly, it supports lamda forms
  11. >>> def incresedBy(n):
    ...     return lambda x: x + n
    ...
    >>> f = incresedBy(42)
    >>> f(1)
    43
    
  12. It supports map and reduce functions as part of its functional programming offerings
  13. It supports Tuples, which are immutable and usually contain an heterogeneous sequence of elements
  14. >>> t=123,321,'hello'
    >>> t
    (123, 321, 'hello')
    
  15. It allows multiple inheritance, for example
  16. class DerivedClassName(Base1, Base2, Base3):
        <statement-1>
        .
        .
        .
        <statement-N>
    

Friday, 29 March 2013

Using Python SimpleHTTPServer to share files

There are cases when I want to share some files within the intranet to other users.

Python comes with a simple built-in module called SimpleHTTPServer that can turn any directory in the system into the web server directory. I literally just need a single command line to accomplish this task, given Python is shipped with openSUSE!

Assuming I am running linux, and I want to share my home directory ~
cd ~
Start up the http server on port 10001
youyang@monkey-dev-01:~> python -m SimpleHTTPServer 10001
Now the http server is running on port 10001. Open a browser and type the following address, where monkey-dev-01 happens to be my hostname
http://monkey-dev-01:10001
Since there is no index.html under my home directory, the files in the home directory will be listed. Now my purpose is served, but there is one slight problem -- the program is running on the foreground, meaning I can't do anything on that open terminal as it continues to display the http requests when I browse the directories. To change the running process from foreground to background, I need to press CTRL+Z to pause the process, as shown below
^Z
[1]+  Stopped                 python -m SimpleHTTPServer 10001
and type 'bg' to move the process to run in the background
youyang@monkey-dev-01:~> bg
[1]+ python -m SimpleHTTPServer 10001 &
We can also use 'jobs -l' to display the jobs in current session
youyang@monkey-dev-01:~> jobs -l
[1]+  4127 Running                 python -m SimpleHTTPServer 10001 &
We can also start up the http server running in the background directly, by appending a '&' at the end of the command
youyang@monkey-dev-01:~> python -m SimpleHTTPServer 10001&