Showing posts with label Algorithms. Show all posts
Showing posts with label Algorithms. Show all posts

July 03, 2014

Comparable & Comparator API

Total order
A total order is a binary relation ≤ that satisfies:
・Antisymmetry: if v ≤ w and w ≤ v, then v = w.
・Transitivity: if v ≤ w and w ≤ x, then v ≤ x.
・Totality: either v ≤ w or w ≤ v or both.
Ex.
・Standard order for natural and real numbers.
・Chronological order for dates or times.
・Alphabetical order for strings.

 Note that the <= operator for double is not a total order. Double.NaN <= Double.NaN is false.

Comparable API
Implement compareTo() so that v.compareTo(w)
・Is a total order.
・Returns a negative integer, zero, or positive integer
if v is less than, equal to, or greater than w, respectively.
・Throws an exception if incompatible types (or either is null).

Tips:
To sort an Array, use the Arrays.sort().
To sort an ArrayList, use the Collections.sort().

Natural Sorting of numbers:

Output: 1,2,3,5,7,10,16,23,46

Incorrect sorting of cars:
Let us consider taking a car class with the data members as shown below.


Here the client tries to sort an array consisting of three cars. But do you see the problem?

Yes. This cannot be sorted by Java because we haven't told it what is the order to be followed and which data member should be considered to be sorted. This is why we implement the comparable interface. The output of above as expected would be.

Correct Sorting of Cars:
Shown below is the class diagram:

Here the Car class now implements the comparable Interface and defines the total order in compareTo() method.
Output: Mazda,Honda,Toyota

Correct Sorting of Cars with different properties:
You do not have to implement Comparable but you have to create static members of type Comparator which overrides the method compare(). Below is shown the class diagram.
Code:
package george.comparablesWithComparator;

import java.util.Comparator;

public class Car{

private String make;
private String model;
private int cost;
public String getMake() {
return make;
}
public void setMake(String make) {
this.make = make;
}
public String getModel() {
return model;
}
public void setModel(String model) {
this.model = model;
}
public int getCost() {
return cost;
}
public void setCost(int cost) {
this.cost = cost;
}
public static Comparator carCostComparator = new Comparator() {

@Override
public int compare(Car c1, Car c2) {
if(c1.cost < c2.cost)
return -1;
else if(c1.cost > c2.cost)
return 1;
else
return 0;
}
};
public static Comparator carModelComparator = new Comparator() {

@Override
public int compare(Car c1, Car c2) {
if(c1.model.compareTo(c2.model) < 0)
return -1;
else if(c1.model.compareTo(c2.model) > 0)
return 1;
else
return 0;
}
};
public static Comparator carMakeComparator = new Comparator() {

@Override
public int compare(Car c1, Car c2) {
if(c1.make.compareTo(c2.make) < 0)
return -1;
else if(c1.make.compareTo(c2.make) > 0)
return 1;
else
return 0;
}
};
}


package george.comparablesWithComparator;

import java.util.Arrays;

public class ClassClient {

/**
* @param args
*/
public static void main(String[] args) {
Car car1 = new Car();
Car car2 = new Car();
Car car3 = new Car();
car1.setCost(1000);
car1.setMake("Honda");
car1.setModel("City");
car2.setCost(120);
car2.setMake("Mazda");
car2.setModel("ZX-1");
car3.setCost(90888);
car3.setMake("Toyota");
car3.setModel("Qualis");
Car[] carArray = {car1, car2, car3};
System.out.print("Sorting by Make: ");
Arrays.sort(carArray,Car.carMakeComparator);
for(Car c : carArray)
{
System.out.print(c.getMake() + ",");
}
System.out.println();
System.out.print("Sorting by Model: ");
Arrays.sort(carArray,Car.carModelComparator);
for(Car c : carArray)
{
System.out.print(c.getModel() + ",");
}
System.out.println();
System.out.print("Sorting by Cost: ");
Arrays.sort(carArray,Car.carCostComparator);
for(Car c : carArray)
{
System.out.print(c.getCost() + ",");
}
}

}


July 02, 2014

The crux of Java collections - Stack, Queue & Bags

Collections is one of the most important standard library available in Java. It allows developers to take the power of generics and Iterator and develop code which expects less input from the user and allows good reusability. All references are from Dr.Sidgewich codebase. Collections can be implemented using linked list or array.

Here I am going show my implementation of Queue using basic re-sizing array, implements Iterable interface and is generic. Below is shown the class diagram. This can be easily ported into stack and bags using simple logic changes.

Code:

package george.queue;

import java.util.Iterator;

