How do I avoid repetitive checking in my recursion? [closed] - java

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 10 years ago.
For those people already read my last post, I do have a different solution for this. Please don't just walk away.
This is an assignment question from school (Sorry for the long story, I already tried shorten it)
I tend to order food based on how much money I have on me. I like to tip at least 15% regardless of service (I do enough evaluating performance in any given week already) and I hate the whole process of giving my money, waiting for the waitress to come back with the change the trying to find the correct change to make a proper tip. What I do is examine the menu for the combination of foods that best matches the money I have on me. When I say best matches, I mean closest to 15% without going under, and I only pick each food item once. As you can imagine it takes me a bit of time to compute all of this so I want you to make a method that I can use.
The method only has to work for the following menu:
Bandera Pizza Bread 6.49
Boston's Pizza Bread 5.35
Garlic Twist Bread 7.49
Single Order 5.35
Sun-Dried Tomato Bruschetta 6.99
Three Cheese Toast 6.35
Double Order wings 16.49
Starter Size wings 8.99
Cactus Nachos 10.29
Baked Ravioli Bites 8.49
Southwest Quesadilla 9.25
Create a method called selectFood that takes the amount of money I have as a parameter, outputs the selections on the screen and returns the percentage tip I will be leaving rounded to one decimal place. Don't worry if there is more food than two people can eat, I often go out with larger groups.
Some example output:
Best order for $10.00 is:Baked Ravioli Bites
The tip is 17.79%
Best order for $20.00 is:Sun-Dried Tomato Bruschetta, Cactus Nachos
The tip is 15.74%
Best order for $60.00 is:Bandera Pizza Bread, Boston's Pizza Bread, Three Cheese Toast, Double Order wings, Starter Size wings, Baked Ravioli Bites
The tip is 15.03%
Best order for $190.00 is:Bandera Pizza Bread, Boston's Pizza Bread, Garlic Twist Bread, Single Order, Sun-Dried Tomato Bruschetta, Three Cheese Toast, Double Order wings, Starter Size wings, Cactus Nachos, Baked Ravioli Bites, Southwest Quesadilla
The tip is 107.58%
My teacher has a restriction - we are not allowed to use an arraylist.
Here's my newest try:
import java.util.*;
class MethodAssign7{
public static void main(String[]args){
boolean[] took = {false,false,false,false,false,false,false,false,false,false,false};
double money = 70.0;
//System.out.println(selectFood(money/1.15,took));
selectFood(money,took);
System.out.println(closest/money*100+15);
}
static double closest = 10000.0;
static void selectFood(double money, boolean[] took){
String[] food = {"Bandera Pizza Bread","Boston's Pizza Bread","Garlic Twist Bread","Single Order","Sun-Dried Tomato Bruschetta","Three Cheese Toast","Double Order wings","Starter Size wings","Cactus Nachos","Baked Ravioli Bites","Southwest Quesadilla"};
double[] costs = {6.49,5.35,7.49,5.35,6.99,6.35,16.49,8.99,10.29,8.49,9.25};
if(money<5.35){
if(money<closest){
closest = money;
}
}
else{
for(double d: costs){
if(money-d*1.15>0){
//System.out.println(money-d);
selectFood(money-d*1.15,took);
}
}
}
}
/*static void printSelections(double money, boolean[] took){
String[] food = {"Bandera Pizza Bread","Boston's Pizza Bread","Garlic Twist Bread","Single Order","Sun-Dried Tomato Bruschetta","Three Cheese Toast","Double Order wings","Starter Size wings","Cactus Nachos","Baked Ravioli Bites","Southwest Quesadilla"};
double[] costs = {6.49,5.35,7.49,5.35,6.99,6.35,16.49,8.99,10.29,8.49,9.25};
if(money<5.35){
if(money==closest){
print the choices by using took
}
}
else{
for(int i=0;i<costs.length;i++){
if(money-costs[i]*1.15>0 && took[i]!=true){
took[i]=true;
//System.out.println(money-d);
selectFood(money-costs[i]*1.15,took);
}
}
}
}*/
}
I'm trying to solve the percentage part of the question first with dynamic programming, I can get the percentage answer with my program, but it takes too long for money input above 60. I tried to addin the boolean list "took" to indicate which ones are already picked but it did not work at all and got me confused :(
All the parts that's commented out are for the output of selections of food. And I know my selectFood method is only void right now and won't return value, but I think that's easier to fix. All I care right now is how do I get this percentage part working.
Thank you for your time reading my question, if you can help me I would appreciate it very much or if you don't get what I'm asking please leave a comment to tell me.

