Every algorithm uses some of the computer’s resources like central processing time and internal memory to complete its task. Because of high cost of computing resources, it is desirable to design algorithms that are economical in the use of CPU time and memory. Efficiency considerations for algorithms are tied in with the design, implementation and analysis of algorithm. Analysis of algorithms is less obviously necessary, but has several purposes:
There is no simpler way of designing efficient algorithm, but a few suggestions as shown below can sometimes be useful in designing an efficient algorithm.
Redundant Computations
Redundant computations or unnecessary computations result in inefficiency in the implementation of the algorithms. When redundant calculations are embedded inside the loop for the variable which remains unchanged throughout the entire execution phase of the loop, the results are more serious. For example, consider the following code in which the value a*a*a*c is redundantly calculated in the loop:
x=0;
for i=0 to n
x=x+1;
y=(a*a*a*c)*x*x+b*b*x;
print x,y
next i
This redundant calculation can be removed by small modification in the program:
x=0;
d=a*a*a*c;
e= b*b;
for i = 0 to n
x = x+1;
y = d*x*x+e*x;
print x,y
next i
Referencing Array Elements
For using the array element, we require two memory references and an additional operation to locate the correct value for use. So, efficient program must not refer to the same array element again and again if the value of the array element does not change. We must store the value of array element in some variable and use that variable in place of referencing the array element. For example:
Version (1)
x=1;
for i = 0 to n
if (a[i] > a[x]) x=i;
next i
max = a[x];
Version (2)
x=1;
max=a[1];
for i = 0 to n
if(a[i]>max)
x=i;
max=a[i];
next i
Version (2) is more efficient algorithm than version (1) algorithm .
Inefficiency Due to Late Termination
Another place where inefficiency can come into an implementation is where considerably more tests are done than are required to solve the problem at hand. For example, if in the linear search process, all the list elements are checked for a particular element even if the point is reached where it was known that the element cannot occur later (in case of sorted list). Second example can be in case of the bubble sort algorithm, where the inner loop should not proceed beyond n-i, because last i elements are already sorted (in the algorithm given below).
for i = 0 to n
for j = 0 to n – 1
if(a[j] > a[j+1])
//swap values a[j], a[j+1]
The efficient algorithm in which the inner loop terminates much before is given as:
for i=0 to n
for j=0 to n – 1
if(a[j]>a[j+1])
//swap values a[j], a[j+1]
Early Detection of Desired Output Condition
Sometimes the loops can be terminated early, if the desired output conditions are met. This saves a lot of unfruitful execution. For example, in the bubble sort algorithm, if during the current pass of the inner loop there are no exchanges in the data, then the list can be assumed to be sorted and the search can be terminated before running the outer loop for n times.
Trading Storage for Efficient Gains
A trade between storage and efficiency is often used to improve the performance of an algorithm. This can be done if we save some intermediary results and avoid having to do a lot of unnecessary testing and computation later on.
One strategy for speeding up the execution of an algorithm is to implement it using
the least number of loops. It may make the program much harder to read and debug. It
is therefore sometimes desirable that each loop does one job and sometimes it is
required for computational speedup or efficiency that the same loop must be used for
different jobs so as to reduce the number of loops in the algorithm. A kind of trade off
is to be done while determining the approach for the same.