public class VariableCapacityQueue implements Iterable {
private E[] s;
private int N = 0;

public VariableCapacityQueue() {
s = (E[]) new Object[1];
}

public boolean isEmpty() {
return N == 0;
}

public void enqueue(E item) {
if (N == s.length)
resize(2 * s.length);
s[N++] = item;
System.out.println("array size is "+s.length);
// array size increament verified
}

public E dequeue() {
// return s[--N];//just moves the pointer but not the element//So
// modifying code
E temp = s[0];
s[0] = null;
moveElements(s.length);
N--;
// The above two steps prevents loitering
if (N > 0 && N == s.length / 4)// otherwise will terminate at -1 itself
resize(s.length / 2);
System.out.println("array size is " + s.length);
return temp;
}

private void resize(int capacity) {
E[] copy = (E[]) new Object[capacity];
for (int i = 0; i < N; i++)
copy[i] = s[i];
s = copy;
}

private void moveElements(int capacity) {
E[] copy = (E[]) new Object[capacity];
for (int i = 1; i < N; i++)
// first element in queue removed so n reduced by 1
copy[i - 1] = s[i];
s = copy;
}

public class ListIterator implements Iterator {
private int i = 0;

public boolean hasNext() {
return i < N;
}

public void remove() {
}

public E next() {
return s[i++];
}
}

public Iterator iterator() {
return new ListIterator();
}


}


Performance:

Order of Growth:
Running time is worst when compared to Linked List implementation because of moveElements() method being called each time something is dequeued.  Thus dequeue operation has a order of growth  O(N^2). Enqueue operation consumes in the order of O(N) for elements of size N. Any suggestion to improve dequeue implementation is welcome.

Memory:
The memory consumption will be almost similar to the one shown below. Expect for the fact that we are using generics so the 8N + 24 for String  will be xN + 24 depending upon the datatype used for storing in the array. Similarly padding will change accordingly.

July 01, 2014

UnionFind for Dynamic connectivity

Union Find is used to find the connection between two objects.
Why do we need Union Find algorithm?

There are three approaches to implement this each with it's own complexity.
1. Quick Find:
Simplest but does union is quadratic in nature
2. Quick Union:
Union is Linear time but find also becomes linear which is constant in Quick find
3. Weighted QuickUnion:
Union and find are logarithmic in nature

Implementation:
The class diagram of my implementation uses code from Dr.Robert Sidgewich's code base. Here I use the template method and strategy pattern to do a quick comparison of performance of all the three types.
The following images show how the implementation is done.
UF is the abstract class from which all other union find classes inherit



UFclient uses the strategy pattern by invoking run() from the main factory method.

QuickFind inherits from UF and uses a single array with a simple idea to find connectivity as shown below.

 QuickUnionUF inherits from UF and uses a single array with tree structure to find connectivity as shown below.

QuickUnionUF inherits from UF and uses a single array with tree structure to find connectivity as shown below. It goes one step further to use a second array for maintaining and balancing weight on the tree.


Performance:

Significant performance difference can be observed from the output as shown below

Time taken for QuickFindUF is 177
Time taken for QuickUnionUF is 66
Time taken for WeightedQuickUnionUF is 25

You can see the difference between Quadratic vs Linear vs Logarithmic time. They are in the ratio 7:3:1

June 30, 2014

Why do we need better algorithms?

This is the most fundamental question every student must ask himself before taking a course on Algorithms. Rather than talking too much about algorithms let me straight away give examples. All the shown examples are from the speech 'Beyond Computation - Michael Sipser'
Problem 1: Finding the factorial of a large number.
We still don't know whether there is any other possible way to find the solution or not.
Problem 2: Finding the largest clique in a given graph with 100 nodes
But trying to find a large clique in the below figure is very difficult for the computer.
Solution: Trying to find the analogy of magnet in mathematics
If you can find the magnet, you are one of the most Intelligent on the planet.
Other Problems:
So you can see that the biggest challenge is 
This is the ultimate reason why we need better algorithms.
To understand more, let me show you the difference between P and NP problems
If P = NP is proved, then we do not need to search for a search algorithm else we need to search. Whether P = NP or P != NP is a mystery.

NP-Completeness
This is the first step taken by humans towards solving an NP problem. NP-Completeness defines that a set of problems are linked to each other and can be easily transformed into each other which means if there is a solution to one problem, then the solution for the other can be found but the reverse is not true. Eg: Factoring can be converted into CLIQUE but not CLIQUE into Factoring

NP-Hard:
There are NP problems for which even checking is not possible. These are called NP-Hard. Eg of such a problem is Chess.

Will it ever be solved?