I'm working on an assignment.
What I must create is a method that searches for an specific int in an array. It is assumed that the array is already sorted from lowest to highest number. But the condition is that it must search by cutting said array by half and checking in which of the halves the target number is in, then cut said half in half again and so on so on.
We were asked not to use recursive methods as well.
This is the code I came up with but I fail to see my mistake, any help to understand the problem better is more than appreciated!!!
public static boolean SearchInHalf(int[] array, int targetint)
{
int fin = array[array.length-1];
int init = array[0];
int range = fin-init;
if ( targetint>array[array.length-1] || targetint< array[0])
{
return false;
}
while (range>=2)
{
int half = array[range/2];
if (targetint>half)
{
init = half;
range = fin-init;
half = array[range/2];
}
else if (targetint<half)
{
fin = half;
range = fin-init;
half = array[range/2];
}
else if (targetint==half || targetint==fin || targetint==init)
{
return true;
}
}
return false;
}
Your problem is known as the "Binary Search". For binary search to work, the elements in the array must already be ordered (which is your case, let's assume ascending). The binary search first compares the key with the element in the middle of the array:
If the key is less than the middle element, you need to continue to search for the key only in the first half of the array.
If the key is greater than the middle element, you need to continue to search for the key only in the second half of the array.
If the key is equal to the middle element, the search ends with a match.
So binary search method eliminates at least half of the array after each comparison. Assuming you will call this method in your static main function:
public static int binarySearch(int[] list, int key) {
int low = 0;
int high = list.length - 1;
while(high >= low) { //search array until there is a single element left
int mid = (low + high) / 2; //mark middle index
if (key < list[mid]) //if key is smaller than the middle element..
high = mid - 1; //new high is the middle element
else if (key == list[mid]) //if key=middle element --> voila!
return mid; //returns the index of searched element if it is in your array
else
low = mid + 1; //if key is greater than the middle element new low is middle element
}
return –low - 1; //high < low, key not found
}
Solved it like this:
while (true) {
if (targetint>array[array.length-1] || targetint<array[0])
return false;
int middleInt = array[Math.round(array.length/2)];
if (middleInt == targetint) {
return true;
} else if (targetint<middleInt) {
int[] firstHalf = new int[array.length/2];
System.arraycopy(array, 0, firstHalf, 0, firstHalf.length);
array = firstHalf;
} else if (targetint>middleInt) {
int[] secondHalf = new int[array.length/2];
System.arraycopy(array, array.length/2, secondHalf, 0, secondHalf.length);
array = secondHalf;
} else if(array.length == 1)
return false;
}
I have the array {1,2,3,4,4,4,5}
I want my function return index of 4.
for example : 4 found at location 4,5,6
public void binarySearch(int value){
sort(); // sorting the array
int index=-1;
int lower=0;
int upper=count-1;
while(lower<=upper){
int middle=(lower+upper)/2;
if(value==array[middle]){
index=middle;
System.out.println(value+ " found at location "+(index+1));
break;
}
else if(value<array[middle]){
upper=middle-1;
}
else lower=middle+1;
}
}
It's not too hard. We know that because the list is sorted, all of our indexes are going to be contiguous (next to one another). So once we've found one, we just have to traverse the list in both directions to find out what other indexes also match.
public static void binarySearch(int value){
sort();
int index = -1;
int lower = 0;
int upper = array.length - 1;
while(lower <= upper){
// The same as your code
}
// Create a list of indexes
final List<Integer> indexes = new LinkedList<>();
// Add the one we already found
indexes.add(index);
// Iterate upwards until we hit the end or a different value
int current = index + 1;
while (current < array.length && array[current] == value)
{
indexes.add(current);
current++;
}
// Iterate downwards until we hit the start or a different value
current = index - 1;
while (current >= 0 && array[current] == value)
{
indexes.add(current);
current--;
}
// Sort the indexes (do we care?)
Collections.sort(indexes);
for (int idx : indexes)
{
System.out.println(value + " found at " + (idx + 1));
}
}
Bear in mind that what you have implemented is already a binary search. The extra code to find additional matching indexes would not fall under the usual definition of a binary search.
In my problem I have few arrays with numbers 1 - 3,
[1,2,3], [1,2,3]
I combined the arrays into one full array,
[1,2,3, 1,2,3]
I need to randomize the array each run, so that no element repeats.
For example, this would work
[1, 2, 1, 3, 2, 3]
but this would not.
[1,2,2,3,1,3]
I chose 1,2,3 to simplify it, but my arrays would consist of the numbers 1 - 6. The idea remains the same though. Is there an algorithm or easy method to accomplish this?
This is a heuristic solution for random shuffling not allowing consecutive duplicates. It applies to lists, but it's easy to transfer it to arrays as it does only swapping and no shift operations are required. It seems to work in the majority of cases for lists consisting of millions of elements and various density factors, but always keep in mind that heuristic algorithms may never find a solution. It uses logic from genetic algorithms, with the exception that this version utilizes one individual and selective mutation only (it's easy to convert it to a real genetic algorithm though), but it's simple and works as follows:
If a duplicate is found, try swapping it with a random element after it; if not possible, try swapping it with an element prior to it (or vice versa). The key point here is the random position for exchanging elements, so as to keep a better uniform distribution on random output.
This question has been asked in alternative forms, but I couldn't find an acceptable solution yet. Unfortunately, as most of the proposed answers (except for the "greedy" extensive re-shuffling till we get a match or computing every combination), this solution does not provide a perfect uniform distribution, but seems to minimize some patterns, :( still not possible to remove every pattern, as you see below. Try it and post any comments for potential improvements.
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Random;
//Heuristic Non-Consecutive Duplicate (NCD) Shuffler
public class NCDShuffler {
private static Random random = new Random();
//private static int swaps = 0;
public static <T> void shuffle (List<T> list) {
if (list == null || list.size() <= 1) return;
int MAX_RETRIES = 10; //it's heuristic
boolean found;
int retries = 1;
do {
Collections.shuffle(list);
found = true;
for (int i = 0; i < list.size() - 1; i++) {
T cur = list.get(i);
T next = list.get(i + 1);
if (cur.equals(next)) {
//choose between front and back with some probability based on the size of sublists
int r = random.nextInt(list.size());
if ( i < r) {
if (!swapFront(i + 1, next, list, true)) {
found = false;
break;
}
} else {
if (!swapBack(i + 1, next, list, true)) {
found = false;
break;
}
}
}
}
retries++;
} while (retries <= MAX_RETRIES && !found);
}
//try to swap it with an element in a random position after it
private static <T> boolean swapFront(int index, T t, List<T> list, boolean first) {
if (index == list.size() - 1) return first ? swapBack(index, t, list, false) : false;
int n = list.size() - index - 1;
int r = random.nextInt(n) + index + 1;
int counter = 0;
while (counter < n) {
T t2 = list.get(r);
if (!t.equals(t2)) {
Collections.swap(list, index, r);
//swaps++;
return true;
}
r++;
if (r == list.size()) r = index + 1;
counter++;
}
//can't move it front, try back
return first ? swapBack(index, t, list, false) : false;
}
//try to swap it with an element in a random "previous" position
private static <T> boolean swapBack(int index, T t, List<T> list, boolean first) {
if (index <= 1) return first ? swapFront(index, t, list, false) : false;
int n = index - 1;
int r = random.nextInt(n);
int counter = 0;
while (counter < n) {
T t2 = list.get(r);
if (!t.equals(t2) && !hasEqualNeighbours(r, t, list)) {
Collections.swap(list, index, r);
//swaps++;
return true;
}
r++;
if (r == index) r = 0;
counter++;
}
return first ? swapFront(index, t, list, false) : false;
}
//check if an element t can fit in position i
public static <T> boolean hasEqualNeighbours(int i, T t, List<T> list) {
if (list.size() == 1)
return false;
else if (i == 0) {
if (t.equals(list.get(i + 1)))
return true;
return false;
} else {
if (t.equals(list.get(i - 1)) || (t.equals(list.get(i + 1))))
return true;
return false;
}
}
//check if shuffled with no consecutive duplicates
public static <T> boolean isShuffledOK(List<T> list) {
for (int i = 1; i < list.size(); i++) {
if (list.get(i).equals(list.get(i - 1)))
return false;
}
return true;
}
//count consecutive duplicates, the smaller the better; We need ZERO
public static <T> int getFitness(List<T> list) {
int sum = 0;
for (int i = 1; i < list.size(); i++) {
if (list.get(i).equals(list.get(i - 1)))
sum++;
}
return sum;
}
//let's test it
public static void main (String args[]) {
HashMap<Integer, Integer> freq = new HashMap<Integer, Integer>();
//initialise a list
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(1);
list.add(2);
list.add(3);
/*for (int i = 0; i<100000; i++) {
list.add(random.nextInt(10));
}*/
//Try to put each output in the frequency Map
//then check if it's a uniform distribution
Integer hash;
for (int i = 0; i < 10000; i++) {
//shuffle it
shuffle(list);
hash = hash(list);
if (freq.containsKey(hash)) {
freq.put(hash, freq.get(hash) + 1);
} else {
freq.put(hash, 1);
}
}
System.out.println("Unique Outputs: " + freq.size());
System.out.println("EntrySet: " + freq.entrySet());
//System.out.println("Swaps: " + swaps);
//for the last shuffle
System.out.println("Shuffled OK: " + isShuffledOK(list));
System.out.println("Consecutive Duplicates: " + getFitness(list));
}
//test hash
public static int hash (List<Integer> list) {
int h = 0;
for (int i = 0; (i < list.size() && i < 9); i++) {
h += list.get(i) * (int)Math.pow(10, i); //it's reversed, but OK
}
return h;
}
}
This is a sample output; it's easy to understand the issue with the non-uniform distribution.
Unique Outputs: 6
EntrySet: [1312=1867, 3121=1753, 2131=1877, 1321=1365, 1213=1793, 1231=1345]
Shuffled OK: true
Consecutive Duplicates: 0
You could use Collections.shuffle to randomize the list. Do it in a while loop, until the list passes your constraint.
If the arrays are relatively small, it would not be too hard for you just to combine the two arrays, randomize it then check the numbers, and if there are too same numbers just shift one over or just randomize it again.
There's no pre-written algorithm that I know of (which doesn't mean one doesn't exist), but the problem is easy to understand and the implementation is straightforward.
I will offer two suggestions dependent on if you want to build a valid array or if you want to build an array and then check its validity.
1 - Create some collection (Array, ArrayList, etc) that contains all of the possible values that will be included in your final array. Grab one of those values and add it to the array. Store a copy of that value in a variable. Grab another value from the possible values, check that it's not equal to your previous value, and add it to the array if it's valid.
2 - Create an array that contains the number of values you want. Check that item n != item n+1 for all items except the last one. If you fail one of those checks, either generate a new random value for that location or add or subtract some constant from the value at that location. Once you have checked all of the values in this array, you know you have a valid array. Assuming the first and last values can be the same.
The most optimal solution, I can think of, is to count the number of occurrences of each value, logically creating a "pool" for each distinct value.
You then randomly choose a value from any of the pools that are not the value of the previous selection. The random selection is weighted by pool sizes.
If a pool is more than half the size of all remaining values, then you must choose from that pool, in order to prevent repetition at the end.
This way you can produce result fast without any form of retry or backtracking.
Example (using letters as values to clarify difference from counts):
Input: A, B, C, A, B, C
Action Selected Pools(Count)
A(2) B(2) C(2)
Random from all 3 pools A A(1) B(2) C(2)
Random from B+C pools C A(1) B(2) C(1)
Random from A+B pools (1:2 ratio) A A(0) B(2) C(1)
Must choose B (>half) B A(0) B(1) C(1)
Random from A+C, so C C A(0) B(1) C(0)
Must choose B (>half) B A(0) B(0) C(0)
Result: A, C, A, B, C, B
I need to find all elements in sorted array with Arrays.binarySearch method. I want to iterate binary search in lowerbound = pos + 1 (pos is previous match), but binarySearch is not guaranteed to return first match (min match index)?
How can I make this?
You can easily use the result of binarySearch to get all the matches :
long[] sortedArr = ...
int index = Arrays.binarySearch (sortedArr, value);
int first = index;
int last = index;
if (index >= 0) {
while (first > 0 && sortedArr[first-1] == value)
first--;
while (last < sortedArr.length - 1 && sortedArr[last+1] == value)
last++;
}
After you run this code, the indices between first and last (inclusive) are all the indices that contain the searched value.
My algorithm is suppose to tell me if 'x'(which has the value 5) is in the sorted array. However, I keep getting a 0. Well since my condition states that if 'x' is not in the array show 0. Where am I going wrong?
import java.util.Arrays;
public class binarySeacg {
public static void main (String[]args)
{
int[] array = {10,7,11,5,13,8};
exchangesort(array);
binsearch(array,5);
System.out.println(Arrays.toString(array));
}
public static void exchangesort(int[] S)
{
int i,j,temp;
for(i=0;i<S.length;i++)
for(j=i+1;j<S.length;j++)
if(S[i]>S[j])
{
temp = S[i];
S[i] = S[j];
S[j] = temp;
}
}
public static int binsearch(int[] S, int x)
{
int location, low, high, mid;
low = 1; high = S.length;
location = 0;
while(low<=high && location==0)
{
mid =(low + high)/2;
if(x== S[mid])
location = mid;
else if(x < S[mid])
high = mid -1;
else
low = mid + 1;
}
System.out.println(location);
return location;
}
}
You set low = 1;, and 5 is the minimal element - so it is in index 0 - so in the sublist of [1,S.length] - it is indeed not there.
You should set low = 0;, and start from the first element - not the second. (Remember that index in java starts from 0, not 1).
(PS, note that in this specific case - the algorithm is correct, since in the sorted list - 5 is in the index 0).
Here you are sorting an array and then the sorted array is used for searching the element.
And if the search is successful, then you do the below assignment
location = mid; which means you are assigning the matching element's index to the location variable.
In this case, element 5 is in 0th index.
Hence you are always getting 0 on your STDOUT
Because, you are trying to find x value, which you are passing 3 and in your list. It is not present. So, change it to other value like 5 and then try.
Also, you should start low=0 instead of low=1. Because, it will miss the first element all the time.
public static int binsearch(int[] S, int x)
{
int location, low, high, mid;
low = 0; high = S.length;
location = 0;
while(low<=high && location==0)
{
mid =(low + high)/2;
if(x == S[mid])
{
location = mid;break;
}
else if (x < S[mid])
{
high = mid - 1;
} else
{
low = mid + 1;
}
}
System.out.println(location);
return location;
}
Note : For the different output, change the value binsearch(array,5); here, which is called from main() method. Remember, change the value, which are present in your list.
in java and most languages, the index starts from 0, not 1, and ends at n-1, not n
for binary search, check carefully about when exiting the while loop, and always remember the meaning of your low and high variables, whether it is [low, high], or [low, high)
for this specific problem, u should also consider what if there r duplicates in the array. whether to return the first element or anyone in the array