/** * Ben Sowell * 6-23-06 */ #include #include #include #include #define NUM_HEURISTICS 4 #define NUM_REPETITIONS 1 struct Bidder { double budget; /* T is the percentage of the budget spent so far. Since this depends on the algorithm, we store an a array with a separate entry for each algorithm. */ double T[NUM_HEURISTICS]; }; struct Bidder *bidders; int *subsetArray; int numBidders; /* File to write data to if specified on the command line */ FILE *out; int output; enum MODE_TYPE {MEHTA, MAX_BUDGET_LEFT, MIN_BUDGET_LEFT, MAX_BID}; /** * Generates the specified number of bidders with budgets * chosen from a uniform random distribution between * minBudget and maxBudget. Also initializes the subsetArray, * which will be used to generate a random subset of bidders. * if output is 1, then each budget will be written to the out * file. * * @param num The number of bidders to generate. * @param minBudget The smallest possible budget. * @param maxBudget The largest possible budget. */ void generateBidders(int num, double minBudget, double maxBudget) { numBidders = num; if(output) fprintf(out, "%d\n", numBidders); int i, j; for(i = 0; i < num; i++) { bidders[i].budget = minBudget + (maxBudget - minBudget) * (double) random() / (double) (RAND_MAX + 1.0); for(j = 0; j < NUM_HEURISTICS; j++) bidders[i].T[j] = 0; subsetArray[i] = i; if(output) fprintf(out, "%g\n", bidders[i].budget); } } /** * Generates a random bidder id that is distinct from * all previously generated ids. This is done by generating * a random subset of integers between 0 and numBidders - 1. * * @param index The index of the subset element we wish to access. * (which is generated on the fly). * @return A random bidder ID. */ int getRandomBidderID(int index) { int swapNum = (int) (numBidders - index) * (double) random() / (double) (RAND_MAX + 1.0) + index; int temp = subsetArray[index]; subsetArray[index] = subsetArray[swapNum]; subsetArray[swapNum] = temp; return subsetArray[index]; } /** * Computes the revenue generated by a single query using NUM_HEURISTICS * different algorithms, and updates the appropriate bidders. If in is * NULL, then numBids bidders are randomly selected each bidding random * amount between minBidSize and maxBidSize. Otherwise, the individual * bids are read from in. * * @param in The file to read from if applicable. * @param numBids The number of bidders bidding on this query. * @param minBidSize The minimum possible bid size. * (Not used if in is not NULL) * @param maxBidSize The maximum possible bid size. * (Not used if in is not NULL) * @param revenue An array containing the revenue so far for each algorithm. */ void getQueryRevenue(FILE *in, int numBids, double minBidSize, double maxBidSize, double revenue[NUM_HEURISTICS]) { int winningBidderID[NUM_HEURISTICS]; double winningBid[NUM_HEURISTICS]; /* Keeps track of whatever metric uses to compare bidders */ double winningTest[NUM_HEURISTICS]; int i; for(i = 0; i < NUM_HEURISTICS; i++) { winningBidderID[i] = 0; winningBid[i] = 0; winningTest[i] = 0; } /* Since the min_budget_left algorithm looks for a minimum, we must initialize the min value to something large. */ winningTest[MIN_BUDGET_LEFT] = 100000; int bidderID; double bid = 0.0; double bidTest = 0.0; struct Bidder currentBidder; if(output) fprintf(out, "%d ", numBids); for(i = 0; i < numBids; i++) { /* If in is null, generate the bids at random, otherwise read them from a file */ if(in == NULL) { bidderID = getRandomBidderID(i); bid = (maxBidSize - minBidSize) * (double) random() / (double) (RAND_MAX + 1.0) + minBidSize; } else { fscanf(in, "%d %lg", &bidderID, &bid); } currentBidder = bidders[bidderID]; if(output) fprintf(out, "%d %g ", bidderID, bid); /* MEHTA algorithm */ bidTest = bid * (1 - exp(currentBidder.T[MEHTA] - 1)); if(bidTest > winningTest[MEHTA]) { winningTest[MEHTA] = bidTest; winningBidderID[MEHTA] = bidderID; winningBid[MEHTA] = bid; } /* Max Bid algorithm */ bidTest = currentBidder.budget - currentBidder.budget * currentBidder.T[MAX_BID]; if(bid > winningBid[MAX_BID] && bidTest >= bid) { winningTest[MAX_BID] = bidTest; winningBidderID[MAX_BID] = bidderID; winningBid[MAX_BID] = bid; } /* Break ties according to the max remaining budget */ else if(bid == winningBid[MAX_BID] && bidTest > winningTest[MAX_BID] && bidTest >= bid) { winningBidderID[MAX_BID] = bidderID; winningTest[MAX_BID] = bidTest; } /* Max Budget left algorithm */ bidTest = currentBidder.budget - currentBidder.budget * currentBidder.T[MAX_BUDGET_LEFT]; if(bidTest > winningTest[MAX_BUDGET_LEFT] && bidTest >= bid) { winningTest[MAX_BUDGET_LEFT] = bidTest; winningBidderID[MAX_BUDGET_LEFT] = bidderID; winningBid[MAX_BUDGET_LEFT] = bid; } /* Min Budget left algorithm */ bidTest = currentBidder.budget - currentBidder.budget * currentBidder.T[MIN_BUDGET_LEFT]; if(bidTest < winningTest[MIN_BUDGET_LEFT] && bidTest >= bid) { winningTest[MIN_BUDGET_LEFT] = bidTest; winningBidderID[MIN_BUDGET_LEFT] = bidderID; winningBid[MIN_BUDGET_LEFT] = bid; } } if(output) fprintf(out, "\n"); /* Update the T values for each winning bidder, and compute the revenues. */ for(i = 0; i < NUM_HEURISTICS; i++) { bidders[winningBidderID[i]].T[i] += winningBid[i]/bidders[winningBidderID[i]].budget; revenue[i] += winningBid[i]; } } /** * Runs the AdWords algorithm for numQueries where * each query has a number of bids randomly chosen from * a uniform distribution between 0 and numBidders. Each * bid is between min and max. * * In order for this method to work properly, generateBidders * must have been called so that the bidders array is properly * instantiated. * * @param numQueries The number of queries. * @param min The minimum bid size. * @param max The maximum bid size. */ void runFromRandomQueries(int numQueries, double min, double max) { int numBids, i; double revenue[NUM_HEURISTICS]; for(i = 0; i < NUM_HEURISTICS; i++) revenue[i] = 0; if(output) fprintf(out, "%d\n", numQueries); for(i = 0; i < numQueries; i++) { numBids = (int) numBidders * (double) random() / (double) (RAND_MAX + 1.0); getQueryRevenue(NULL, numBids, min, max, revenue); } for(i = 0; i < NUM_HEURISTICS; i++) printf("%g ", revenue[i]); printf("\n"); } /** * Runs AdWords algorithm by using query data from the * specifed file. This file should be in the form: * * n * b1 min1 max1 * b2 min2 max2 * b3 min3 max3 * ... * where n is the total number of queries, bi is the number of * bids for the ith query, and mini and maxi are the are the minimum * and maximum values for each bid. Each bid is randomly chosen * from a uniform random distribution between min and max. * * In order for this method to work properly, generateBidders * must have been called so that the bidders array is properly * instantiated. * * @param fileName The file containing the query data. */ void runFromQueryFile(char* fileName) { FILE *f = fopen(fileName, "r"); int numQueries, numBids, i; double min, max; double revenue[NUM_HEURISTICS]; for(i = 0; i < NUM_HEURISTICS; i++) revenue[i] = 0; fscanf(f, "%d\n", &numQueries); if(output) fprintf(out, "%d\n", numQueries); for(i = 0; i < numQueries; i++) { fscanf(f, "%d %lg %lg", &numBids, &min, &max); getQueryRevenue(NULL, numBids, min, max, revenue); } fclose(f); for(i = 0; i < NUM_HEURISTICS; i++) printf("%g ", revenue[i]); printf("\n"); } /** * Runs the AdWords algorithm where all data is specified in a file. * The file should be of the following form: * * numBidders * budget1 * budget2 * ... * numQueries * numBids bidder1 bid1 bidder2 bid2 ... * numBids bidder1 bid1 bidder2 bid2 ... * ... * * @param fileName The file containing the bidder and query data. */ void runFromFile(char* fileName) { FILE *f = fopen(fileName, "r"); int numQueries, numBids, i, j; double budget; double revenue[NUM_HEURISTICS]; for(i = 0; i < NUM_HEURISTICS; i++) revenue[i] = 0; /*Read the number of bidders and allocate the appropriate amount of memory*/ fscanf(f, "%d\n", &numBidders); bidders = (struct Bidder*) malloc(sizeof(struct Bidder) * numBidders); for(i = 0; i < numBidders; i++) { fscanf(f, "%lg\n", &budget); bidders[i].budget = budget; for(j = 0; j < NUM_HEURISTICS; j++) bidders[i].T[j] = 0; } fscanf(f, "%d\n", &numQueries); for(i = 0; i < numQueries; i++) { fscanf(f, "%d ", &numBids); getQueryRevenue(f, numBids, 0, 0, revenue); } fclose(f); for(i = 0; i < NUM_HEURISTICS; i++) printf("%g ", revenue[i]); printf("\n"); } int main(int argc, char *argv[]) { srandom(time(0)); int startIndex = 0; /* * If there is an additional parameter, then we * output the randomly generated data to a file. */ if(argc == 8 || argc == 6) { output = 1; out = fopen(argv[++startIndex], "w"); argc--; } else output = 0; /* * The only parameter is a fileName, so we call * runFromFile. */ if(argc == 2) { runFromFile(argv[++startIndex]); } /* * The user specified, the number of bidders, min and max * budget size, the number of queries, and min and max query * size. We call runFromRandomQueries. */ else if(argc == 7) { int numQueries; double minBudget, maxBudget, minBid, maxBid; sscanf(argv[++startIndex], "%d", &numBidders); sscanf(argv[++startIndex], "%lg", &minBudget); sscanf(argv[++startIndex], "%lg", &maxBudget); sscanf(argv[++startIndex], "%d", &numQueries); sscanf(argv[++startIndex], "%lg", &minBid); sscanf(argv[++startIndex], "%lg", &maxBid); bidders = (struct Bidder*) malloc(sizeof(struct Bidder) * numBidders); subsetArray = (int*) malloc(sizeof(int) * numBidders); int i; for(i = 0; i < NUM_REPETITIONS; i++) { generateBidders(numBidders, minBudget, maxBudget); runFromRandomQueries(numQueries, minBid, maxBid); } } /* * The user specifed the number of bids, the min * and max budgets, and a file name, so we call * runFromQueryFile. */ else if(argc == 5) { double minBudget, maxBudget; char* fileName = argv[++startIndex]; sscanf(argv[++startIndex], "%d", &numBidders); sscanf(argv[++startIndex], "%lg", &minBudget); sscanf(argv[++startIndex], "%lg", &maxBudget); bidders = (struct Bidder*) malloc(sizeof(struct Bidder) * numBidders); subsetArray = (int*) malloc(sizeof(int) * numBidders); int i; for(i = 0; i < NUM_REPETITIONS; i++) { generateBidders(numBidders, minBudget, maxBudget); runFromQueryFile(fileName); } } else { printf("Invalid arguments\n"); } free(bidders); free(subsetArray); if(output) fclose(out); return 0; }