Trying to perform a binary search on a sorted array of Book objects.
Its not working well, it returns the correct results for some of the objects, but not all.
I went through the loop on paper and it seems that a number can get missed out due to rounding #.5 upwards.
Any ideas how to make this work?
Book found = null;
/*
* Search at the center of the collection. If the reference is less than that,
* search in the upper half of the collection, else, search in the lower half.
* Loop until found else return null.
*/
int top = numberOfBooks()-1;
int bottom = 0;
int middle;
while (bottom <= top && found == null){
middle = (bottom + top)/2;
if (givenRef.compareTo(bookCollection.get(middle).getReference()) == 0) {
found = bookCollection.get(middle);
} else if (givenRef.compareTo(bookCollection.get(middle).getReference()) < 0){
bottom = middle + 1;
} else if (givenRef.compareTo(bookCollection.get(middle).getReference()) > 0){
top = middle - 1;
}
}
return found;
A couple suggestions for you:
there's no need to keep a Book variable. In your loop, just return the book when it's found, and at the end return null. And you can also remove the boolean check for the variable in the while condition.
the middle variable can be scoped inside the loop, no need to have it live longer.
you're doing bookCollection.get(middle).getReference() three times. Consider creating a variable and then using it.
the middle = (bottom + top)/2 is a classic mistake in binary search implementation algorithms. Even Joshua Bloch, who wrote the Java Collection classes, made that error (see this interesting blog post about it). Instead, use (bottom+top) >>> 1, to avoid integer overflow for very large values (you probably wouldn't encounter this error, but it's for the principle).
As for your actual problem statement, rounding would be downwards (integer division), not upwards. To troubleshoot the problem:
are you sure the numberOfBooks() method corresponds to the length of your collection?
are you sure the compareTo() method works as expected for the types you are using (in your code example we do not know what the getReference() return type is)
are you sure your collection is properly sorted according to getReference()?
and finally, are you sure that using givenRef.compareTo(bookCollection.get(middle).getReference()) < 0 is correct? In standard binary search implementations it would be reversed, e.g. bookCollection.get(middle).getReference().compareTo(givenRef) < 0. This might be what donroby mentions, not sure.
In any case, the way to find the error would be to try out different values and see for which the output is correct and for which it isn't, and thus infer what the problem is. You can also use your debugger to help you step through the algorithm, rather than using pencil and paper if you have to run many tests. Even better, as donroby said, write a unit test.
What about Collections.binarySearch()?
All of JRL's suggestions are right, but the actual fail is that your compares are reversed.
I didn't see this immediately myself, but replicating your code into a function (using strings instead of Books), writing a some simple Junit tests and then running them in the debugger made it really obvious.
Write unit tests!
I found the problem.
It turns out i was binary searching my bookCollection arrayList, and NOT the new sroted array i had created - sortedLib.
Silly mistake at my end, but thanks for the input and suggestions!
Related
The question was asking me to return set containing all the possible combination of strings made up of "cc" and "ddd" for given length n.
so for example if the length given was 5 then set would include "ccddd" and "dddcc".
and length 6 would return set containing "cccccc","dddddd"
and length 7 would return set contating "ccdddcc","dddcccc","ccccddd"
and length 12 will return 12 different combination and so on
However, set returned is empty.
Can you please help?
"Please understand extremeply poor coding style"
public static Set<String> set = new HashSet<String>();
public static Set<String> generateset(int n) {
String s = strings(n,n,"");
return set; // change this
}
public static String strings(int n,int size, String s){
if(n == 3){
s = s + ("cc");
return "";}
if(n == 2){
s = s + ("ddd");
return "";}
if(s.length() == size)
set.add(s);
return strings(n-3,size,s) + strings(n-2,size,s);
}
I think you'll need to rethink your approach. This is not an easy problem, so if you're extremely new to Java (and not extremely familiar with other programming languages), you may want to try some easier problems involving sets, lists, or other collections, before you tackle something like this.
Assuming you want to try it anyway: recursive problems like this require very clear thinking about how you want to accomplish the task. I think you have a general idea, but it needs to be much clearer. Here's how I would approach the problem:
(1) You want a method that returns a list (or set) of strings of length N. Your recursive method returns a single String, and as far as I can tell, you don't have a clear definition of what the resulting string is. (Clear definitions are very important in programming, but probably even more so when solving a complex recursive problem.)
(2) The strings will either begin with "cc" or "ddd". Thus, to form your resulting list, you need to:
(2a) Find all strings of length N-2. This is where you need a recursive call to get all strings of that length. Go through all strings in that list, and add "cc" to the front of each string.
(2b) Similarly, find all strings of length N-3 with a recursive call; go through all the strings in that list, and add "ddd" to the front.
(2c) The resulting list will be all the strings from steps (2a) and (2b).
(3) You need base cases. If N is 0 or 1, the resulting list will be empty. If N==2, it will have just one string, "cc"; if N==3, it will have just one string, "ddd".
You can use a Set instead of a list if you want, since the order won't matter.
Note that it's a bad idea to use a global list or set to hold the results. When a method is calling itself recursively, and every invocation of the method touches the same list or set, you will go insane trying to get everything to work. It's much easier if you let each recursive invocation hold its own local list with the results. Edit: This needs to be clarified. Using a global (i.e. instance field that is shared by all recursive invocations) collection to hold the final results is OK. But the approach I've outlined above involves a lot of intermediate results--i.e. if you want to find all strings whose length is 8, you will also be finding strings whose length is 6, 5, 4, ...; using a global to hold all of those would be painful.
The answer to why set is returned empty is simply follow the logic. Say you execute generateset(5); which will execute strings(5,5,"");:
First iteration strings(5,5,""); : (s.length() == size) is false hence nothing added to set
Second iteration strings(2,5,""); : (n == 2) is true, hence nothing added to set
Third iteration strings(3,5,""); : (n == 3) is true, hence nothing added
to set
So set remains un changed.
I had this snippet in my code to check if every value of an array was contained in another array (basically for filtering).
This is the snippet
public boolean linearIn(int[] subarray, int[] array) {
for (int i = 0; i < subarray.length; i++) {
if (!Arrays.asList(array).contains(subarray[i])) {
Log.d(TAG, "Array doesn't contain " + subarray[i]);
return false;
}
}
return true;
}
This used to work. I remember because it worked in an app I am working on. What do I mean by it worked? Well actually it could understand when there were or there weren't matches, indeed a filter was applied.
Now it turns out that it just never works. With the Log it outputs that doesn't find 0 even when I know that array contains a 0.
But luckily Android Studio isn't silent about this. This is the warning that gives at the if-statement:
List<int[]>' may not contain objects of type 'Integer' more... (CTRL+F1)
At the time I was inspired by the 3rd answer in this question.
And I was satisfied since it worked on my code, and it's pretty easy to say this since now it couldn't even find one match.
Now I resolved using Integer types (a bit unconfortable since it requires a loop for casting if I must have an int[])
Is it possible that SDK changed in the updates in a way that broke this code snipped?
Arrays.asList(array)
creates a
List<int[]>
not a
List<Integer>.
I made Sudoku checker/solver with ease, but I need one that can tell wheter there is more than one solution, and couldn't wrap my head around it. I found a working algorithm, but I'm trying to understand why it's working. It's the answer from this question, provided by #fabian
Copied below:
// returns 0, 1 or more than 1 depending on whether 0, 1 or more than 1 solutions are found
static byte solve(int i, int j, int[][] cells, byte count /*initailly called with 0*/) {
if (i == 9) {
i = 0;
if (++j == 9)
return 1+count;
}
if (cells[i][j] != 0) // skip filled cells
return solve(i+1,j,cells, count);
// search for 2 solutions instead of 1
// break, if 2 solutions are found
for (int val = 1; val <= 9 && count < 2; ++val) {
if (legal(i,j,val,cells)) {
cells[i][j] = val;
// add additional solutions
count = solve(i+1,j,cells, count));
}
}
cells[i][j] = 0; // reset on backtrack
return count;
}
I tried implementing it, and as it should, it works. However though I think I understand what each part of the code does, I cannot get why it works.
First: The first if statement stops the method once the final number in the 2d array is reached. I get this in finding a single solution, but why does it work in finding more than one solution? Shouldn't the method just return 0+1=1 after solution is found?
Second: after if (cells[i][j] != 0) why does the recursive solve(...) call need return statement in front of it? I have made several recursive algorithms, but always by just calling the method again.
Third: If none suitable numbers are found the for loop stops and 0 is inputted to the cell place. Since it should already have 0, shouldn't the backtracking put 0 to the last place instead of current? At least that is how I made the solver that I made myself.
Fourth: After the backtrack set, there is just return count. Why is the program still working? Shouldn't it just return count = 0 and stop after facing first place that doesn't allow any numbers? Howcome there isn't a recursive call at the end?
If you made it this far on this rampling question, it is clear that I'm understanding some things completely wrong. I'd highly appreciate assistance/explanation, since using code one doesn't understand is a complete failure as far as learning to code goes.
Ok, so Google gracefully provided an Powerpoint lecture from Harvard:
http://www.fas.harvard.edu/~cscie119/lectures/recursion.pdf
If someone else is having problems getting recursive backtracking, I recommend checking it out. Very short but informative.
My problem seemed to be only that I stupidly (at least on hindsight) assumed for the method to stop after it calls itself recursively. I forgot that after it gets results from the recursive call it makes, it executes itself to the end. Funny how you can use umphteen hours solving something just because your initial thought process was flawed. Well, live and learn I guess.
Objects First with Java
A Practical Introduction using BlueJ
Working threw this book and I do not understand what this exercise is asking me to do.
The exercise is...
Exercise 4.51 Rewrite getLot so that it does not rely on a lot with a particular number being stored at index (number–1) in the collection. For instance, if lot number 2 has been removed, then lot number 3 will have been moved from index 2 to index 1, and all higher lot numbers will also have been moved by one index position. You may assume that lots are al- ways stored in increasing order according to their lot numbers.
/**
* Return the lot with the given number. Return null if a lot with this
* number does not exist.
*
* #param lotNumber The number of the lot to return.
*/
public Lot getLot(int lotNumber) {
if ((lotNumber >= 1) && (lotNumber < nextLotNumber)) {
// The number seems to be reasonable.
Lot selectedLot = lots.get(lotNumber - 1);
// Include a confidence check to be sure we have the
// right lot.
if (selectedLot.getNumber() != lotNumber) {
System.out.println("Internal error: Lot number "
+ selectedLot.getNumber()
+ " was returned instead of "
+ lotNumber);
// Don't return an invalid lot.
selectedLot = null;
}
return selectedLot;
} else {
System.out.println("Lot number: " + lotNumber
+ " does not exist.");
return null;
}
}
A hint in the right direction with pesudo code would be fine.
I am really confused in what the exercise is asking me to do.
I will be upfront about this, this is for a class and the teacher is really just handing us the book, with very little guidance. So I am not looking for someone to write my homework, I just want some help. Please don't flame me because I am asking. This is a place to ask questions about coding? No? Thanks in advance.
The algorithm of the given method relies on lot lotNumber being stored in index lotNumber-1. It just looks it up by index and verifies it has found the correct one.
The exercise is to give up this assumption. Lot number and index are no longer this closely related. So you cannot just calculate the index, you have to search for the lot.
The simplest possible approach is to look at each lot in your collection and return it once you found a matching lot number. You can use an iterator, explicitly or implicitly ("foreach"), for this. If your course hasn't covered iterators yet, you can also use a for loop to count through all existing indexes of your collection.
But the exercise specifies that the lots are still stored in order. This allows you to modify the simple approach to give up once you found a lot number higher than the one you're looking for.
The optimal approach would be using a search algorithm for sorted lists, such as binary search.
In the code you provided, there is a big assumption: the assumption that lot number i is stored in the array at the position i-1. Now what if we don't assume that? Well we have no idea where lot i might be in the array, so the only solution is to go through the array and look for lot number i and hopefully we'll found it.
I'm trying to solve a problem that calls for recursive backtracking and my solution produces a stackoverflow error. I understand that this error often indicates a bad termination condition, but my ternimation condition appears correct. Is there anything other than a bad termination condition that would be likely to cause a stackoverflow error? How can I figure out what the problem is?
EDIT: sorry tried to post the code but its too ugly..
As #irreputable says, even if your code has a correct termination condition, it could be that the problem is simply too big for the stack (so that the stack is exhausted before the condition is reached). There is also a third possibility: that your recursion has entered into a loop. For example, in a depth-first search through a graph, if you forget to mark nodes as visited, you'll end up going in circles, revisiting nodes that you have already seen.
How can you determine which of these three situations you are in? Try to make a way to describe the "location" of each recursive call (this will typically involve the function parameters). For instance, if you are writing a graph algorithm where a function calls itself on neighbouring nodes, then the node name or node index is a good description of where the recursive function is. In the top of the recursive function, you can print the description, and then you'll see what the function does, and perhaps you can tell whether it does the right thing or not, or whether it goes in circles. You can also store the descriptions in a HashMap in order to detect whether you have entered a circle.
Instead of using recursion, you could always have a loop which uses a stack. E.g. instead of (pseudo-code):
function sum(n){
if n == 0, return 0
return n + sum(n-1)
}
Use:
function sum(n){
Stack stack
while(n > 0){
stack.push(n)
n--
}
localSum = 0
while(stack not empty){
localSum += stack.pop()
}
return localSum
}
In a nutshell, simulate recursion by saving the state in a local stack.
You can use the -Xss option to give your stack more memory if your problem is too large to fix in the default stack limit size.
As the other fellas already mentioned, there might be few reasons for that:
Your code has problem by nature or in the logic of the recursion. It has to be a stoping condition, base case or termination point for any recursive function.
Your memory is too small to keep the number of recursive calls into the stack. Big Fibonacci numbers might be good example here. Just FYI Fibonacci is as follows (sometimes starts at zero):
1,1,2,3,5,8,13,...
Fn = Fn-1 + Fn-2
F0 = 1, F1 = 1, n>=2
If your code is correct, then the stack is simply too small for your problem. We don't have real Turing machines.
There are two common coding errors that could cause your program to get into an infinite loop (and therefore cause a stack overflow):
Bad termination condition
Bad recursion call
Example:
public static int factorial( int n ){
if( n < n ) // Bad termination condition
return 1;
else
return n*factorial(n+1); // Bad recursion call
}
Otherwise, your program could just be functioning properly and the stack is too small.