The simple version of this is that you start with an amount of money, x. At least a certain amount of this will be required as a tip, which we will call t. That effectively means that you want to spend as much money as you can, without going over (x - t).
What you want to do then, is define your targets:
double totalMoney = 190.0;
double minimumTip = totalMoney/115*15;
double targetMoney = totalMoney - minimumTip;
I'll assume that you have the required data structures, like so:
MenuItem[] items = new MenuItem[]
{
new MenuItem("Bandera Pizza Bread", 6.49),
new MenuItem("Boston's Pizza Bread", 5.35),
[...]
};
Now we want to search recursively for the best possible combination of these items, such that the total cost of the selected items is maximized while always remaining less than targetMoney.
Each branch of the recursive tree will represent one combination of products that I could purchase. This is the key difference between my solution and yours. At the first branch of the tree, I will evaluate two possibilities - either I will purchase "Bandera Pizza Bread", or I will not. At the second level of branches, I will evaluate whether I should purchase "Boston's Pizza Bread", or not. At each recursive call, I only need to know whether I still have any money left to spend (at which point I look at the next item down the list) or whether this order has 'overspent', at which point I give up on this combination (since buying anything else will just make it even more expensive!).
To reduce the number of arrays I need to create/discard, I'm using an integer "selected" as a bit field specifying which items I have decided to purchase on this branch. If a bit is 1, then I have chosen to purchase this item. If a bit is 0, then I have chosen not to purchase this item. You could get the same effect using arrays of booleans, but there would be a lot of array manipulation getting in the way of the actual algorithm I'm demonstrating.
I've also created class variables for the best case:
int bestPurchaseSet = 0;
double bestCost = 0;
You don't have to do this, you can pass your results around with parameters and return types, but it makes the code less heavy this way.
So then, the recursive function looks a bit like this:
public void search(MenuItem[] items,
int selected,
int depth,
double currentCost,
double maxCost)
{
if(currentCost > maxCost)
{
// too expensive
return;
}
if(currentCost > bestCost)
{
// New best combination! Save it.
bestCost = currentCost;
bestPurchaseSet = selected;
}
if(depth >= items.length)
{
// run out of food types
return;
}
// if we do choose this item, then we mark it as selected and increase the cost of this order.
search(items, selected | (0x1 << depth), depth + 1, currentCost + items[depth].cost, maxCost);
// if we don't choose this item
search(items, selected, depth + 1, currentCost, maxCost);
}
This should run quite efficiently, because each food item only adds one additional level of recursion - and many of the recursive branches are chopped off early (as soon as the purchase becomes too expensive).
Finally, it's a matter of printing out the results:
System.out.print("Best order for $" + totalMoney + " is: ");
for(int i=0; i<items.length; i++)
{
if((bestPurchaseSet & (0x1 << i)) != 0)
{
System.out.print(items[i].name + ", ");
}
}
System.out.println("The tip is " + (totalMoney - bestCost)/bestCost * 100 + "%");

Related

Finding the best combination of a complex collection of items?

