Binary Search using Recursion in java - java

I am writing a program for a recursive binary search. I have an input file with a series of sorted numbers, which I added to an ArrayList. The program searches to see if a key given by user input is in the ArrayList.
public static int binarySearch(int key, int median){
if(key == (int)array.get(median)){
return key;
}else if(key < (int)array.get(median)){
binarySearch(key,median-1);
}else if(key > (int)array.get(median)){
binarySearch(key,median+1);
}
return -1;
}
For example, let's say the key is 90. I debugged and placed watches at key and array.get(median). After stepping through the program, I discovered that even when key and array.get(median) are equal to 90, the program continues through the loop without ever returning the key. I know that recursion isn't ideal for this, but it's what I need to use.

This does not look like a correct binary search, as a binary search uses a divide and conquer approach. You only divide your list initially once and then check every element from there on. It would be better to then just divide your list again and so on, see https://en.wikipedia.org/wiki/Binary_search_algorithm
Anyway, to get your code running, why you do not get an result is most likely because you do not return the result of the recursion, but instead -1.
Remove the return -1 and set a return before your recursive binarySearch calls. And you are missing a exit condition when you do not find the element.
Example (still not a correct binary search):
public static int binarySearch(int key, int median){
if (median < 0 || median > array.size() - 1) { // element not found
return -1;
}
if (key == (int)array.get(median)){
return key;
} else if(key < (int)array.get(median)){
return binarySearch(key,median-1);
} else{
return binarySearch(key,median+1);
}
}

If all you care about is whether it is in the data structure...
public static boolean binarySearch(int key, int median){
if(key == (int)array.get(median)){
return true;
}else if(key < (int)array.get(median)){
return binarySearch(key,median-1);
}else if(key > (int)array.get(median)){
return binarySearch(key,median+1);
}
}
your code could be better written, but copied to to get across the important part of it

You should change your code like this:
public static int binarySearch(int key, int median){
if(key == (int)array.get(median)){
return key;
}else if(key < (int)array.get(median)){
return binarySearch(key,median-1);
}else if(key > (int)array.get(median)){
return binarySearch(key,median+1);
}
return -1;
}
If you do this your recursion would end but I will leave it to you to test out your binary search code. There are some things like the start index and the end index that you are ignoring in this method The return statements should be added because if you don't the control moves to the next statement in the method which is undesirable. Hope that helps!

