Data Structure: Selection Sort


This type of sorting is called "Selection Sort" because it works by repeatedly element. It works as follows: first find the smallest in the array and exchange it with the element in the first position, then find the second smallest element and exchange it with the element in the second position, and continue in this way until the entire array is sorted.

SELECTION_SORT (A)
for i ← 1 to n-1 do
    min j ← i;
    min x ← A[i]
    for j ← i + 1 to n do
        If A[j] < min x then
            min j ← j
            min x ← A[j]
    A[min j] ← A [i]
    A[i] ← min x

Selection sort is among the simplest of sorting techniques and it work very well for small files. Furthermore, despite its evident "naïve approach "Selection sort has a quite important application because each item is actually moved at most once, Section sort is a method of choice for sorting files with very large objects (records) and small keys.


The worst case occurs if the array is already sorted in descending order. Nonetheless, the time require by selection sort algorithm is not very sensitive to the original order of the array to be sorted: the test "if A[j] < min x" is executed exactly the same number of times in every case. The variation in time is only due to the number of times the "then" part (i.e., min j ← j; min x ← A[j] of this test are executed.
The Selection sort spends most of its time trying to find the minimum element in the "unsorted" part of the array. It clearly shows the similarity between Selection sort and Bubble sort. Bubble sort "selects" the maximum remaining elements at each stage, but wastes some effort imparting some order to "unsorted" part of the array. Selection sort is quadratic in both the worst and the average case, and requires no extra memory.
For each i from 1 to - 1, there is one exchange and - i comparisons, so there is a total of -1 exchanges and (-1) + (-2) + . . . + 2 + 1 = n(-1)/2 comparisons. These observations hold no matter what the input data is. In the worst case, this could be quadratic, but in the average case, this quantity is O(log n). It implies that the running time of Selection sort is quite insensitive to the input.

Implementation

void selectionSort(int numbers[], int array_size)
{
  int i, j;
  int min, temp;

  for (i = 0; i < array_size-1; i++)
  {
    min = i;
    for (j = i+1; j < array_size; j++)
    {
      if (numbers[j] < numbers[min])
        min = j;
    }
    temp = numbers[i];
    numbers[i] = numbers[min];
    numbers[min] = temp;
  }
}

 

Read More

Data Structure: Insertion Sort


If the first few objects are already sorted, an unsorted object can be inserted in the sorted set in proper place. This is called insertion sort. An algorithm consider the elements one at a time, inserting each in its suitable place among those already considered (keeping them sorted). Insertion sort is an example of an incremental algorithm; it builds the sorted sequence one number at a time. This is perhaps the simplest example of the incremental insertion technique, where we build up a complicated structure on n items by first building it on n − 1 items and then making the necessary changes to fix things in adding the last item. The given sequences are typically stored in arrays. We also refer the numbers as keys. Along with each key may be additional information, known as satellite data. [Note that "satellite data" does not necessarily come from satellite!]

Algorithm: Insertion Sort
It works the way you might sort a hand of playing cards:
  1. We start with an empty left hand [sorted array] and the cards face down on the table [unsorted array].
  2. Then remove one card [key] at a time from the table [unsorted array], and insert it into the correct position in the left hand [sorted array].
  3. To find the correct position for the card, we compare it with each of the cards already in the hand, from right to left.
Note that at all times, the cards held in the left hand are sorted, and these cards were originally the top cards of the pile on the table.

Pseudocode
We use a procedure INSERTION_SORT. It takes as parameters an array A[1.. n] and the length n of the array. The array A is sorted in place: the numbers are rearranged within the array, with at most a constant number outside the array at any time.

INSERTION_SORT (A)
1.     FOR j ← 2 TO length[A]
2.             DO  key ← A[j]   
3.                   {Put A[j] into the sorted sequence A[1 . . j − 1]}  
4.                    i ← j − 1   
5.                    WHILE i > 0 and A[i] > key
6.                                 DO A[i +1] ← A[i]           
7.                                         i ← i − 1    
8.                     A[i + 1] ← key

Example: Following figure (from CLRS) shows the operation of INSERTION-SORT on the array A= (5, 2, 4, 6, 1, 3). Each part shows what happens for a particular iteration with the value of j indicated. j indexes the "current card" being inserted into the hand.
The operation of INSERTION-SORT on the array A = <5, 2, 4, 6, 1, 3>
Read the figure row by row. Elements to the left of A[j] that are greater than A[j] move one position to the right, and A[j] moves into the evacuated position.

Analysis
Since the running time of an algorithm on a particular input is the number of steps executed, we must define "step" independent of machine. We say that a statement that takes ci steps to execute and executed n times contributes cin to the total running time of the algorithm. To compute the running time, T(n), we sum the products of the cost and times column [see CLRS page 26]. That is, the running time of the algorithm is the sum of running times for each statement executed. So, we have
T(n) = c1n + c2 (n  1) + 0 (n  1) + c4 (n  1) + c5 ∑2  j  n ( t)+ c6 ∑2  j  n (tj  1) + c7 ∑2  j  n (tj  1) + c8 (n 1)                               
In the above equation we supposed that tj  be the number of times the while-loop (in line 5) is executed for that value of j. Note that the value of j runs from 2 to (n  1). We have
T(n) = c1n + c2 (n  1) + c4 (n  1) + c5 ∑2  j  n ( tj )+ c6 ∑2  j  n (tj   1) + c7 ∑2  j  n (tj   1) + c8 (n − 1)  Equation (1)

Best-Case
The best case occurs if the array is already sorted. For each j = 2, 3, ..., n, we find that A[i] less than or equal to the key when i has its initial value of (j  1). In other words, when i = j 1, always find the key A[i] upon the first time the WHILE loop is run.
Therefore, tj = 1 for j = 2, 3, ..., n and the best-case running time can be computed using equation (1) as follows:
T(n) = c1n + c2 (n  1) + c4 (n  1) + c5 ∑2  j  n (1) + c6 ∑2  j  n (1 − 1) + c7 ∑2  j  n (1 − 1) + c8 (n − 1)
T(n) = c1n + c2 (n  1) + c4 (n  1) + c5 (n − 1) + c8 (n − 1)
T(n) = (c1 + c2 + c4  + c5  + c8 ) n + (c2  + c4  + c5  + c8)
This running time can be expressed as an + b for constants a and b that depend on the statement costs ci. Therefore, T(n) it is a linear function of n.
The punch line here is that the while-loop in line 5 executed only once for each j. This happens if given array A is already sorted.
T(n) = an + b = O(n)
It is a linear function of n.

Worst-Case
The worst-case occurs if the array is sorted in reverse order i.e., in decreasing order. In the reverse order, we always find that A[i] is greater than the key in the while-loop test. So, we must compare each element A[j] with each element in the entire sorted subarray A[1 ..j  1] and so tj = j for j = 2, 3, ..., n. Equivalently, we can say that since the while-loop exits because i reaches to 0, there is one additional test after (j  1) tests. Therefore, tj = j for j = 2, 3, ..., n and the worst-case running time can be computed using equation (1) as follows:
T(n) = c1n + c2 (n  1) + c4  (n  1) + c5 ∑2  j  n ( j ) + c6 ∑2  j  n(j  1) + c7 ∑2  j  n(j  1) + c8 (n  1)
And using the summations in CLRS on page 27, we have
T(n) = c1n + c2 (n  1) + c4  (n  1) + c5 ∑2  j  n [n(+1)/2 + 1] + c6 ∑2  j  n [n(n  1)/2] + c7 ∑2  j  n [n(n  1)/2] + c8 (n 1)
T(n) = (c5/2 + c6/2 + c7/2) n2 + (c1 + c2 + c4 + c5/2  c6/2  c7/2 + c8n  (c2 + c4 + c5 + c8)
This running time can be expressed as (an2 + bn + c) for constants ab, and c that again depend on the statement costs ci. Therefore, T(n) is a quadratic function of n.
Here the punch line is that the worst-case occurs, when line 5 executed j times for each j. This can happens if array A starts out in reverse order
T(n) = an2 + bn + c = O(n2)
It is a quadratic function of n.

The graph shows the n2 complexity of the insertion sort.

Worst-case and average-case Analysis
We usually concentrate on finding the worst-case running time: the longest running time for any input size n. The reasons for this choice are as follows:
  • The worst-case running time gives a guaranteed upper bound on the running time for any input. That is, upper bound gives us a guarantee that the algorithm will never take any longer.
  • For some algorithms, the worst case occurs often. For example, when searching, the worst case often occurs when the item being searched for is not present, and searches for absent items may be frequent.
  • Why not analyze the average case? Because it's often about as bad as the worst case.
Example: Suppose that we randomly choose n numbers as the input to insertion sort.
On average, the key in A[j] is less than half the elements in A[1 .. j − 1] and it is greater than the other half. It implies that on average, the while loop has to look halfway through the sorted subarray A[1 .. j − 1] to decide where to drop key. This means that tj  = j/2.
Although the average-case running time is approximately half of the worst-case running time, it is still a quadratic function of n.

Stability
Since multiple keys with the same value are placed in the sorted array in the same order that they appear in the input array, Insertion sort is stable.

Extra Memory
This algorithm does not require extra memory.
  • For Insertion sort we say the worst-case running time is θ(n2), and the best-case running time is θ(n).
  • Insertion sort use no extra memory it sort in place.
  • The time of  Insertion sort is depends on the original order of a input. It takes a time in Ω(n2) in the worst-case, despite the fact that a time in order of n is sufficient to solve large instances in which the items are already sorted.

Implementation
void insertionSort(int numbers[], int array_size)
{
  int ij, index;

  for (i = 1; i < array_size; i++)
  {
          index = numbers[i];
          j = i;
          while ((j > 0) && (numbers[j − 1] > index))
          {
                   numbers[j] = numbers[j − 1];
                   j = j  1;
          }
          numbers[j] = index;
  }
}
Read More

Data Structure: Bubble Sort


Bubble Sort is an elementary sorting algorithm. It works by repeatedly exchanging adjacent elements, if necessary. When no exchanges are required, the file is sorted.

SEQUENTIAL BUBBLESORT (A)
for i ← 1 to length [A] do
    for j ← length [A] downto +1 do
        If A[A] < A[j-1] then
            Exchange A[j] ↔ A[j-1]
  
Here the number of comparison made

            1 + 2 + 3 + . . . + (- 1) = n(- 1)/2 = O(n2)
  

Clearly, the graph shows the n2 nature of the bubble sort.
In this algorithm the number of comparison is irrespective of data set i.e., input whether best or worst.

 

Memory Requirement

Clearly, bubble sort does not require extra memory.

 

 

Implementation

 
void bubbleSort(int numbers[], int array_size)
{
  int i, j, temp;

  for (i = (array_size - 1); i >= 0; i--)
  {
    for (j = 1; j <= i; j++)
    {
      if (numbers[j-1] > numbers[j])
      {
        temp = numbers[j-1];
        numbers[j-1] = numbers[j];
        numbers[j] = temp;
      }
    }
  }
}
  
Algorithm for Parallel Bubble Sort

 

PARALLEL BUBBLE SORT (A)

  1. For k = 0 to n-2
  2. If k is even then
  3.     for i = 0 to (n/2)-1 do in parallel
  4.         If A[2i] > A[2i+1] then
  5.             Exchange A[2i] ↔ A[2i+1]
  6. Else
  7.     for i = 0 to (n/2)-2 do in parallel
  8.         If A[2i+1] > A[2i+2] then
  9.             Exchange A[2i+1] ↔ A[2i+2]
  10. Next k

 Parallel Analysis

Steps 1-10 is a one big loop that is represented -1 times. Therefore, the parallel time complexity is O(n). If the algorithm, odd-numbered steps need (n/2) - 2 processors and even-numbered steps require (n/2) - 1 processors. Therefore, this needs O(n)processors.

Read More

Sorting: Algorithm and Data Structure


The objective of the sorting algorithm is to rearrange the records so that their keys are ordered according to some well-defined ordering rule.
Problem:   Given an array of n real number A[1.. n].
Objective: Sort the elements of A in ascending order of their values. Internal Sort
 
External Sort
  • Sorting files from tape or disk.
  • In this method, an external sort algorithm must access records sequentially, or at least in the block.
 
Memory Requirement
  1. Sort in place and use no extra memory except perhaps for a small stack or table.
  1. Algorithm that use a linked-list representation and so use N extra words of memory for list pointers.
  1. Algorithms that need enough extra memory space to hold another copy of the array to be sorted.
 
Stability
The Decision Tree Model
 
 
A Lower Bound for the Worst Case
Taking logarithms on both sides

(lg(n!) ≤ h
        h  ≥ lg(n!)

Taking logarithms on both sides
(lg(n!) ≤ h
        h  ≥ lg(n!)

(lg(n!) ≤ h
        h  ≥ lg(n!)

        h  ≥ lg(n!)
If the file to be sorted will fit into memory or equivalently if it will fit into an array, then the sorting method is called internal. In this method, any record can be accessed easily.
A sorting algorithm is called stable if it is preserves the relative order of equal keys in the file. Most of the simple algorithm are stable, but most of the well-known sophisticated algorithms are not.


There are two classes of sorting algorithms namely, O(n2)-algorithms and O(n log n)-algorithms. O(n2)-class  includes bubble sort, insertion sort, selection sort and shell sort. O(n log n)-class includes heap sort, merge sort and quick sort.


O(n2) Sorting Algorithms



O(n log n) Sorting Algorithms


Now we show that comparison-based sorting algorithm has an Ω(log n) worst-case lower bound on its running time operation in sorting, then this is the best we can do. Note that in a comparison sort, we use only comparisons between elements to gain information about an input sequence <a1a2, . . . , an>. That is, given two elements ai and aj we perform one of the tests, ai < ajai ≤ aj,ai aj and ai ≥ aj to determine their relative order.
Given all of the input elements are distinct (this is not a restriction since we are deriving a lower bound), comparisons of the form ai =aj are useless, so no comparison of ai aj are made. We also note that the comparison ai ≤  aj , ai ≥ aj and ai < aare all equivalent. Therefore we assume that all comparisons have form ai ≥ aj.


Each time a sorting algorithm compares two elements aand aj , there are two outcomes: "Yes" or "No". Based on the result of this comparison, the sorting algorithm may perform some calculation which we are not interested in and will eventually perform another comparison between two other elements of input sequence, which again will have two outcomes. Therefore, we can represent a comparison-based sorting algorithm with a decision tree T.
As an example, consider the decision tree for insertion sort operating on given elements a1a2 and a3. There are are 3! = 6 possible permutations of the three input elements, so the decision tree must have at least 6 leaves.


In general, there are n! possible permutations of the n input elements, so decision tree must have at least n! leaves.
The length of the longest path from the root to any of its leaves represents the worst-case number of comparisons the sorting algorithm perform. Consequently, the worst-case number of comparisons corresponds to the height of its tree. A lower bound on the height of the tree is therefore a lower bound on the running time of any comparison sort algorithm.

Theorem    The running time of any comparison-based algorithm for sorting an n-element sequence is Ω(n lg n) in the worst case.

Examples of comparison-based algorithms (in CLR) are insertion sort, selection sort, merge sort, quicksort, heapsort, and treesort.

Proof    Consider a decision tree of height h that sorts n elements. Since there are n! permutation of n elements and the tree must have at least n! leaves. We have
n! ≤ 2h

Since the lg function is monotonically increasing, from Stirling's approximation we have
                          n! > (n/e)n         where e = 2.71828 . . .
           h  ≥  (n/e)n          which is Ω(lg n)
Read More

Divide-and-Conquer Algorithm


Divide-and-conquer is a top-down technique for designing algorithms that consists of dividing the problem into smaller subproblems hoping that the solutions of the subproblems are easier to find and then composing the partial solutions into the solution of the original problem.

Little more formally, divide-and-conquer paradigm consists of following major phases:
  • Breaking the problem into several sub-problems that are similar to the original problem but smaller in size,
  • Solve the sub-problem recursively (successively and independently), and then
  • Combine these solutions to subproblems to create a solution to the original problem.

Binary Search (simplest application of divide-and-conquer)

Binary Search is an extremely well-known instance of divide-and-conquer paradigm. Given an ordered array of n elements, the basic idea of binary search is that for a given element we "probe" the middle element of the array. We continue in either the lower or upper segment of the array, depending on the outcome of the probe until we reached the required (given) element.

Problem    Let A[1 . . . n] be an array of non-decreasing sorted order; that is A [i] ≤  A [j] whenever   i    j    n. Let'q' be the query point. The problem consist of finding 'q' in the array A. If q is not in A, then find the position where 'q' might be inserted.
 
Formally, find the index such that 1   i   n+1 and A[i-1] < x  A[i].

 Sequential Search

Look sequentially at each element of A until either we reach at the end of an array A or find an item no smaller than 'q'.
Sequential search for 'q' in array A
for i = 1 to n do
    if A [i] ≥ q then
        return index i
    return n + 1

 Analysis

This algorithm clearly takes a θ(r), where r is the index returned. This is Ω(n) in the worst case and O(1) in the best case.
If the elements of an array A are distinct and query point q is indeed in the array then loop executed (n + 1) / 2 average number of times. On average (as well as the worst case), sequential search takes θ(n) time.

 

Binary Search

Look for 'q' either in the first half or in the second half of the array A. Compare 'q' to an element in the middle, n/2 , of the array. Let k = n/2. If q ≤  A[k], then search in the A[1 . . . k]; otherwise search T[k+1 . . n] for 'q'. Binary search for q in subarray A[i . . j] with the promise that 
A[i-1] < x ≤ A[j]
If i = then
    return (index)
k= (i + j)/2
if q ≤ A [k]
    then return Binary Search [[i-k], q]
    else return Binary Search [A[k+1 . . j], q]

 

Analysis

Binary Search can be accomplished in logarithmic time in the worst case , i.e., T(n) = θ(log n). This version of the binary search takes logarithmic time in the best case.

 

 

Iterative Version of Binary Search

Interactive binary search for q, in array A[1 . . n]
if q > A [n]
    then return n + 1
i = 1;
j = n;
while j do
    k = (i + j)/2
    if q ≤ A [k]
        then j = k
        else + 1
return i (the index)

 Analysis

The analysis of  Iterative algorithm is identical to that of its recursive counterpart.
Read More

Algorithm: A Beginner to Advance Guide of Programming

An algorithm, named after the ninth century scholar Abu Jafar Muhammad Ibn Musu Al-Khowarizmi, is defined as follows: Roughly speaking:
  • An algorithm is a set of rules for carrying out calculation either by hand or on a machine.
  • An algorithm is a finite step-by-step procedure to achieve a required result.
  • An algorithm is a sequence of computational steps that transform the input into the output.
  • An algorithm is a sequence of operations performed on data that have to be organized in data structures.
  • An algorithm is an abstraction of a program to be executed on a physical machine (model of Computation).

The most famous algorithm in history dates well before the time of the ancient Greeks: this is the Euclid's algorithm for calculating the greatest common divisor of two integers. This theorem appeared as the solution to the Proposition II in the Book VII of Euclid's "Elements." Euclid's "Elements" consists of thirteen books, which contain a total number of 465 propositions.

 The Classic Multiplication Algorithm
1. Multiplication, the American way:
Multiply the multiplicand one after another by each digit of the multiplier taken from right to left.

                                          981
                                        1234
                                -------------
                                        3924
                                      2943
                                    1962
                                    981
                                ------------
                                  1210554


 2. Multiplication, the English way:
Multiply the multiplicand one after another by each digit of the multiplier taken from left to right.

                                           981
                                        1234
                                ---------------
                                    981
                                    1962
                                      2943
                                        3924
                                ----------------
                                  1210554
Algorithmic is a branch of computer science that consists of designing and analyzing computer algorithms
1. The “design” pertain to
i. The description of algorithm at an abstract level by means of a pseudo language, and
ii. Proof of correctness that is, the algorithm solves the given problem in all cases.

2. The “analysis” deals with performance evaluation (complexity analysis).

We start with defining the model of computation, which is usually the Random Access Machine (RAM) model, but other models of computations can be use such as PRAM. Once the model of computation has been defined, an algorithm can be describe using a simple language (or pseudo language) whose syntax is close to programming language such as C or java.

Algorithm's Performance

Two important ways to characterize the effectiveness of an algorithm are its space complexity and time complexity. Time complexity of an algorithm concerns determining an expression of the number of steps needed as a function of the problem size. Since the step count measure is somewhat coarse, one does not aim at obtaining an exact step count. Instead, one attempts only to get asymptotic bounds on the step count. Asymptotic analysis makes use of the O (Big Oh) notation. Two other notational constructs used by computer scientists in the analysis of algorithms are Θ (Big Theta) notation and Ω (Big Omega) notation.
The performance evaluation of an algorithm is obtained by totaling the number of occurrences of each operation when running the algorithm. The performance of an algorithm is evaluated as a function of the input size n and is to be considered modulo a multiplicative constant.

The following notations are commonly use notations in performance analysis and used to characterize the complexity of an algorithm.

Θ-Notation (Same order)

This notation bounds a function to within constant factors. We say f(n) = Θ(g(n)) if there exist positive constants n0, c1 and c2 such that to the right of n0 the value of f(n) always lies between c1 g(n) and c2 g(n) inclusive.

In the set notation, we write as follows:

Θ(g(n)) = {f(n) : there exist positive constants c1, c1, and n0 such that 0 ≤ c1 g(n) ≤ f(n) ≤ c2 g(n) for all n ≥ n0}

We say that is g(n) an asymptotically tight bound for f(n).

Graphically, for all values of n to the right of n0, the value of f(n) lies at or above c1 g(n) and at or below c2 g(n). In other words, for all n ≥ n0, the function f(n) is equal to g(n) to within a constant factor. We say that g(n) is an asymptotically tight bound for f(n).

In the set terminology, f(n) is said to be a member of the set Θ(g(n)) of functions. In other words, because O(g(n)) is a set, we could write

f(n) ∈ Θ(g(n))

to indicate that f(n) is a member of Θ(g(n)). Instead, we write

f(n) = Θ(g(n))

to express the same notation.

Historically, this notation is "f(n) = Θ(g(n))" although the idea that f(n) is equal to something called Θ(g(n)) is misleading.

Example: n2/2 − 2n = (n2), with c1 = 1/4, c2 = 1/2, and n0 = 8.


Ο-Notation (Upper Bound)

This notation gives an upper bound for a function to within a constant factor. We write f(n) = O(g(n)) if there are positive constants n0 and c such that to the right of n0, the value of f(n) always lies on or below c g(n).

In the set notation, we write as follows: For a given function g(n), the set of functions

Ο(g(n)) = {f(n): there exist positive constants c and n0 such that 0 ≤ f(n) ≤ c g(n) for all n ≥ n0}

We say that the function g(n) is an asymptotic upper bound for the function f(n). We use Ο-notation to give an upper bound on a function, to within a constant factor.

Graphically, for all values of n to the right of n0, the value of the function f(n) is on or below g(n). We write f(n) = O(g(n)) to indicate that a function f(n) is a member of the set Ο(g(n)) i.e.

f(n) ∈ Ο(g(n))

Note that f(n) = Θ(g(n)) implies f(n) = Ο(g(n)), since Θ-notation is a stronger notation than Ο-notation.

Example: 2n2 = Ο(n3), with c = 1 and n0 = 2.

Equivalently, we may also define f is of order g as follows:

If f(n) and g(n) are functions defined on the positive integers, then f(n) is Ο(g(n)) if and only if there is a c > 0 and an n0 > 0 such that

| f(n) | ≤ | g(n) | for all n ≥ n0

Historical Note: The notation was introduced in 1892 by the German mathematician Paul Bachman. 

Ω-Notation (Lower Bound)

This notation gives a lower bound for a function to within a constant factor. We write f(n) = Ω(g(n)) if there are positive constants n0 and c such that to the right of n0, the value of f(n) always lies on or above c g(n).

In the set notation, we write as follows: For a given function g(n), the set of functions

Ω(g(n)) = {f(n) : there exist positive constants c and n0 such that 0 ≤ c g(n) ≤ f(n) for all n ≥ n0}

We say that the function g(n) is an asymptotic lower bound for the function f(n).

The intuition behind Ω-notation is shown above.

Example: √n = (lg n), with c = 1 and n0 = 16.

Algorithm Analysis

The complexity of an algorithm is a function g(n) that gives the upper bound of the number of operation (or running time) performed by an algorithm when the input size is n.

There are two interpretations of upper bound.

Worst-case Complexity
The running time for any given size input will be lower than the upper bound except possibly for some values of the input where the maximum is reached.
Average-case Complexity
The running time for any given size input will be the average number of operations over all problem instances for a given size.

Because, it is quite difficult to estimate the statistical behavior of the input, most of the time we content ourselves to a worst case behavior. Most of the time, the complexity of g(n) is approximated by its family o(f(n)) where f(n) is one of the following functions. n (linear complexity), log n (logarithmic complexity), na where a ≥ 2 (polynomial complexity), an (exponential complexity).

 Optimality

Once the complexity of an algorithm has been estimated, the question arises whether this algorithm is optimal. An algorithm for a given problem is optimal if its complexity reaches the lower bound over all the algorithms solving this problem. For example, any algorithm solving “the intersection of n segments” problem will execute at least n2 operations in the worst case even if it does nothing but print the output. This is abbreviated by saying that the problem has Ω(n2) complexity. If one finds an O(n2) algorithm that solve this problem, it will be optimal and of complexity Θ(n2).

Reduction

Another technique for estimating the complexity of a problem is the transformation of problems, also called problem reduction. As an example, suppose we know a lower bound for a problem A, and that we would like to estimate a lower bound for a problem B. If we can transform A into B by a transformation step whose cost is less than that for solving A, then B has the same bound as A.

The Convex hull problem nicely illustrates "reduction" technique. A lower bound of Convex-hull problem established by reducing the sorting problem (complexity: Θ(n log n)) to the Convex hull problem.

Read More

CON: Tutorial to create folder with name CON


Simple ppl dont know why they cant create File named with "CON"? Very few know that they can still create it someway.. but  why are they supposed to do exactly like that.. Now, After reading this tutorial, you will become one of the rest. Not only CON, we cannot create any of these:
CON, PRN, AUX, CLOCK$, NUL, COM1, COM2, COM3, COM4, COM5, COM6, COM7, COM8, COM9, LPT1, LPT2, LPT3, LPT4, LPT5, LPT6, LPT7, LPT8, LPT9 and more The reason is that con, prn, lpt1..lpt9, etc are underlying devices from the time dos was written. so if u r allowed to create such folders, there will be an ambiguity in where to write data when the data is supposed to go to the specified devices. In other words, if i want to print something, internally what windows does is -- it will write the data to the folder prn (virtually u can call it a folder, i mean prn, con, etc are virtual folders in device level). So if we are able to create con folder, windows will get confused where to write the data, to virtual con folder or real one. So Now, Try this...


C:\> md \\.\x:\con
Note: x: repersent the Drive letter where want to create. (it may be C:, D: and So on)
Now, Open My Computer and browse through the path where you created CON folder ... Surprising.. ?? Yeah.. you have created it successfully..
Now, try to delete the folder from My computer 
OOPS!!! You cant delete it...


Now, try this in command prompt console




C:\> rd \\.\x:\con


You Did it


For More info You can Click on http://support.microsoft.com

Read More