import java.util.*; import java.io.*; /** * @author Ben Sowell * @version 6-20-06 */ class AuctionTester { /** * Class to represent a bidder. Stores * the budget and the value T, which * is the percentage of the budget that * the bidder has spent. */ private class Bidder { public double budget, T; public Bidder(double budget) { this.budget = budget; } public void update(double amount) { T += (amount/budget); } } private ArrayList bidders; private Random rand; private int[] subsetArray; public AuctionTester() { bidders = new ArrayList(); rand = new Random(); } /** * Generates the specified number of bidders with budgets * chosen from a uniform random distribution between * minBudget and maxBudget. Also initializes the subestArray, * which will be used to generate a random subset of bidders. * * @param num The number of bidders to generate. * @param minBudget The smallest possible budget. * @ param maxBudget The largest possible budget. */ public void generateBidders(int num, double minBudget, double maxBudget) { double budget; subsetArray = new int[num]; for(int i = 0; i < num; i++) { budget = rand.nextDouble() * (maxBudget - minBudget) + minBudget; bidders.add(new Bidder(budget)); subsetArray[i] = i; } } /** * 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. * @return The revenue from this run. */ public double runFromQueryFile(String fileName) { int num, swapNum, temp; double max, min; int maxBidderID; double bid, maxBid, bidTest, maxBidTest; double revenue = 0.0; try { Scanner queryScanner = new Scanner(new File(fileName)); while(queryScanner.hasNext()) { num = queryScanner.nextInt(); min = queryScanner.nextDouble(); max = queryScanner.nextDouble(); revenue += getQueryRevenue(num, min, max); } return revenue; } catch(FileNotFoundException e) { System.err.println("Could not find query file"); System.exit(1); } catch(IOException e) { System.err.println("IO error"); System.exit(1); } return -1.0; } /** * 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. */ public double runFromRandomQueries(int numQueries, double min, double max) { int num; double revenue = 0.0; for(int j = 0; j < numQueries; j++) { num = rand.nextInt(bidders.size()); revenue += getQueryRevenue(num, min, max); } return revenue; } /** * 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. * @return The revenue generated by this run. */ public double runFromFile(String fileName) { try { Scanner fileScanner = new Scanner(new File(fileName)); Scanner lineScanner; int bidderID, maxBidderID; double maxBid, bid, bidTest, maxBidTest; double revenue = 0.0; //Read info on each of the bidders. int numBidders = fileScanner.nextInt(); for(int i = 0; i < numBidders; i++) bidders.add(new Bidder(fileScanner.nextFloat())); while(fileScanner.hasNextLine()) { lineScanner = new Scanner(fileScanner.nextLine()); maxBidTest = 0.0; maxBidderID = 0; maxBid = 0.0; while(lineScanner.hasNext()) { bidderID = lineScanner.nextInt(); bid = lineScanner.nextFloat(); bidTest = bid * (1 - Math.exp(bidders.get(bidderID).T - 1)); if(bidTest > maxBidTest) { maxBidTest = bidTest; maxBidderID = bidderID; maxBid = bid; } } bidders.get(maxBidderID).update(maxBid); revenue += maxBid; } return revenue; } catch(FileNotFoundException e) { System.out.println("Could not find input file"); System.exit(1); } catch(IOException e) { System.out.println("IOException"); System.exit(1); } return -1.0; } /** * Randomly generates num bids between min and max and * finds which one maximizes the tradeoff function as specifed * in "AdWords and Generalized On-line Matching" by Mehta et. al. * * @param num The number of bids to generate. * @param min The minimum bid size. * @param max The maximum bid size. * @return The revenue made from this query. */ public double getQueryRevenue(int num, double min, double max) { int maxBidderID = 0, swapNum, temp; double bid = 0.0, maxBid = 0.0, bidTest = 0.0, maxBidTest = 0.0; double revenue = 0.0; for(int i = 0; i < num; i++) { swapNum = rand.nextInt(bidders.size() - i) + i; temp = subsetArray[i]; subsetArray[i] = subsetArray[swapNum]; subsetArray[swapNum] = temp; //Generate a random bid for this query. bid = rand.nextDouble() * (max - min) + min; bidTest = bid * (1 - Math.exp( bidders.get(subsetArray[i]).T - 1)); if(bidTest > maxBidTest) { maxBidTest = bidTest; maxBidderID = subsetArray[i]; maxBid = bid; } } bidders.get(maxBidderID).update(maxBid); return maxBid; } public static void main(String[] args) { AuctionTester aTest = new AuctionTester(); double revenue; if(args.length == 1) { revenue = aTest.runFromFile(args[0]); System.out.println("revenue: " + revenue); } else if(args.length == 6) { int numBidders = Integer.parseInt(args[0]); double minBudget = Double.parseDouble(args[1]); double maxBudget = Double.parseDouble(args[2]); int numQueries = Integer.parseInt(args[3]); double minBid = Double.parseDouble(args[4]); double maxBid = Double.parseDouble(args[5]); aTest.generateBidders(numBidders, minBudget, maxBudget); revenue = aTest.runFromRandomQueries(1000, .10, 1.0); System.out.println("revenue: " + revenue); } else if(args.length == 4) { int numBidders = Integer.parseInt(args[1]); double minBudget = Double.parseDouble(args[2]); double maxBudget = Double.parseDouble(args[3]); aTest.generateBidders(numBidders, minBudget, maxBudget); revenue = aTest.runFromQueryFile(args[0]); System.out.println("revenue: " + revenue); } else { System.out.println("Invalid command line arguments"); } } }