The code posted is not a Binary Search and is, in fact, a Linear Search using recursion. A Binary Search divides the search space in half each step, binary meaning "two halves" in context. The result is that this Linear Search runs in O(n) and not the expected O(lg n) bounds of a Binary Search
Problems/issues (other than it not being a Binary Search):
The values from the recursive cases are lost as they are not themselves returned. The result of a recursive case must be used in some way (or why call the function at all?). In this case the value should be returned directly, eg. return binarySearch(..). This causes "continues through the loop without ever returning the key" - it actually does find the key, but discards the recursive result.
The code does not correctly detect the out-of-bounds terminal condition. This may result in an IndexOutOfBoundsException when the key is not found.
The step-left/right approach might never terminate correctly without further logic. For example, when the list is {1,3,5,7} and the value being sought is 4 the original code will ping-pong between indices 1 (value 3) and 2 (value 4). This will result in a StackOverflowError being thrown.
The key is returned. This makes little sense as the key is already known and it also prevents -1 from being detectable. Return the index found instead, or a boolean if only an existence test is required.
Take some time to understand and fix these issues .. and then read on for a spoiler if needed.
Consider this rewrite fixing the issues outlined above. Note that it is overly complex1 while still employing an inferior algorithm and O(n) performance. In any case;
// By taking in the List we make this function universal and not
// dependent upon a static field. It should probably take in a List<Integer>
// but I don't know what the actual type of "array" is. A more advanced
// implementation would take in List<Comparable> and then be modified to work
// with any objects that correctly implement said interface.
public static int linearSearch(int key, List list, int index, int step) {
if (index < 0 || index >= list.size()) {
// Base case: not found, out of bounds
return -1;
}
int x = (int)list.get(index);
if (key < x && step <= 0) { // need to look left, NOT looking right
// Recursive case: look left, returning result
return linearSearch(key, list, index - 1, -1);
} else if (key > x && step >= 0){ // need to look right, NOT looking left
// Recursive case: look right, returning result
return linearSearch(key, list, index + 1, +1);
} else if (key == x) {
// Base case: found key, return index found
return index;
} else {
// Base case: key not equal, refusing to ping-pong
return -1;
}
}
And then consider the use of this helper/wrapper function;
// Returns the index in "array" where key was found, or -1 if it was not found
public static int linearSearch(int key) {
// Have to start somewhere, might as well be the middle..
// ..but it does NOT make the time complexity any better
// ..and it is still NOT a Binary Search.
return linearSearch(key, array, array.size() / 2, 0);
}
1 Alternatively, since it is a Linear Search it could be rewritten without the extra left/right movement and have the same complexity bounds. (It is also trivial to modify this simpler code to a recursive Binary Search.)
public static int linearSearch(int key, List list, int index) {
if (index >= list.size() {
// Base case: end of list
return -1;
}
int x = (int)list.get(index);
if (key < x) {
// Recursive case: not there yet, keep looking
return linearSearch(key, list, index + 1);
} else if (key == x) {
// Base case: found key, return index
return index;
} else { // -> key > x
// Base case: read past where the key would be found
return -1;
}
}

Here's binary search for ArrayList<Integer> :)
public int binarySearch(List<Integer> list, int find) {
int mid = list.size() / 2;
int val = list.get(mid);
if (val == find) {
return val;
} else if (list.size() == 1) {
return -1;
}
return val > find ?
binarySearch(list.subList(0, mid), find) :
binarySearch(list.subList(mid, list.size()), find);
}

Related

Transposition Tables and Alpha-Beta Pruning

I got the following problem. I have implemented transposition tables, which seem to work well or at least I can not see they are not working.
Additionally, I wanted to implement some move-ordering.
Basically, I search the move first that's saved in my transposition table. For some reason, that doesn't improve the search at all. Either I haven't implemented move-ordering properly or my implementation of the alpha-beta algorithm along with transposition tables is just wrong.
To measure the performance I counted the number of nodes visited during search. My implementation of move-ordering didn't have any effect at all.
Here is the Java-Code I have come up with:
public int alphabeta(final CheckerBoard board,int alpha,int beta,int depth,boolean nullMoveAvailable){
nodeCount++;
int alphaOrig=alpha;
Transposition entry =table.find(board.key);
if(entry!=null && entry.depth>=depth){
if(entry.flag==Transposition.EXACT){
return entry.value;
}else if(entry.flag==Transposition.UPPER){
beta = Math.min(beta,entry.value);
}else if(entry.flag == Transposition.LOWER){
alpha =Math.max(alpha,entry.value);
}
if(alpha>=beta){
return entry.value;
}
}
if(depth==0 || board.isTerminalState()){
return quiesceneSearch2(board,alpha,beta);
}
ArrayList<Move>sucessors =MGenerator.getMoves(board);
Move currentBest =null;
for( int i=0;i<sucessors.size();i++){
if(entry!=null && entry.depth<depth && (entry.flag == Transposition.UPPER || entry.flag == Transposition.EXACT) && sucessors.get(i).equals(entry.best)){
Collections.swap(sucessors,0,i);
break;
}
}
int bestValue =Integer.MIN_VALUE;
for(Move move : sucessors){
board.makeMove(move);
int value =-alphabeta(board, -beta, -alpha, depth - 1, true);
board.undoMove(move);
if(value>bestValue){
bestValue =value;
currentBest = move;
}
alpha =Math.max(alpha,value);
if(alpha>=beta){
break;
}
}
Transposition next =new Transposition();
next.depth=depth;
next.value =bestValue;
next.zobrisKey=board.key;
next.best = currentBest;
if(bestValue<=alphaOrig){
next.flag =Transposition.UPPER;
}else if(bestValue>=beta){
next.flag = Transposition.LOWER ;
}else{
next.flag = Transposition.EXACT;
}
table.insert(next);
return alpha;
}
and the following code to start the search:
public int findBestMove(int depth){
if (table != null) {
table.clear();
}
ArrayList<Move> sucessors =MGenerator.getMoves(board);
int max = Integer.MIN_VALUE;
for(Move m : sucessors){
board.makeMove(m);
int value =-alphabeta(board, -1000000, 1000000, depth, true);
board.undoMove(m);
if(value>max){
max=value;
bestMove=m;
}
}
board.makeMove(bestMove);
return max;
}
Would appreciate it if someone looked at my code. Maybe there isn't anything wrong with this part of my code but I didn't want to post everything since it's already quite a lot of code to look at.
I see a couple of things "off" compared to this reference site for alphabeta: https://web.archive.org/web/20071031100051/http://www.brucemo.com/compchess/programming/hashing.htm
1) You're not storing any transposition entries for depth 0; and
2) You're storing a transposition value of alpha instead of beta when alpha>=beta.
Don't know if that would make the difference, but...
And, oh, 3) You're also returning alpha instead of beta from the function when alpha>=beta.

Recursive binary search and return index of object