I have the following objects:
enum Slot
{
HANDS, LEGS, CHEST, HEAD, FEET;
}
class Clothing
{
// The slot this piece of clothing is worn on.
Slot s;
// The color of the clothing, used for `gradeOutfit`
Color c;
}
class Person
{
Map<Slot, Clothing> body;
// look through his outfit and give a score
// for how well he looks
int gradeOutfit()
{
return ...
}
}
I have one Person object and a collection of Clothing. This collection has many Clothing objects of each Slot. For example, it might look like this:
MyCloset = { GREEN_HAT, RED_VEST, BLACK_VEST,
BLUE_JEANS, BROWN_PANTS, RED_SHOES, BLACK_HAT, BLUE_GLOVES, PURPLE_VEST }
In the reality of my program, there are a lot more items than just these but this is just a simplified example.
Problem:
I need to find a combination of these clothes that lead to the highest gradeOutfit score. That means my Person will have to make sure he tries on every Clothing item with every other Clothing item (within limits, ex. it's impossible for two hats to be worn because both are for HEAD Slot). A Person cannot have their gradeOutfit called until they are wearing a Clothing item for every Slot.
I was thinking recursion is the best way to do this but then I think I'd get a stack overflow very fast if I had a decent amount of items. I tried doing it iteratively but I cannot seem to find a good easy way to loop through everything. My program basically looks like
Person p = new Person();
for (Clothing i : MyCloset)
{
for (Clothing h : MyCloset)
{
if (i == h) continue;
if (!p.isWearing(h.slot())
{
p.wear(h);
}
}
int score = p.gradeOutfit();
}
But I know this is just a terrible approach. In order to ensure that every clothing item has been paired up with every other Clothing item, I would need so much more looping logic than just this. No matter what I try, it turns into spaghetti code. I also need to avoid looping over the same outfit twice and make sure that no outfit combination is forgotten about.
What is the best way to approach something like this?
This is an example of a mathematical optimization problem. You seem to already have the objective function (the function that calculates the gradeOutfit score - taking as an input five clothings, one per slot) and you need some constraints (e.g. each clothing in a combination of 5 belongs to a different slot). You need a Java solver library to do this. If your objective function is linear, a linear solver will do. As I have only experience with commercial solvers, I cannot recommend an open-source one, a list of options can be found here.
A simpler (but not extremely elegant) way, without a solver:
Create 5 sets of Clothing objects, one per slot (you can use Java
HashSet for this).
Iterate over all combinations, each time taking one item from each of the 5 sets. You need n1 x n2 x n3 x n4 x n5 combinations, where ni is the number of clothing instances per slot.
It also seems to me that the gradeOutfit function should not be part of the Person class - as it is actually the property of an outfit, not a person (i.e. two persons with the same outfit have exactly the same scores). I 'd prefer to have an Outfit class and put it there.
You have very poorly created the data structure.
enum Slot
{
HANDS, LEGS, CHEST, HEAD, FEET;
numbers = new int[values.length()]
}
enum COLOR
{
RED,BLUE,...;
}
enum Clothing {
GREEN_HAT(HEAD,GREEN), ...;
Slot slot;
Color color;
public static Clothing (Slot slot, Color color){...}
}
class Outfit extends Map <Slot, Clothing> {
countScore(){};
public static Outfit(){
//foreach slot this.put(slot, Clothing.values().get(0));
}
}
...
int n=slot.values.length()-1;
Outfit currentOutfit = new Outfit();
Outfit bestOutfit = new Outfit();
int currentActiveSlot = 0;
// make a cycle for combination of all Outfits
for an enum , you have to use the method "values()" to loop on it:
For (clothe c: clothes.values())

Lost on how to find the largest price below the target value from a given menu

I am trying to create a method called selectFood that takes the amount of money I have as a parameter, outputs the selections on the screen and returns the percentage tip I will be leaving rounded to one decimal place.
I have the possible costs without tip done through recursion but I need to compare them and find the larger one. Please help
public static void selectFood(double money){
/*String[]menu={"Bandera Pizza Bread","Boston's Pizza Bread","Garlic Twist Bread","Single Order",
"Sun-Dried Tomato Bruschetta","Three Cheese Toast","Double Order wings","Starter Size wings",
"Cactus Nachos","Baked Ravioli Bites","Southwest Quesadilla"};
*/
double[]itemCost={6.49,5.35,7.49,5.35,6.99,6.35,16.49,8.99,10.29,8.49,9.25};
possibilities(itemCost.length,"",itemCost,money);
//selectFood(n,itemCost,0);
}
public static void possibilities(int length,String sofar,double[]itemCost,double money){
if(length==0){
//selectFood(sofar,itemCost,money,0);
float totCost=0;
double target=money/1.15;
double minTip=money-target;
char[]sofarList=sofar.toCharArray();
for(int i=0;i<sofarList.length;i++){
if(sofarList[i]=='1'){
totCost+=itemCost[i];
}
}
if(totCost<target){
System.out.println(totCost);
}
}
else{
possibilities(length-1,sofar+"0",itemCost,money);
possibilities(length-1,sofar+"1",itemCost,money);
}
}
Right now I get back
0.0
8.49
6.35
6.99
5.35
7.49
5.35
6.49
To find the largest value below the target value, you need a variable to hold the current maximum in each iteration you are making thru recursion.
I changed the logic of your program to keep track of the current maximum value
public static void possibilities(int length,String sofar,double[]itemCost,double money){
if(length==0){
//selectFood(sofar,itemCost,money,0);
float totCost=0;
double target=money/1.15;
double minTip=money-target;
char[]sofarList=sofar.toCharArray();
for(int i=0;i<sofarList.length;i++){
if(sofarList[i]=='1'){
totCost+=itemCost[i];
}
}
if(totCost<target && totCost>max){
max = totCost;
System.out.println(totCost);
for(int i=0;i<sofarList.length;i++){
System.out.print(sofarList[i]);
}
System.out.println("\n");
}
}
else{
possibilities(length-1,sofar+"0",itemCost,money);
possibilities(length-1,sofar+"1",itemCost,money);
}
}
and you need to declare the variable max as static inside your class
static double max=0;
I added print statement to show the maximum value chosen in each feasible iteration.
After the end of all iterations the maximum value is stored in the max variable and to keep track of the menu list we need to add one more static variable as given below
static String menulist=null;
and we need to assign the value inside the method possibilities
if(totCost<target && totCost>max){
max = totCost;
menulist = sofarList;
}
The trouble you probably have with your method (besides it being relatively hard to understand) is, that your method is not actually computing a value (well... not returning any at least), but it is printing something to System.out. Generally, your methods should either compute something or alter state (e.g., perform I/O), but not both.
A simple fix would be to store totCost in an instance variable instead of printing it. That is, you can change your lines:
if(totCost < target){
System.out.println(totCost);
}
to
if(totCost < target && highestPrice < totCost){
highestPrice = totCost;
}
where highestPrice is an instance variable. Now you can access highestPrice from the selectFood method.
Note that this is a hack rathar than a clean solution! Your code would be much cleaner if your possibilities method would return the value it computes as the regular return value.
Rethink how you would recursivly compute the cost:
You are given a set (in your case, an array) of prices, and a budget.
For each price you can either take the item and pay the price, or you can not take the item and keep the money
If you have only one item in the list, the most expensive combination you can make is either 0, if you cannot afford the item, or the value of the item, if you can.
The last point is your base-case (not considering that you could also have an empty array). So you have for a base case
double possibilities(double[] items, double money){
if (items.length == 1 && items[0] < money / 1.15) return items[0]; else return 0;
A side note here: Don't put magic numbers (e.g., 1.15) in your code. They do not convey any meaning.
Now for the general case where items.length>1: We can either take the first item, or we can not take it. If we do take it, then we will have less money to spend on the remaining items. Also, we can only take it, if we can afford it, i.e., if money>items[0]. With the remaining money and the remaining items we call the method recursivly and take the bigger value.
if(items.length > 1)
if (items[0] < money / 1.15)
double totCostWithItem0 = items[0] + possibilities(/* items[1 to length], money - items[0] */)
double totCostWithoutItem0 = possibilities(/* items[1 to length], money */)
return totCostWithItem0 > totCostWithoutItem0 ? totCostWithItem0 : totCostWithoutItem0;
else // cannot afford it
return possibilities(/* items[1 to length], money */
Sorry for being so sloppy with the code here. Because you are working with arrays, you need to copy items 1 to length in a new array in order to pass it in the recursive call. Also... I am not quite sure what the division by 1.15 is supposed to do, so you may need to adjust the money you are recursively passing.

Is Chain of Rep. appropriate or not [closed]

Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions concerning problems with code you've written must describe the specific problem — and include valid code to reproduce it — in the question itself. See SSCCE.org for guidance.
Closed 9 years ago.
Improve this question
I was asked in an interview to implement a business rule
Requirements change. They always do:
assess a 20% fee for any amount below $100,000.
assess a 10% fee for any amount between $100,000 and $500,000.
assess a 5% fee for any amount above $500,000
Calculate the fee for an arbitrary amount x.
Example: Given a $600,000 invoice the fee should be $65,000.
Given a $50,000 invoice the fee should be $10,000.
Given a $200,000 invoice the fee should be $30,000.
I used CofR but the interviewer then asked what if their is more than 3 conditions like n of them would i create n-classes to handle each request.
Is their a better approach to the question asides writing a very long recursive function checking for each of the conditions.
CoR is helpful when the members of the chain have substantially different rules, but in this case, all of the rules are basically the same (charge a certain percent if the amount is over X). Instead of independent classes, just have one struct-like class that holds the minimum amount and percentage, and another one that looks up the appropriate fee:
class FeeSchedule {
static class Entry implements Comparable<Entry> {
int threshold;
int percentage;
int compareTo(Entry other) {
// sort by percentage, descending
}
SortedSet<Entry> feeTable;
int calculateFee(int invoiceAmount) {
for(Entry e : feeTable)
if(invoiceAmount > e.threshold)
return (invoiceAmount * e.percentage);
// error condition; return 0?
}
}
I would guess that the interviewer was implying that something like the chain-of-responsibility pattern would be a little over-engineered for a problem like this. There's also an argument that your implementing classes would actually have the same responsibility, in that they'd all be computing an amount based on a given input, just with different parameters.
I would probably do this with two simple classes. One would compute the percentage fee rate based on the input value and one would use this rate to return the fee amount.
If you need to add a fourth condition, you just add it to the class containing the rate computation. I don't see why it needs to be any more complicated than this for such a simple problem.
EDIT:
I was thinking along the same lines as #chrylis in that there'd be a class to perform the calculation by processing an ordered list of rates.
class Rate {
int rangeSize;
double commission;
Rate(int rangeSize, double commission){
this.rangeSize = rangeSize;
this.commission = commission;
}
int computeForAmount(int amount) {
if (amount <= 0) {
return 0;
}
return (int) (Math.min(amount, this.rangeSize) * this.commission);
}
}
class FeeCalculator {
List<Rate> rates = Arrays.asList(
new Rate(100, 0.2),
new Rate(400, 0.1),
new Rate(500, 0.05));
int calculateCommission(int startingAmount) {
int commission = 0;
int remainingAmount = startingAmount;
for (Rate rate : this.rates) {
commission += rate.computeForAmount(remainingAmount);
remainingAmount -= rate.rangeSize;
}
return commission;
}
}
I admit that I'm not entirely happy about breaking the encapsulation by calling rate.rangeSize but it does demonstrate the design I was trying to articulate.
I think a simple strategy pattern should be enough in this case. Something like:
interface FeeAssessor {
public double assessFee(double invoice);
}
FeeAssessor feeAssessor = new FeeAssessor() {
// logic goes here
};
double calculateFee(double invoice) {
return feeAssessor.assessFee(invoice);
}
For a simple business logic that you presented, I think it would be simpler to implement it all inside one assessFee() function. You can implement different (simple) ones and swap out the "strategy" object as needed. If the fee assessment algorithm depends on multiple varying factors that are independent of each other, then you can further split them up into multiple strategy methods.

Cannot use arrays or foreach loops to iterate over list of data and print out only certain values

The Prompt:
A program that accepts a candy name (for example, “chocolate-covered blueberries”), price per pound, and number of pounds sold in the average month, and displays the item’s data only if it is a best-selling item. Best-selling items are those that sell more than 2000 pounds per month.
b. A program that accepts candy data continuously until a sentinel value is entered and displays a list of high- priced, best-selling items. Best-selling items are defined in Exercise 2a. High-priced items are those that sell for $10 per pound or more.
Here is an example of a good design in operation:
High-priced, Best-selling Candy
Fudge $12.50 4500 lbs
Vanilla Creme $13.75 2200 lbs.
Fudge, 12.50, 4500 Jawbreakers, 6.50, 5500 Chocolate, 14.00, 790 Butterscotch, 9.50, 4500 Vanilla Creme, 13.75, 2200
Item that sold most pounds: Jawbreakers
but the problem I am having is that my teacher is not letting me use for loops, or arrays. And I do not want to define multiple instances of the same variable because it is finite to a certain amount.... What would be the most efficient way of doing this?
start
// Declarations
num QUIT = "Y";
final String HEADING = "High Priced, Best Selling Candy" + "\n" + "\n";
final String HSPS = candyName + " " + candyPrice + " " + candySold + " ";
final String MOSTSOLD = "Item that sold the most pounds is "
while <> QUIT;
enterCandy();
printHighPriceBestSelling();
printSoldMostPounds();
endwhile;
stop
entercandy()
String candyName = "poop";
double candyPrice = 0.0;
double candyWeight = 0.0;
int candySold = 0;
output "Please enter name of candy.";
input candyName;
output "Please enter candy price.";
input candyPrice;
output "Please enter pounds sold.";
input candySold;
printHighPriceBestSelling()
if(candySold > 2000 && candyPrice > 10)
{
output HEADING;
output HSPS;
}
else
{
output "There were/are no best selling, high priced candy!"
}
printSoldMostPounds();
//There is no basis for comparison.
There are only two ways of doing this. Create lots of different, artbitrary, and predefined variables to be filled by the loop until they are overwritten. Lets say 10. Or create an array. I am sure there is an overly complex way of doing it with nested if/switch/while loops, but why teach us/force us to use the ugly inefficient way?
output "MOSTSOLD ";
I'm assuming that, besides arrays, you're teacher isn't allowing you to use any standard Collection objects.
You could always just build your own LinkedList of entered candy orders--it's ugly, but it would work. A single "link" in the chain would look like this
public class CandyOrderLink {
private String candyName;
private Double candyPrice;
private Double orderAmount;
private CandyOrderLink nextOrderLink;
public CandyOrderLink(String candyName, Double candyPrice, Double orderAmount) {
this.candyName = candyName;
this.candyPrice = candyPrice;
this.orderAmount = orderAmount;
}
public CandyOrderLink getNextLink() {
return nextOrder;
}
public void setNextLink(CandyOrderLink nextOrderLink) {
this.nextOrderLink= nextOrderLink;
}
public String getCandyName() {
return candyName;
}
public Double getCandyPrice() {
return candyPrice;
}
public Double getOrderAmount() {
return orderAmount;
}
}
Not sure if I'm quite grasping the point of the assignment, but using a list data-structure to keep track of all orders will work. Just build a link for each entry (candyName, price, amount) and set that link as the next link of the previous one. At the end of input, iterate through the list by repeatedly calling getNextLink() on each link and printing information (if appropriate). Here is Wikipedia's article on linked lists: http://en.wikipedia.org/wiki/Linked_list
From the problem's description, I see no need to store the data entered so that it can be sorted. Both a and b state simple conditions for displaying a candy: greater than 2,000 pounds and at least $10/lb. You can print each entry immediately after it is entered.
However, your example output implies that you must pick the single best-selling candy which contradicts the description. Which is correct?

Solving a simple maximization game

I've got a very simple question about a game I created (this is not homework): what should the following method contain to maximize payoff:
private static boolean goForBiggerResource() {
return ... // I must fill this
};
Once again I stress that this is not homework: I'm trying to understand what is at work here.
The "strategy" is trivial: there can only be two choices: true or false.
The "game" itself is very simple:
P1 R1 R2 P2
R5
P3 R3 R4 P4
there are four players (P1, P2, P3 and P4) and five resources (R1, R2, R3, R4 all worth 1 and R5, worth 2)
each player has exactly two options: either go for a resource close to its starting location that gives 1 and that the player is sure to get (no other player can get to that resource first) OR the player can try to go for a resource that is worth 2... But other players may go for it too.
if two or more players go for the bigger resource (the one worth 2), then they'll arrive at the bigger resource at the same time and only one player, at random, will get it and the other player(s) going for that resource will get 0 (they cannot go back to a resource worth 1).
each player play the same strategy (the one defined in the method goForBiggerResource())
players cannot "talk" to each other to agree on a strategy
the game is run 1 million times
So basically I want to fill the method goForBiggerResource(), which returns either true or false, in a way to maximize the payoff.
Here's the code allowing to test the solution:
private static final int NB_PLAYERS = 4;
private static final int NB_ITERATIONS = 1000000;
public static void main(String[] args) {
double totalProfit = 0.0d;
for (int i = 0; i < NB_ITERATIONS; i++) {
int nbGoingForExpensive = 0;
for (int j = 0; j < NB_PLAYERS; j++) {
if ( goForBiggerResource() ) {
nbGoingForExpensive++;
} else {
totalProfit++;
}
}
totalProfit += nbGoingForExpensive > 0 ? 2 : 0;
}
double payoff = totalProfit / (NB_ITERATIONS * NB_PLAYERS);
System.out.println( "Payoff per player: " + payoff );
}
For example if I suggest the following solution:
private static boolean goForBiggerResource() {
return true;
};
Then all four players will go for the bigger resource. Only one of them will get it, at random. Over one million iteration the average payoff per player will be 2/4 which gives 0.5 and the program shall output:
Payoff per player: 0.5
My question is very simple: what should go into the method goForBiggerResource() (which returns either true or false) to maximize the average payoff and why?
Since each player uses the same strategy described in your goForBiggerResource method, and you try to maximize the overall payoff, the best strategy would be three players sticking with the local resource and one player going for the big game. Unfortunately since they can not agree on a strategy, and I assume no player can not be distinguished as a Big Game Hunter, things get tricky.
We need to randomize whether a player goes for the big game or not. Suppose p is the probability that he goes for it. Then separating the cases according to how many Big Game Hunters there are, we can calculate the number of cases, probabilities, payoffs, and based on this, expected payoffs.
0 BGH: (4 choose 0) cases, (1-p)^4 prob, 4 payoff, expected 4(p^4-4p^3+6p^2-4p+1)
1 BGH: (4 choose 1) cases, (1-p)^3*p prob, 5 payoff, expected 20(-p^4+3p^3-3p^2+p)
2 BGH: (4 choose 2) cases, (1-p)^2*p^2 prob, 4 payoff, expected 24(p^4-2p^3+p^2)
3 BGH: (4 choose 3) cases, (1-p)*p^3 prob, 3 payoff, expected 12(-p^4+p^3)
4 BGH: (4 choose 4) cases, p^4 prob, 2 payoff, expected 2(p^4)
Then we need to maximize the sum of the expected payoffs. Which is -2p^4+8p^3-12p^2+4p+4 if I calculated correctly. Since the first term is -2 < 0, it is a concave function, and hopefully one of the roots to its derivative, -8p^3+24p^2-24p+4, will maximize the expected payoffs. Plugging it into an online polynomial solver, it returns three roots, two of them complex, the third being p ~ 0.2062994740159. The second derivate is -24p^2+48p-24 = 24(-p^2+2p-1) = -24(p-1)^2, which is < 0 for all p != 1, so we indeed found a maximum. The (overall) expected payoff is the polynomial evaluated at this maximum, around 4.3811015779523, which is a 1.095275394488075 payoff per player.
Thus the winning method is something like this
private static boolean goForBiggerResource ()
{
return Math.random() < 0.2062994740159;
}
Of course if players can use different strategies and/or play against each other, it's an entirely different matter.
Edit: Also, you can cheat ;)
private static int cheat = 0;
private static boolean goForBiggerResource ()
{
cheat = (cheat + 1) % 4;
return cheat == 0;
}
I take it you tried the following:
private static boolean goForBiggerResource() {
return false;
};
where none of the player try to go for the resource that is worth 2. They are hence guaranteed to each get a resource worth 1 every time hence:
Payoff per player: 1.0
I suppose also that if you ask this nice question is because you guess there's a better answer.
The trick is that you need what is called a "mixed strategy".
EDIT: ok here I come with a mixed-strategy... I don't get how Patrick found the 20% that fast (when he commented, only minutes after you posted your question) but, yup, I found out basically that same value too:
private static final Random r = new Random( System.nanoTime() );
private static boolean goForBiggerResource() {
return r.nextInt(100) < 21;
}
Which gives, for example:
Payoff per player: 1.0951035
Basically if I'm not mistaken you want to read the Wikipedia page on the "Nash equilibrium" and particularly this:
"Nash Equilibrium is defined in terms of mixed strategies, where players choose a probability distribution over possible actions"
Your question/simple example if I'm not mistaken also can be used to show why colluding players can do better average payoffs: if players could colude, they'd get 1.25 on average, which beats the 1.095 I got.
Also note that my answers contains approximation errors (I only check random numbers from 0 to 99) and depends a bit on the Random PRNG but you should get the idea.
if the players cannot cooperate and have no memory there is only one possible way to implement goForBiggerResource: choose a value randomly. Now the question is what is the best rate to use.
Now simple mathematics (not really programming related):
assume the rate x represents the probability to stay with the small resource;
therefore the chance for no player going for the big one is x^4;
so the chance for at least one player going to the big one is 1-x^4;
total profit is x + ( 1 - x^4 ) / 2
find the maximum of that formula for 0% <= x <= 100%
the result is about 79.4% (for returning false)
Mmm, I think your basic problem is that the game as described is trivial. In all cases, the optimal strategy is to stick with the local resource, because the expected payoff for going for R5 is only 0.5 (1/4 * 2). Raise the reward for R5 to 4, and it becomes even; there's no better strategy. reward(R5)>4 and it always pays to take R5.

Categories

Resources