next up previous
Next: References Up: Declared-Strategy Voting: An Instrument Previous: Survey Instrument

DSV Simulator Source Code

/* code for decision function and related functions from dsvsim4.c
 *
 * declared strategy voting simulation program
 * by Lorrie Faith Cranor (lorracks@cs.wustl.edu)
 */

#define square(x) ((x)*(x))

struct ballot_list {
  int *ballot;  /* array of utility values */
  int *indx;    /* indexes for ballot sorted in ascending order */
  int *tindx;   /* location of utility ties */
  struct ballot_list *next;
};

void decide_vote(ptr, tally, last_tally, altvote, N, B, S, pfunc)
     struct ballot_list *ptr;  /* pointer to one voter's ballot */
     int *tally;  /* current election tally (will be incremented) */
     int *last_tally;  /* tally in BBB, tally from last round in Batch */
     int *altvote;  /* number of voters who have voted insincerely so far */
     int N;  /* number of candidates */
     int B;  /* number of voters */
     double S;  /* uncertaintly */
     int pfunc; /* probability function to use */
{   
  int i, j, k, t, found;
  int *E_indx, *E_tindx, *t_indx, *t_tindx;
  double *E;  /* array of expected gains */
  double lb, ub;  /* lower bound, upper bound */
  double param1, param2, param3, param4; /* params to prob. calc. function */
  double M, x, P, U;
  double (*func)();  /* probability calculation function */

  /* setup */
  E = (double *)malloc (sizeof (double) * (N+1)); 
  E_indx = (int *)malloc (sizeof (int) * (N+1));
  E_tindx = (int *)malloc (sizeof (int) * (N+1));   
  t_indx = (int *)malloc (sizeof (int) * (N+1));
  t_tindx = (int *)malloc (sizeof (int) * (N+1));
  iindexx(N, last_tally, t_indx);  /* index last_tallies */
  ifindties(N, last_tally, t_indx, t_tindx); /* find ties in last_tallies
                                              * also randomizes tied
                                              * elements in array */
  if (pfunc == 1) func = euDistanc;
  else if (pfunc == 2) func = gaussian3;
  else if (pfunc == 4) func = gaussian1;
  else {
    fprintf (stderr, "Invalid pfunc\n");
    free (E); free (E_indx); free (E_tindx); free (t_indx); free(t_tindx);
    exit (0);
  }
  found = 0;

  /* if 1st choice is in 1st or 2nd place vote for it and don't
   * worry about probabiliy calculations
   */
  for (t = ptr->tindx[N]+1; t>0; t--) { /* loop over 1st choice ties */
    for (i = t_tindx[N]+1; i>0; i--) { /* loop over 1st place ties */
      if (ptr->indx[N-t+1]==t_indx[N-i+1]) { /* 1st choice in 1st place? */
        tally[t_indx[N-i+1]]++;
        found = 1; t = 0; i = 0;
      }
    }
  }
  if (found) {
    free (E); free (E_indx); free (E_tindx); free (t_indx); free (t_tindx);
    return;
  }

  /* calculate expected gain */
  E[1] = 0;  /* we're never going to vote for last choice, so doesn't matter */
  for (i=N; i>1; i--) { 
    E[i] = 0;
    for (j=N; j>0; j--) {
      if (i==j) continue;
      if (pfunc == 2 || pfunc == 1) {
        lb = 1.0/3.0;
        ub = 0.5;
        param1 = (double)last_tally[ptr->indx[i]]/(double)B;
        param2 = (double)last_tally[ptr->indx[j]]/(double)B;
        for (k=1; k<=N; k++)
          if(i!=k && j!=k) 
            param3 = (double)last_tally[ptr->indx[k]]/(double)B;
        param4 = S;
        E[i] += (double)((ptr->ballot[ptr->indx[i]]-ptr->ballot[ptr->indx[j]])
                * qsimp (func, lb, ub, param1, param2, param3, param4));
      }
      else if (pfunc == 4) {
        U = (double)(ptr->ballot[ptr->indx[i]] - ptr->ballot[ptr->indx[j]]);
        if (U != 0) { /* doesn't matter what P is, if E term = 0 */
          M = (double)last_tally[t_indx[N]]/(double)B;
          x = (double)min(last_tally[ptr->indx[i]],last_tally[ptr->indx[j]])/
            (double)B;
          if (M == x)  ub = M;
          else ub = (square(M) - square(x))/(2 * (M - x)); /* intercept */
          P = (S/2.0)*erf((ub-M)/(S*sqrt(2)))+S*.5;
          if (P == 0.0) 
             P = (S/fabs(ub-M))*(1/sqrt(2*M_PI)) * exp(-square(ub-M)/(S*S*2));
          E[i] += U * P;
        }
      }
    } /* end j loop */
  } /* end i loop */

  indexx(N, E, E_indx);  /* index expected gain */
  findties(N, E, E_indx, E_tindx);  /* look for ties in expected gain index */
  tally[ptr->indx[E_indx[N]]]++;  /* increment tally */

  /* did we vote for someone other than first choice */
  found = 0;
  for (i = 0; i<= ptr->tindx[N]; i++) 
    if (ptr->indx[E_indx[N]] == ptr->indx[N-i]) found = 1;
  if (!found) {
    *altvote = *altvote + 1;
  }

  free (E); free (E_indx); free (E_tindx); free (t_indx); free (t_tindx);
}