I'm having difficulty figuring out how I can return the index of an item in an array via the recursive binarySearch function I have below:
public static <AnyType extends Comparable<AnyType>> int binarySearch
(AnyType[] a, AnyType x)
{
int nMin = 0;
int nMax = a.length - 1;
int nMid = (nMin + nMax) / 2;
int compare = a[nMid].compareTo(x);
if(compare == 0)
return 1;
else if(compare == 1)
return binarySearch(Arrays.copyOfRange(a, 0, a.length/2), x);
else
return binarySearch(Arrays.copyOfRange(a, a.length/2, nMax), x);
}
The requirement is to write a BS function with the function header and not to modify it. My function works fine, however, as I modify the array through each level of recursion, I lose the original index. We are also required to return the index of the item we're searching for. My implementation will always return 1.
Is it even possible to do this without creating another method and calling it from within the binarySearch function?
So after much confusion from pretty much the entire 300 student class, the teacher addressed this during lecture and explained that we could, in fact, create another function to handle anything we needed (position in this case).
So what I did was just write a normal recursive binary search implementation and call that from the function the professor required to have included.
public static <AnyType extends Comparable<AnyType>> int binarySearch
(AnyType[] a, AnyType x)
{
return binarySearch(a, x, 0, a.length);
}
public static <AnyType extends Comparable<AnyType>> int binarySearch
(AnyType[] a, AnyType x, int nMin, int nMax)
{
if(nMin < nMax)
{
int nMid = nMin + (nMax - nMin) / 2;
if(x.comapreTo(a[nMid]) == -1)
return binarySearch(a, x, nMin, nMid);
else if(x.compareTo(a[nMid]) == 1)
return binarySearch(a, x, nMid+1, nMin);
else
return nMid;
}
rteurn -(nMin + 1);
}
Also, as a side note, I think my original question was poorly worded. I wasn't looking for a solution to the problem, I was more or less looking for confirmation in my thinking that the problem was impossible.
Recursive search by copying the array on each recursion is bad for performance.
Pass the entire array on recursion, with additional range values, i.e. start/end indexes to process.
This is faster, and will also allow you to immediately know the actual index of the found element, since index values are unchanged.
Update
If the method signature cannot be changed, then a few changes are needed:
int compare = a[nMid].compareTo(x);
if(compare == 0)
return 1;
If the element at index nMid is equal, that is the index to return, not 1.
else if(compare == 1)
return binarySearch(Arrays.copyOfRange(a, 0, a.length/2), x);
compareTo() returns a "positive integer", not necessarily 1, so test should be compare > 0.
Also, there is no need for the else part, since the if has unconditional return.
else
return binarySearch(Arrays.copyOfRange(a, a.length/2, nMax), x);
First, the last argument of copyOfRange() is exclusive, so it needs to be a.length, not nMax.
Since the recursive call is passing in a range not starting at 0, any index value returned must be offset, i.e. return binarySearch(...) + a.length/2.
Again, no need for else.
In addition to all the above, if the value doesn't exist, the code will eventually fail because copyOfRange() will create an empty array, and code doesn't handle that, causing a[nMid] with a value of 0 to throw an IndexOutOfBoundsException. Need to add code to handle that.

Wrap Around Grid - errors on east/west only

I have four methods that check whether or not a given grid location is next to an occupied location (value of 1). The grid is assumed to wrap around, ie, if in a 50x50 grid[0][1] is the given location and grid[49][1] is occupied, the method should return true/ My checkNorth and checkEast method are working fine, but I get an ArrayIndexOutofBoundsException: -1 error for either the south or west methods every time I run the program. I checked my math and I think it should work - am I using the modulo incorrectly, or am I missing something else?
EDIT: Clarified the wrapping criterion, word use correction.
boolean checkWest(int indexA, int indexB)
{
if (indexA-1 > 0)
{
if (grid[indexA-1][indexB] == 1)
{
return true;
}
}
if (indexA-1 < 0)
{
if (grid[(indexA-1)%width][indexB] == 1)
{return true;}
else return false;
}
return false;
}
I see a couple problems. First, Java arrays are zero-indexed, which means that the first element is at index 0. So it's okay to check grid[indexA-1][indexB] when indexA-1 is equal to 0. Second, you're not properly handling when indexA equals 0. Here is my implementation. I also simplified the logic a bit.
boolean checkWest(int indexA, int indexB)
{
if (indexA > 0)
return grid[indexA - 1][indexB] == 1;
else
return grid[width + indexA - 2][indexB] == 1;
}
EDIT: I'm pretty sure I butchered the math with the second return statement. It should be right now...

Negamax chess algorithm: How to use final return?

