Algorithm Alley
by Andrew Colin


Listing One
/* FILE: ahp.c
** AUTHOR: Andrew Colin
** DATE: 29th October 1998
** DISCLAIMER: No liability is assumed by the author for any use made
** of this program.
** DISTRIBUTION: Any use may be made of this program, as long as the
** clear acknowledgment is made to the author in code and runtime
** executables. The author retains copyright.
*/

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <assert.h>

#define MAX_ATTRIBUTES 10
#define N_CASES 3
#define MAX_ITERATIONS 100

#define randomize() srand((unsigned)time(NULL))

typedef struct element {
    int     n_factors;                     /* Number of attributes/items */
    char    attributes[MAX_ATTRIBUTES][0xFF]; /* Not used  */
    double  preferences[MAX_ATTRIBUTES][MAX_ATTRIBUTES]; /* rankings */
    struct  element *branch[MAX_ATTRIBUTES]; /* Address of sub-attribute */
    double  eigenvector[MAX_ATTRIBUTES]; /* computed rankings */
    double  ranking[N_CASES];   /* Final, relative worth of each choice */
} ELEMENT;

ELEMENT array[] = { 
    { 4, {"Top-level", "b", "c", "d"}, 
    {{1,1.5,0.75,3},{0.667,1,0.5,2},{1.333,2,1,4},{0.333,0.5,0.25,1}}, 
    {&array[1], &array[2], &array[3], &array[4]} },

    { 3,{"Documentation"},{{1,0.25,0.333},{4,1,1.333},{3,0.75,1}}, {NULL}},
    { 3, {"Cost"}, {{1,1.6,4},{0.625,1,2.5},{0.25,0.4,1}}, { NULL } },
    { 3, {"IDE"}, {{1,0.714,1.25},{1.4,1,1.75},{0.8,0.571,1}}, { NULL } },
    { 2, {"C++ features"}, {{1,2},{0.5,1}}, { &array[5], &array[6] } },
    { 3, {"STL library"}, {{1,0.111,0.111},{9,1,1},{9,1,1}}, { NULL } },
    { 3, {"Exception handling"}, {{1,1,9},{1,1,9},{0.111,0.111,1}},{NULL}},
};
/*---------------------------------------------------------------------------*/
/* Multiplies a matrix m and a vector v; returns the result in vector
 * r. Note the syntax for passing a statically declared multidimensional array 
 * to a function (see K&R, p112, second edition)
 */
void mmult( int size, double m[][MAX_ATTRIBUTES], double v[], double r[] ) {
    int i, j;
    for (i=0; i<size; i++) {
       r[i] = 0.0;
       for (j=0; j<size; j++)
          r[i] += m[i][j] * v[j];
    }
}
/*---------------------------------------------------------------------------*/
/* Given a matrix m, this routine returns the normalised principal
 * eigenvector e using the power method (see Burden and Faires,
 * Numerical Analysis, Prindle, Weber & Schmidt, 1985, pp 452-456)
 */
void np_eigenvalue( int size, double m[MAX_ATTRIBUTES][MAX_ATTRIBUTES], 
                    double e[MAX_ATTRIBUTES] )  {
    double v[MAX_ATTRIBUTES];
    double largest, s, sum;
    int i, iteration;
    /* Initial random guess for eigenvector */
    for (i=0; i<size; i++)
       v[i] = (double)rand() / RAND_MAX;
    iteration = 0;
    while (iteration < MAX_ITERATIONS) {
       /* Construct new approximation to eigenvector */
       mmult( size, m, v, e );
       /* Find largest element in new eigenvector */
       largest = 0.0;
       for (i=0; i<size; i++) {
           s = fabs(e[i]);
           if (s > largest)
               largest = s;
       }
       /* Normalise by dividing by element of largest absolute magnitude */
       for (i=0; i<size; i++)
           e[i] /= largest;
       /* Copy new approximation to old approximation */
       for (i=0; i<size; i++)
           v[i] = e[i];
       ++iteration;
    }
    /* Normalise eigenvector so that sum of elements = 1 */
    sum = 0.0;
    for (i=0; i<size; i++)
       sum += e[i];
    for (i=0; i<size; i++)
       e[i] /= sum;
}
/*---------------------------------------------------------------------------*/
/* Evaluates the priority vector for each node in the decision
 * tree. This routine calculates the principal eigenvector of the
 * array 'preferences' and writes it to the array 'eigenvector'.
 */
void get_eigenvector (ELEMENT *e) {
    np_eigenvalue( e->n_factors, e->preferences, e->eigenvector );
}
/*---------------------------------------------------------------------------*/
/* Recursive routine to evaluate the ranking vector for an element of
 * a hierarchy, where the priorities (or principal eigenvectors) have
 * already been calculated.
 */
void evaluate_hierarchy( ELEMENT *e ) {
    int i, j;
    if (e->branch[0] == NULL) { /* At bottom level of hierarchy */
        assert (e->n_factors == N_CASES);
        for (i=0; i<N_CASES; i++)
            e->ranking[i] = e->eigenvector[i];
    }
   else {
        for (i=0; i<e->n_factors; i++)   /* At intermediate level */
            evaluate_hierarchy( e->branch[i] );
        for (j=0; j<N_CASES; j++) {
            e->ranking[j] = 0.0;
            for (i=0; i<e->n_factors; i++)
                e->ranking[j] += e->eigenvector[i] 
                            * e->branch[i]->ranking[j];
        }
    }
    #ifdef _DEBUG
        for (i=0; i<N_CASES; i++)
            printf("Ranking for item %i is %f\n", i, e->ranking[i]);
    #endif
}
/*---------------------------------------------------------------------------*/
main() {
    int i;
    randomize();

    printf("\nWelcome to the Analytic Hierarchy Process!");
    printf("\nLast compiled on %s, %s\n", __TIME__, __DATE__);
    /* Evaluate priority arrays for each element in 'array' */
    for (i=0; i<sizeof(array) / sizeof(array[0]); i++)
        get_eigenvector (&array[i]);
    /* Evaluate the tree. The result is returned in array[0].ranking */
    evaluate_hierarchy (&array[0]);
    /* Display rankings for each case. The highest score is the winner. */
    for (i=0; i<N_CASES; i++)
        printf("Case %i has ranking %f\n", i, array[0].ranking[i]);
    return 0;
}



3