/* return smaller of a and b */
int min (a,b)
     int a, b;
{
  if (a < b) return (a);
  else return (b);
}


/* creates an index that identifies ties in an indexed list sorted
 * in ascending order
 *
 * The Nth element of the indx array is the index of the first ranked
 * data element.  If the N-1 element is tied with the Nth element,
 * the Nth element of the tindx array will contain a non-zero number
 * corresponding to the number of elements that tie with the Nth
 * element
 * 
 * this function also swaps the indecies for the tied elements with
 * random probability
 */
int findties(n,data,indx,tindx)
int n;
double data[];
int indx[], tindx[];
{
  int i,t,r,temp,first;
  first = 0;
  for (i=1; i<=n; i++) tindx[i] = 0;
  for (i=n; i>1; i--) {
    for (t=i-1; t>0; t--) {
      if (data[indx[i]] != data[indx[t]]) t=0;
      else {
        tindx[i]++;
        if (i == n && tindx[i] > first) first = tindx[i];
        r = (int)random()%2;
        if (r == 1) {
          temp=indx[i];
          indx[i]=indx[t];
          indx[t]=temp;
        }
      }
    }
    i = i - tindx[i];
  }
  return (first+1);  /* first-place tie size */
}


/* for integers, same as above but data is an int */
int ifindties(n,data,indx,tindx)
int n, data[], indx[], tindx[];
{
/* ... */
}


/***** probability calculation functions and auxilaries *****************/

/* simple distance (note we only implemented this for 3 candidates */
double euDistanc (q, x, y, z,S)
     double q, x, y, z,S;
{
  return (1 - (sqrt(square(q - x) + square(q - y) + square(1 - 2.0 * q - z))));
}


/* Hoffman's method (note we only implemented this for 3 candidates */
double gaussian3 (q, x, y, z, S)
     double q, x, y, z, S;
{
  double D;
  D = sqrt(square(q - x) + square(q - y) + square(1 - 2.0 * q - z));
  return (exp (-.5 * square(D)/square(S)));
}


/* 2-D gaussian method */
double gaussian1 (q, x, S, blank1, blank2)
     double q, x, S, blank1, blank2;
{
  double pi = 3.14159;
  return (exp (-.5 * square(q-x)/square(S))/ sqrt(2 * pi));
}


/***** functions from Numerical Recipes in C ****************************/
/*     note floats have been replaced by doubles throughout             */
/************************************************************************/

/* from Numerical Recipes in C: The Art of Scientific Computing
 * by William H. Press, Brian P. Flannery, Saul A. Teukolsky, 
 * and William T. Vetterling.  Cambridge University Press, 1988.
 *
 * The bodies of these functions have been removed and replaced with
 * blank comments.  Please refer to the above-mentioned text for the
 * complete functions.
 */

/* Indexes an array arrin[1..n], i.e. outputs the array indx[1..n] such
 * that arrin[indx[j]] is in ascending order for j=1,2,...,N.  The input
 * quantities n and arrin are not changed.
 */
void indexx(n,arrin,indx)
int n,indx[];
double arrin[]; 
{
/* ... */
}

/* same as indexx, but we made arrin an array of ints */
void iindexx(n,arrin,indx)
int n,indx[];
int arrin[]; /* changed from double */
{
/* ... */
}

/* Returns the integral of the function func from a to b.  The parameters
 * EPS can be set to the desired fractional accuracy and JMAX so that 
 * 2^JMAX-1 is the maximum allowed number of steps.  Integration is performed
 * by Simpson's rule
 *
 * modified slightly so that function can take 4 parameters
 */
#define EPS 1.0e-5  /* changed from 1.0e-6 */
#define JMAX 20
double qsimp(func,a,b,x,y,z,S)
double a,b,x,y,z,S;
double (*func)();
{
/* ... */
}
#undef EPS
#undef JMAX

/* This routine computes the nth stage of refinement of an extended 
 * trapezoidal rule.  func is input as a pointer to the function to be
 * integrated between limits a and b, also input.  When called with n=1,
 * the routine returns the crudest estimate of int(a->b)f(x)dx.  
 * Subsequent calls with n=2,3,... (in tht sequential order) will
 * improve the accuracy by adding 2^n-2 additional interior points
 *
 * modified slighly so func can take 4 parameters
 */
#define FUNC(x) ((*func)(x,g,h,i,S))
double trapzd(func,a,b,n,g,h,i,S)
double a,b,g,h,i,S;
double (*func)();       /* ANSI: double (*func)(double); */
int n;
{
/* ... */
}



lorrie@acm.org