So I made this game where I pick a random number 1-100 and the computer guesses and I tell it too high, too low, or whatever. It works pretty good, except that it gets stuck in cycles when I try binary search and I can't seem to prevent it. I'll demonstrate:
Say the number is 79. Eventually, the program will ask if the number is 42. No, that's too low. Then it asks if it's 71. Guess higher! Then it asks 82. Nope, lower. Then it goes back to 42 and the cycle repeats over and over. Here's my code (note that it's an excerpt from the full code, so excuse the lack of the importation of JOptionPane and whatnot):
int x = 50;
int y = x;
int[] alreadyGuessed = {};
boolean secondGuess = false;
//The user has to select, too high, too low, correct!
while (secondGuess == false) {
Object[] options = {"Too high", "Too Low", "Correct"};
int pick = JOptionPane.showOptionDialog(null, "Is your number " + x
+ "?", "Guess",
JOptionPane.DEFAULT_OPTION, JOptionPane.WARNING_MESSAGE,
null, options, options[2]);
for (int positionInList = 0; positionInList >= 100; positionInList++) {
arrayDemo(x, positionInList);
}
if (pick == 0) {
int max = x - 1;
int min = 0;
x = ((max + min) / 2);
}
if (pick == 1) {
int max = 100;
int min = x+1;
x = ((max + min) / 2);
}
if (pick == 2) {
System.out.println("Yay! I win!");
secondGuess = true;
}
}
You need to keep track of both min and max. So in your code, when you say max = 100 or min = 0, the program is forgetting what the max & min are.
Delete those lines & you should be OK. In essence, you need to remember min & max all the way until you find the answer.
e.g. if the number is 42, min and max will be like:
0..100, guess 50 (too high)
0..49, guess 25 (too low)
26..49, guess 38 (too low)
39..49, guess 44 (too high)
39..43, guess 41 (too low)
42..43, guess 42 WIN!
Notice how both min and max zoom in on the answer. Carrying that information forward is what makes the strategy work.
Related
I have on the first problem of Google Foobar Level 2. The problem is:
Being a henchman isn't all drudgery. Occasionally, when feeling generous, Commander Lambda hand out Lucky LAMBs (Lambda's All-purpose Money Bucks). Henchmen can use Lucky LAMBs to buy things like a second pair of socks, a pillow for their bunks, or even a third daily meal!
However, actually passing out LAMBs isn't easy. Each henchman squad has a strict seniority ranking which must be respected -- or else the henchmen will revolt and you'll all get demoted back to minions again!
There are 4 key rules which you must follow in order to avoid a revolt:
1. The most junior henchman (with the least seniority) gets exactly 1 LAMB. (There will always be at least 1 henchman on a team.)
2. A henchman will revolt if the person who ranks immediately above them gets more than double the number of LAMBs they do.
3. A henchman will revolt if the amount of LAMBs given to their next two subordinates combined is more than the number of LAMBs they get. (Note that the two most junior henchmen won't have two subordinates, so this rule doesn't apply to them. The 2nd most junior henchman would require at least as many LAMBs as the most junior henchman.)
4. You can always find more henchmen to pay - the Commander has plenty of employees. If there are enough LAMBs left over such that another henchman could be added as the most senior while obeying the other rules, you must always add and pay that henchman.
Note that you may not be able to hand out all the LAMBs. A single LAMB cannot be subdivided. That is, all henchmen must get a positive integer number of LAMBs.
Write a function called solution(total_lambs), where total_lambs is the integer number of LAMBs in the handout you are trying to divide. It should return an integer which represents the difference between the minimum and maximum number of henchmen who can share the LAMBs (that is, being as generous as possible to those you pay and as stingy as possible, respectively) while still obeying all of the above rules to avoid a revolt. For instance, if you had 10 LAMBs and were as generous as possible, you could only pay 3 henchmen (1, 2, and 4 LAMBs, in order of ascending seniority), whereas if you were as stingy as possible, you could pay 4 henchmen (1, 1, 2, and 3 LAMBs). Therefore, solution(10) should return 4-3 = 1.
To keep things interesting, Commander Lambda varies the sizes of the Lucky LAMB payouts. You can expect total_lambs to always be a positive integer less than 1 billion (10 ^ 9).
My code:
public class Solution {
public static int solution(int total_lambs) {
// person 1 gets 1 lamb
// person 2 gets more than 1 lamb
// person 3 gets more than or = person 1 + person 2 but less or = than double person 2
// person 4 gets more than person 3 + person 2 but less than or = double person 3
return (solution_conservative(total_lambs) - solution_generous(total_lambs));
}
public static int solution_generous(int total_lambs) {
int lamb_pay_current = 0;
int person_before = 0;
//int person_before_before = 0;
int person_counter = 0;
for (int lamb_pay_cumm = 0; lamb_pay_cumm < total_lambs; lamb_pay_cumm += lamb_pay_current) {
if (!(total_lambs > 0)) {break;}
if ((total_lambs == 1)) {break;}
if ((lamb_pay_cumm == 0) && (total_lambs > 0)) {
lamb_pay_current = 1;
person_counter++;
continue;
}
if ((lamb_pay_cumm) == 1 && (total_lambs > 1)) {
lamb_pay_current = 2;
person_before = 1;
person_counter++;
continue;
}
if (person_before == 1) {
person_before = 2;
}
if (person_before * 2 + lamb_pay_cumm > total_lambs) {continue;}
lamb_pay_current = person_before * 2;
person_counter++;
// person_before_before = person_before;
person_before = lamb_pay_current;
}
if (total_lambs == 1) {return 1;}
if (!(total_lambs > 0)) {return 0;}
return person_counter;
}
public static int solution_conservative(int total_lambs) {
int lamb_pay_current = 0;
int person_before = 0;
int person_before_before = 0;
int person_counter = 0;
for (int lamb_pay_cumm = 0; lamb_pay_cumm < total_lambs; lamb_pay_cumm += lamb_pay_current) {
if (!(total_lambs > 0)) {break;}
if ((total_lambs == 1)) {break;}
if ((lamb_pay_cumm == 0) && (total_lambs > 0)) {
lamb_pay_current = 1;
person_counter++;
continue;
}
if ((lamb_pay_cumm) == 1 && (total_lambs > 1)) {
lamb_pay_current = 1;
person_before = 1;
person_counter++;
continue;
}
if (person_before == 1) {
person_before_before = 1;
}
if (person_before + person_before_before + lamb_pay_cumm > total_lambs) {continue;}
lamb_pay_current = person_before + person_before_before;
person_counter++;
person_before_before = person_before;
person_before = lamb_pay_current;
}
if (total_lambs == 1) {return 1;}
if (!(total_lambs > 0)) {return 0;}
return person_counter;
}
}
What I am trying to get done is that I have 2 functions. One is to find the most "generous" possible way to give everyone the most amount of LAMBS possible before someone revolts. This pattern appears to be like this: 1, 2, 4, 8, 16 etc. I may be wrong. The other function is to find the most "conservative" possible way to give everyone the least amount of LAMBS possible before someone revolts. This pattern appears to be like this: 1, 1, 2, 3, 5, 8, 13, etc. I may also be wrong.
Whenever I do
"verify Solutions.java", it always fails Test #7. All other tests succeed, just this one fails. And I don't know what Test #7 is.
i have also tried most of the tests i knew of on Intellij Idea but all of them looked correct to me...
You are right about the patterns of generous and stingy. Generous path follows powers of two and the stingy follows pattern of fibonacci.
It is hard to figure out what is wrong with your code. But I think most probably your code doesn't calculate the number of henchmen correctly especially when there are remaining lambs that can be handed out without violating the restrictions.
Generous:
Start with the power of two = 1 and keep taking powers of two and adding to lambs to be paid, until you exceed the total_lambs. When you exceed make sure that you subtract the excess from the paid out lambs, so you get the original lambs paid. Also get the last two paid lambs which can be easily obtained by:
current_power_of_two/2 + current_power_of_two/4
See if it is less than or equal to remaining lambs = total_lambs - lambs_paid, then increment the henchmen by 1.
Stingy: Its the same approach except here you keep track of the last two paid lambs in the fibonacci series.
Here is one straightforward implementation for both generous and stingy handouts:
public static int solution_generous(int total_lambs) {
// Apply power of two
int power_of_two = 1;
int henchmen = 1;
int lambs_paid = 1;
while ( lambs_paid <= total_lambs ) {
power_of_two = 2*power_of_two;
lambs_paid += power_of_two;
if ( lambs_paid > total_lambs ) {
lambs_paid -= power_of_two;
power_of_two = power_of_two/2;
break;
}
henchmen++;
}
int last_one = power_of_two;
int last_before_one = power_of_two/2;
if ( last_one + last_before_one <= total_lambs - lambs_paid ) {
henchmen++;
}
return henchmen;
}
public static int solution_conservative(int total_lambs) {
//Fibonacci
int f1 = 1;
int f2 = 1;
int f12 = f1 + f2;
int henchmen = 1;
int lambs_paid = f1;
int last_two_paid = f2;
while ( lambs_paid <= total_lambs ) {
last_two_paid = f2;
f1 = f2;
f2 = f12;
f12 = f1 + f2;
lambs_paid += f1;
if ( lambs_paid > total_lambs ) {
break;
}
henchmen++;
}
if ( last_two_paid <= total_lambs - lambs_paid ) {
henchmen++;
}
return henchmen;
}
I have been assigned to write a java program for class that counts multiples. The program takes three integers as input: low, high, x. The program then outputs the number of multiples of x between low and high exclusively.
If the input is: 1, 10, 2
the output would be: 5
My teacher has the proclivity to assign problems we haven't covered in class and I am unsure where to begin. Im not asking for the full code, just how to set up the problem
I am unsure of how to follow my logic thru the program: what I have so far is this
import java.util.Scanner;
public class LabProgram {
public static void main(String[] args) {
Scanner scnr = new Scanner(System.in);
int low, high, x;
int count = 0;
low = scnr.nextInt();
high = scnr.nextInt();
x = scnr.nextInt();
for(int i = low; i <= high; i++){
if(i % x == 0){
count++;
}
if (i % x != 0){
//System.out.println(count);// someone suggested to move this
}
}
System.out.println(count);
}
}
~~~~~~
If I input 1, 10, 2
My output is 01234
Moved the print of count outside of the loop... man I am tired.
FINAL EDIT: This code works, it accomplishes the goal. Thank you to #charisma and everyone else that helped me understand what was going on here. I am new to java but determined to learn more! Thanks all!!!!!
You can input numbers using scanner class similar to the following code from w3schools:
import java.util.Scanner; // Import the Scanner class
class Main {
public static void main(String[] args) {
Scanner myObj = new Scanner(System.in); // Create a Scanner object
System.out.println("Enter username");
String userName = myObj.nextLine(); // Read user input
System.out.println("Username is: " + userName); // Output user input
}
}
low, high and x can be of data type int.
To check which numbers between low and high are multiples of x, you can use a for loop. You can declare a new variable count that can be incremented using count++ every time the for loop finds a multiple. The % operator could be useful to find multiples.
You can output using System.out.println(" " + );
Edit:
% operator requires 2 operands and gives the remainder. So if i % x == 0, it means i is a multiple of x, and we do count++.
The value of i will run through low to high.
for (i = low; i <= high; i++) {
if (i % x == 0) {
count++;
}
}
Once you get to the basic implementation (as explained by Charisma), you'll notice, that it can take a lot of time if the numbers are huge: you have high - low + 1 iterations of the loop. Therefore you can start optimizing, to get a result in constant time:
the first multiple is qLow * x, where qLow is the ceiling of the rational quotient ((double) low) / x,
the last multiple is qHigh * x, where qHigh is the floor of the rational quotient ((double) high) / x,
Java provides a Math.floor() and Math.ceil(), but you can get the same result using integer division and playing with the signs:
final int qLow = -(-low / x);
final int qHigh = high / x;
Now you just have to count the number of integers between qLow and qHigh inclusive.
return qHigh - qLow + 1;
Attention: if x < 0, then you need to use qLow - qHigh, so it is safer to use:
return x > 0 ? qHigh - qLow + 1 : qLow - qHigh + 1;
The case x == 0 should be dealt with at the beginning.
put count++; after the last print statement
Looking at this RuneScape experience table: http://runescape.wikia.com/wiki/Experience/Table
You can see that an experience point amount correlates to a "level"
where:
if (xp < 83){
level = 0;
}
else if (xp >= 83 && xp < 174){
level = 1;
}
//etc
So when I save just the experience as a field for my player, how can I calculate the level they will be with their experience?
Without looping through 100 experience point integers.
i.e I don't want to do this: (Because a player will have many levels, meaning this will need to be looped many times)
for (int i =0; i < 100) {
if (playersExperience < experienceRequiredForLevel(i){
return i;
}
perhaps there is a faster way to do this?
Edit:
There is a formula to calculate the experience for level X here:
rsdo.net/rsdonline/guides/Experience%20formula.html
though I don't know how to reverse engineer that to find level instead
You can have a multiplier of X the experience levels. And you just take the integer part of division.
Ex if X=100: LVL1 = 100, LVL2 = 200; etc ...
In this case you just can just:
LVL = Experience / X;
Ex: 100 / 100 = 1 LVL, 200 / 100 = 2 ... etc
X can be any number.
Hope it helps.
You could use binary search to speed up things:
private static final int[] experiences={83, 174, ...};
int level(int experience) {
// boundary check
if (experience < experiences[0])
return 0;
if (experience >= experiences[experiences.length-1])
return experiences.length;
int minLevel = 0;
int maxLevel = experiences.length;
while (maxLevel - minLevel > 1) {
int chk = (maxLevel+minLevel)/2;
if (experience < experiences[chk])
maxLevel = chk;
if (experience >= experiences[chk])
minLevel = chk;
}
return maxLevel;
}
Given that N is a random number (range 1 to 1000). We need to guess the N and for each guess, one of the following feedbacks may be given:
The guess is correct;
The guess is too large, so you should guess a smaller number;
The guess is too small, so you should guess a larger number.
In case 3, the value of N will increase by P, where P is another random number(range 1 to 200).
If the initial value of N=800 and P=150. You guess in the following sequence:
Example
How do you code the following especially when it involves two number (N and P). I was thinking of using Binary Search but the it would be a problem if we do not know the value of P.
This is my code as of now :
myGuess=0;
checkCode=0;
int lower = 1, upper = 999;
myGuess = (lower+upper)/2;
do{
if (checkCode == 2) {
upper = myGuess - 1;
}
else if (checkCode == 3){
lower = myGuess + 1;
upper += ran.nextInt(200); //Need to guess the P value
}
myGuess = (lower+upper)/2;
}while(checkCode!=1);
The first step is to obtain a working guessing system. This code provides a rough guide to a binary search approach. The second step would the be to analyze how to improve efficiency. (note: can restore some of the S.O.P() to see progress)
private static int doGuess()
{
int lowerBound = 1;
int upperBound = 1000;
int numberToGuess = ThreadLocalRandom.current().nextInt(upperBound) + 1;
int guess = 0;
int steps = 0;
int increases = 0;
while (guess != numberToGuess) {
++steps;
guess = (lowerBound + upperBound) / 2;
// System.out.printf("[%5d] Guessing %d (is: %d)%n",
// steps,
// guess,
// numberToGuess);
if (guess == numberToGuess) {
System.out.printf("Guessed %d in %d steps (%d increases)%n",
numberToGuess,
steps,
increases);
continue;
}
else if (guess > numberToGuess) {
// System.out.println("Guess is too high!");
// adjust upper bound to be guess
upperBound = guess;
}
else {
// System.out.println("Guess is too low; changing number");
numberToGuess += ThreadLocalRandom.current().nextInt(200) + 1;
// adjust lower bound to this guess
lowerBound = guess;
// the number moved, so adjust upper bound by max range
upperBound += 200;
// track increases
++increases;
}
}
return steps;
}
public static void main(String[] args)
{
List<Integer> steps = new ArrayList<>();
int iterations = 10;
for (int i = 0; i < iterations; ++i) {
steps.add(doGuess());
}
IntSummaryStatistics stats =
steps.stream().collect(IntSummaryStatistics::new,
IntSummaryStatistics::accept,
IntSummaryStatistics::combine);
System.out.println(stats);
}
Output:
Guessed 8838 in 145 steps (83 increases)
Guessed 6301 in 106 steps (59 increases)
Guessed 3239 in 58 steps (30 increases)
Guessed 5785 in 109 steps (58 increases)
Guessed 2547 in 56 steps (27 increases)
Guessed 16071 in 300 steps (164 increases)
Guessed 3847 in 54 steps (31 increases)
Guessed 3125 in 42 steps (24 increases)
Guessed 6708 in 93 steps (57 increases)
Guessed 7433 in 143 steps (74 increases)
IntSummaryStatistics{count=10, sum=1106, min=42, average=110.600000, max=300}
[Note: based upon quick simulations, the average across multiple runs is about 115, so efficiency improvements should reduce on average from 115 steps]
[Note: the amount of change in the code is different with each guess that is too low; a comment by the OP might suggest the increase is randomly chosen once, in which case the increase in the number to guess in the above code would need to change]
Edit:
Logically if guessing low moves the the number one is to guess, then using some sort of bias towards picking higher would seem to be logical. As Holger has suggest in the various comments, there are some ways to make adjustments.
I had attempted some basic adjustments prior to seeing Holger's suggestion; I then also attempted to implement his algorithm. However, I have not found the adjustments to make a marked improvement (and some are worse).
Using 100,000 runs, the standard binary search averaged 127.7 steps (note: up slightly from my earlier estimate based upon a lower run count). Assuming I implemented Holger's algorithm correctly, at 100,000 the average was 126.6 steps.
As I lack the math skills (and unfortunately time at the moment) to investigate further, it seems that simple modifications do not seem to radically change the efficiency of the algorithm on average. I did not investigate worse cases. It would be interesting to ask the question over on the Math StackExchange to see if they could provide any definite input. I did do a quick Google search, but did not have time to read the academic papers that might give some improvement (again, with unknown trade-offs in speed and algorithmic complexity).
It is, of course, possible I did not implement Holgen's suggestion properly. Here is the code I used (replacing the change in the guess calculation if too low) based straight from the comment:
if (tryHolgen) {
double risc = 200.0/(upperBound-lowerBound);
if (risc <= 1) {
guess = (upperBound + lowerBound) /2;
}
else {
guess = upperBound -
Math.max((int)((upperBound - lowerBound)/risc/2),1);
}
else {
guess = (lowerBound + upperBound) / 2;
}
I am curious if others have a better implementation than the straight binary search.
It is interesting, though, that a 1..1000 range with a standard binary search would take 8 steps on average with O(log n) complexity. By allowing the guess to change, it moves the average by about 120 steps.
I reworked my solution once I understood what you were trying to do. This will give you some statistics. The current solution incorporates a random number between 0 and 13 for each guess, as well as adding the lower and upper bound together and divide them by 2. Why 13? It seems like it's a sweet spot for this exact task.
public static void main(String args[]) throws IOException {
int numTests = 1000000;
long averageTries = 0;
int maxAttempts = 0;
int minAttempts = Integer.MAX_VALUE;
for (int i = 0; i < numTests; i++) {
int numAttempts = 0;
int answer = (int) (Math.random() * 1000) + 1;
int lower = 1;
int upper = 1000;
int myGuess;
do {
myGuess = (int) (((lower + upper) / 2) + (Math.random() * 14));
numAttempts++;
if (myGuess > answer) {
upper = myGuess;
} else if (myGuess < answer) {
lower = myGuess;
upper += (lower + upper) / 2;
answer += (int) (Math.random() * 200) + 1;
}
} while (myGuess != answer);
averageTries += numAttempts;
if (numAttempts > maxAttempts) {
maxAttempts = numAttempts;
}
if (numAttempts < minAttempts) {
minAttempts = numAttempts;
}
}
System.out.println("Average attempts (of " + numTests + " tests): " + (averageTries / numTests));
System.out.println("Most attempts in one run: " + maxAttempts);
System.out.println("Least attempts in one run: " + minAttempts);
}
Output:
Average attempts (of 1000000 tests): 266
Most attempts in one run: 72228
Least attempts in one run: 1
You can try to do something similar to binary search. Just consider that binary search requires the input to be sorted. If the input is not sorted you have to sort it yourself.
Rather than guessing a random number, just guess the one exactly in the middle of the partition. However compared with binary search which halves each time, in this case it's a moving target, so the bounds of the search need to be adjusted for that.
I am currently in a Java 1 class, and made a number guessing game for fun. Basic take input, tells you if it's too high or low, then lets you guess again. I thought it would be interesting to make it so the computer guesses as well, then compares your guesses to its. I have all of the generation and comparing working, but it continues to guess numbers without taking the greater/less than into account, which I want to add. I have:
public static void autoPlay(int num){
Random rand = new Random();
int guess1 = rand.nextInt(100) + 1;
int counter = 0;
while(guess1 != num){
counter++;
int guess = rand.nextInt(100) + 1;
int initialHigh = 100;
int initialLow = 0;
// I want it to guess smart and recognize if it were too high or too low, and generate a number between there
if(guess1 > num){
int newGuess = rand.nextInt(initialHigh - guess1) + 1;
}else if(guess1 < num){
int newGuess2 = rand.nextInt(initialLow + guess1) + 1;
}
initialLow = guess;
initialHigh = guess;
guess1 = guess;
System.out.printf("%3d", guess1);
}
System.out.println("It took " + counter + " guesses to get the correct number");
}
I can't tell what is wrong with my math in the if statement, or if theres just something I can call to do that.
If you want to avoid duplicates, then generate the appropriate numbers and shuffle it (for a full random function):
List<Integer> values = IntStream.range(0, /* max */).collect(Collectors.toList());
Collections.shuffle(values);
int guesses = values.indexOf(/* some number */) + 1;
The list would be fully randomly ordered, so you'd guess in order of the randomized list, thus the index is the number of guesses (-1, since it's 0-indexed)
The problem with your code is that you are just using the same bounds for the random number. You generate new bounds here:
if(guess1 > num){
int newGuess = rand.nextInt(initialHigh - guess1) + 1;
}else if(guess1 < num){
int newGuess2 = rand.nextInt(initialLow + guess1) + 1;
}
But you don't use them at all, you just reuse the values you had before:
initialLow = guess;
initialHigh = guess;
guess1 = guess;
System.out.printf("%3d", guess1);
You must use the values produced by newGuess and newGuess2 (althought you don't need these two variables, declare one of them outside the if and just assign a value to it inside the if). Then you will be using updated values.
I also noticed that you created many variables that store the same value, such as guess and guess1, which you don't need, you just need to declare one of them and reuse later (so you can save memory : ) ).
Also, I see a problem in setting initialHigh and initialLow both as guess, why would you want that?
Try to review your code logic and clean up some variables, some of them are duplicated.
But, in summary, I think the problem is that you are generating new bounds but you are not using them.
Let me know if this helped you and remember to upvote/select this answer as correct if it did : ). If you still have questions, post again.