I've made a negamax algorithm for a chess-like game and I want to know how to use the final board value result. I understand the final return of the negamax algorithm represents what the board value will be after the player takes his best possible move, but that isn't exactly useful information. I need to know what that move is, not what it's worth.
Here's the code:
public int negamax(Match match, int depth, int alpha, int beta, int color) {
if(depth == 0) {
return color*stateScore(match);
}
ArrayList<Match> matches = getChildren(match, color);
if(matches.size() == 0) {
return color*stateScore(match);
}
int bestValue = Integer.MIN_VALUE;
for(int i = 0; i != matches.size(); i++) {
int value = -negamax(matches.get(i), depth-1, -beta, -alpha, -color);
if(value > bestValue) {
bestValue = value;
}
if(value > alpha) {
alpha = value;
}
if(alpha >= beta) {
break;
}
}
return bestValue;
}
public void getBestMove(Match match, int color) {
int bestValue = negamax(match, 4, Integer.MIN_VALUE, Integer.MAX_VALUE, color);
// What to do with bestValue???
}
I thought of re-evaluating the children of the current match state after bestValue is determined. Then I iterate through them and find which of those children has a stateScore equal to bestValue. But that wouldn't work because a lot of them will have the same stateScore anyway, it's what they can lead to which counts...
I can see you're doing a qsearch and alpha-beta. Your algorithm is well-known but you're missing a key part.
Let me sketch out the basic algorithm for chess search, it applies even to Stockfish (the strongest engine in the world).
search(Position p) {
if (leaf node)
qsearch(p)
if (need to do move reduction)
do_move_reduction_and_cut_off(p)
moves = generate_moves(p)
for_each(move in moves) {
p.move(move)
v = -search(p, -beta, -alpha)
p.undo(move)
store the score and move into a hash table
if (v > beta)
cutoff break;
}
This is just a very brief sketch, but all chess algorithms follow it. Compare your version with it, do you notice that you haven't done p.move(move) and p.undo(move)?
Basically, the traditional approach generates a list of moves for a given position. Loop through the moves, play it and undo it and search it. If you do it, you know exactly which move produces which score.
Also notice the line for storing the move and score into a hash table. If you do this, you can easily reconstruct the entire principal variation from a root node.
I don't know what exactly is inside your Java class Match, but in any case your attempt was close but no exactly the classical way to do a search. Remember you'll need to give a position object in a search algorithm but instead you gave it a Match object, which is wrong.

Recursively counting specific nodes in a binary search tree

I'm trying to create a method in Java to count Nodes in a Binary Search Tree that contain a certain value and print out their contents. Each Node contains an Article object which has a title, and I want to find and print Articles in the tree containing a keyword in the title. However, I don't want the program to print out more than 10 of these (a 1-letter keyword could cause the program to stall or crash, for example). The code is here:
public int traverse(String key) {
if (root == null) {
System.out.println("Empty Tree!");
return 0;
} else {
int n = traverseHelper(root, key, 0);
return n;
}
}
public int traverseHelper(Node t, String key, int n) {
if (t == null) {
return n;
} else {
if (t.data.getTitle().indexOf(key) >= 0 && n <= 10) {
System.out.println(t.data);
n++;
}
return traverseHelper(t.left, key, n) + traverseHelper(t.right, key, n);
}
}
I'm trying to keep a running count of how many times the program has printed the data, but I'm not entirely sure how. Currently the program prints all occurrences, or very close to it. I know something is wrong with my recursive approach (I'm never any good at recursion anyway), so a good explanation of what I'm doing wrong would be greatly appreciated. This is homework, though, so I don't expect an explicit solution.
A couple other things: the traverse function's purpose is to print out the Articles, so I will most likely change it to a void method later. It currently should return the final count of articles printed. Also, the tree is set up like any other BST, but I'll give any clarification of my code if necessary.
Thanks!
Since you count with n you can only pass n to one of the leafs or you can nest them like this:
public int traverseHelper(Node t, String key, int n) {
if (t == null) {
return n;
} else {
if (t.data.getTitle().indexOf(key) >= 0 && n <= 10) {
System.out.println(t.data);
n++;
}
return traverseHelper(t.left, key, traverseHelper(t.right, key, n));
}
}
Nesting like this reduces stack usage slightly since you don't have the extra adding and the left becomes a tail call. Java don't have tail call optimization but it doesn't hurt to write as if it had.
The issue I see is with the n variable.
You do
return traverseHelper(t.left, key, n) + traverseHelper(t.right, key, n);
So if n was 5 to begin with, your method would return 10 (5 from each branch) even if no items were found.
Make the function return only the number of items found in the subtree.

Categories