Efficient algorithm writing concerns the effectiveness of the overall system. Every real-life algorithm iterates at least once. Iteration may be explicit (due to the looping construct) or it may be implicit (due to recursion). Loops are the major contributor to efficiency analysis of the algorithm. Sometimes the code within the loop executes thousands of times, or even billions of times. A small gain in proper algorithm design greatly reduces the running time.

Efficient algorithm

Effectively using loops

It is just as important to write efficient algorithms as it is to have high-level hardware. The carelessly written algorithm often runs one time more or less than intended. The following three points should be considered while writing a looping construct:

Initialization

Choose the loop counter such that it can address the smallest possible instance of the problem. Consider the problem of summing the first n integers. The smallest possible instance is to add a zero number. In that case, the minimum value of the sum variable and the loop counter i will be zero.

i = 0
s = 0

Find iterative construct

Find the problem with next smallest instance. Careful inspection of loop and logic tells us that in each iteration variable i needs to be incremented and A[i] should be added to the partially computed sum.

i = i + 1
sum = sum + A[i]

The complete code segment would be :

i ← 0
sum ← 0
while i < n do
  i ← i + 1
  sum ← sum + a[i]	// Assume that array index starts from 1.
end

Termination

A number of iterations may be known in advance or it may depend on certain conditions to be satisfied. To find
5! loop should be iterated 5 times

The loop count is known in advance:

for (i = 1; i <= 5; i++)
{
	. . . 
}

The loop count is not predefined:

Sometimes loop may be forced to terminate from in-between

for (i = 0; i < n; i++)
{
  if A[i] == x
    break;
}

How to write efficient algorithm?

Remove redundant operations:

The computational cost of an algorithm is defined by its efficiency. People used to frequently write unneeded code within the loop. Redundant code that is repeated within loops has a negative impact on execution time.

The algorithm’s efficiency can be increased by carefully designing it. Consider the code segment below for determining pixel coordinates along the ellipse boundary:

Algorithm DRAW_ELLIPSE(a, b)

for x ← 0 to a do
  y ← (b/a)*sqrt(a*a – x*x)
  PlotPixel(x, round(y))
end

By relocating the constant computation outside the loop, a substantially better version of the above algorithm can be developed. The optimized ellipse algorithm is illustrated below.

Algorithm EFFICIENT_DRAW_ELLIPSE(a, b)

t ← b/a;
m ← a * a;
for x ← 0 to a do
  y ← t*sqrt(m – x*x)
  PlotPixel(x, round(y))
end

Constant division b/a and constant multiplication a*a is moved outside of the loop, and hence we reduced n multiplications and n divisions

Referencing Array

One memory access corresponds to a reference to one array element. If the array is large, this may result in a significant number of memory accesses, slowing down the process. The algorithm’s efficient design can save a large amount of memory access.

Arrays are typically used to retain a huge amount of data and are accessible via the loop. Consider the following two scenarios for locating the array’s smallest element.

Algorithm POOR_FIND_MIN

k ← 0
for i ← 1 to n do
  if A[i] < A[k] then 
    k ← i
  end
end
Min ← A[k]

Efficient version of this algorithm would be:

Algorithm EFFICIENT_FIND_MIN

Min ← A[0]
for i ← 1 to n do
  if A[i] < Min then
    Min ← A[i]
    k ← i
  end
end
Min ← A[k]

The POOR_FIND_MIN algorithm requires two array accesses for each comparison, whereas EFFICIENT_FIND_MIN needs only one. Arithmetic on the array needs to address calculation on each array access.

EFFICIENT_FIND_MIN compares A[i] with constant Min, which reduces the array access by a factor of two

Late termination

Sometimes, inefficiency in the program is also introduced due to unnecessary more tests. Program keep iterating after producing useful results. Consider the problem of linear search.

Algorithm POOR_LINEAR_SEARCH(A, Key)

Index ← -1
for i ← 1 to n do
  if A[i] == key then
    Index ← i;
  end
end

if Index == -1 then
  print “Key not found”
else
  print “Key found on index i”
end

Efficient version of this algorithm would be:

Algorithm EFFICIENT_LINEAR_SEARCH(A, Key)

flag ← 0
for i ← 1 to n do
  if A[i] == key then
    flag ← 1
    break
  end
end

if flag == 0 then
  print “Key not found”
else
  print “Key found on index i”
end

POOR_LINEAR_SEARCH goes on to iterate n times Key is found at the first location. Whereas, EFFICIENT_LINEAR_SEARCH breaks the loop once Key is found, which makes it more efficient


Additional Resource: Problems, Solutions and Tools. Click to read.