I am trying to understand how Collections.binarySearch work in Java.
I don't quite understand the output I get.
public static void main(String args[]) {
// create arraylist
ArrayList<String> arlst=new ArrayList<String> ();
arlst.add("A");
arlst.add("D");
arlst.add("C");
arlst.add("B");
arlst.add("E");
int index=Collections.binarySearch(arlst, "D", Collections.reverseOrder());
System.out.println(index);
}
}
The output of this code is -1.
And when the elements have been inserted at this order
arlst.add("D");
arlst.add("E");
arlst.add("C");
arlst.add("B");
arlst.add("A");
I get 0 as a result. I thought the negative number was a result if the element was not found. Could anybody please clarify the output I receive?
Your data must be sorted according to the given comparator for the binary search to work as intended. (If it's not, the behavior is undefined.)
The list must be sorted into ascending order according to the specified comparator (as by the sort(List, Comparator) method), prior to making this call.
If the data is indeed sorted, the method will return the index of the sought element (if it's found) otherwise (-(insertion point) - 1), as specified in the documentation.
Example:
// Make sure it's sorted
Collections.sort(arlst, Collections.reverseOrder());
int index=Collections.binarySearch(arlst, "D", Collections.reverseOrder());
System.out.println(index); // prints 1
Just to make it clearer - why the output is -1. Yes, you didn't sort it first is a big mistake. But here are some other things to take clear as well.
As #aioobe mentioned in his answer, but he didn't make it clear enough though I think. What does (-(insertion point) - 1) mean? Here is what the doc says.
The index of the search key, if it is contained in the list; otherwise, (-(insertion point) - 1). The insertion point is defined as the point at which the key would be inserted into the list: the index of the first element greater than the key, or list.size() if all elements in the list are less than the specified key. Note that this guarantees that the return value will be >= 0 if and only if the key is found.
So to make the answer more clear: -1 = -0 - 1
What I want to underline here is that the output maybe -2 or -3 or whatever. Because if all elements in the list are less than the specified key, the output will be -list.size() - 1.
■ Searches are performed using the binarySearch() method.
■ Successful searches return the int index of the element being searched.
■ Unsuccessful searches return an int index that represents the insertion point. The insertion point is the place in the collection/array where the element would be inserted to keep the collection/array properly sorted.
* Return values and 0 indicate successful searches
* Negative numbers to indicate insertion points
* The first available insertion point is -1. Therefore,the actual insertion point is represented as (-(insertion point) -1)
Here is the inner workings of the binarySearch method:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package binarysearch;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
public class BinarySearch {
static ArrayList<String> strings;
public static void main(String[] args) {
String sample = "a quick brown fox jumped right over the lazy dog while an active lion was nowhere in sight";
String[] simpleArray = sample.split(" ");
strings = new ArrayList(Arrays.asList(simpleArray));
Collections.sort(strings);
// Enter a search string; here it is "lazy"
binarySearch(strings, "lazy");
System.out.println("");
// Print the Array contents for convenience
printArrayList(strings);
}
static void printArrayList(ArrayList<String> strings) {
int i = 0;
for (String s: strings) {
i++;
System.out.println(i + s );
}
}
static void binarySearch (ArrayList<String> strings, String searchString) {
boolean debug = true;
int low = 0;
int high = strings.size();
int mid = 0;
while (low <= high) {
// The > symbol marks the hits before search is concluded
System.out.print(">");
mid = (high + low) / 2;
int comparison = strings.get(mid).compareToIgnoreCase(searchString);
if (comparison > 0) {
high = mid - 1;
} else if (comparison < 0) {
low = mid + 1;
} else {
int temp = mid;
System.out.println("Found '" + searchString + "' at: " + (temp + 1) );
break;
}
}
}
}
Related
code
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class SynchronizedObjEffectArea {
interface Func {
void run();
}
private volatile int index = 0;
private static final int size = 20000;
public static void main(String[] args) {
final List<Func> threadList = new ArrayList<>();
final SynchronizedObjEffectArea obj = new SynchronizedObjEffectArea();
for (int i = 0; i < size; i++) {
threadList.add(obj::task1);
threadList.add(obj::task2);
}
Collections.shuffle(threadList);
for (final Func thread : threadList) {
thread.run();
}
}
private void task1() {
synchronized (SynchronizedObjEffectArea.this) {
index = index + 1;
}
}
private void task2() {
synchronized (SynchronizedObjEffectArea.this) {
index = index + 1;
if (index == size * 2) {
System.out.println("index: " + index);
}
}
}
}
With the code like above, i usually can got the output "index: 40000" or sometimes can not.
By comment out the code Collections.shuffle(threadList);, the code works fine, it always output "index: 40000"
I confuse that what shuffle function do cause the result like above? Can anyone explain this?
I don't think you're really asking about what Collections.shuffle does in general: I think you're asking why Collections.shuffle affects the output:
You are creating a load of tasks of two kinds: one of them increments a value ("kind 1"); the other increments a value and sometimes prints a message ("kind 2"). These are put into a list, and executed sequentially.
If you put an equal number of these two kinds of tasks into a list, shuffle them, and execute them sequentially, the item at a position is going to be a "kind 1" task 50% of the time, and a "kind 2" task the rest of the time.
Since "kind 2" tasks only prints for a specific value, you will only get something printed if it is a "kind 2" task at position 2 * size - 1, which will be 50% of the time.
If you don't shuffle them, but add "kind 1" and "kind 2" in turn, the item at position 2 * size - 1 will always be a "kind 2", so it will always print.
Collections.shuffle() Randomly permutes the specified list using a default source of randomness. All permutations occur with approximately equal likelihood.
So if tasks ar shuffled, sometimes task2 run first and then output is different.
This is the javadoc and source code for java.util.Collections.shuffle():
/**
* Randomly permute the specified list using the specified source of
* randomness. All permutations occur with equal likelihood
* assuming that the source of randomness is fair.<p>
*
* This implementation traverses the list backwards, from the last element
* up to the second, repeatedly swapping a randomly selected element into
* the "current position". Elements are randomly selected from the
* portion of the list that runs from the first element to the current
* position, inclusive.<p>
*
* This method runs in linear time. If the specified list does not
* implement the {#link RandomAccess} interface and is large, this
* implementation dumps the specified list into an array before shuffling
* it, and dumps the shuffled array back into the list. This avoids the
* quadratic behavior that would result from shuffling a "sequential
* access" list in place.
*
* #param list the list to be shuffled.
* #param rnd the source of randomness to use to shuffle the list.
* #throws UnsupportedOperationException if the specified list or its
* list-iterator does not support the <tt>set</tt> operation.
*/
#SuppressWarnings({"rawtypes", "unchecked"})
public static void shuffle(List<?> list, Random rnd) {
int size = list.size();
if (size < SHUFFLE_THRESHOLD || list instanceof RandomAccess) {
for (int i=size; i>1; i--)
swap(list, i-1, rnd.nextInt(i));
} else {
Object arr[] = list.toArray();
// Shuffle array
for (int i=size; i>1; i--)
swap(arr, i-1, rnd.nextInt(i));
// Dump array back into list
// instead of using a raw type here, it's possible to capture
// the wildcard but it will require a call to a supplementary
// private method
ListIterator it = list.listIterator();
for (int i=0; i<arr.length; i++) {
it.next();
it.set(arr[i]);
}
}
}
So, as out common knowledge of the English language tells us, shuffle will randomly re-order the contents of the Collection you specify.
That's also what the javadoc says.
So seeing you have this problem, next time
Look up the English term in a translator or dictionary
Use an IDE. That will show you the javadoc for the methods, and as a bonus it will do the formatting of your source code for you
Run a test with 3-10 items on a list, this will also show you what happens
I am trying to have this code determine which element has the closest value to a constant.
In this code the variable boxes = 5, any element that has boxCapacity >= boxes is added to an ArrayList. From that list, the one with the closest boxCapacity to boxes should be used. I am able to select those greater than boxes, but unable to pick that with the closest boxCapacity.
public void deliver(double miles, int boxes) {
for (int i = 0; i < cars.size(); i++){
if (cars.get(i).getBoxCapacity() >= boxes){
deliveryCars = new ArrayList<Car>();
deliveryCars.add(cars.get(i));
smallest = deliveryCars.get(0).getBoxCapacity();
for(j = 0; j < deliveryCars.size(); j++){
if (deliveryCars.get(j).getBoxCapacity() < smallest) {
smallest = deliveryCars.get(j).getBoxCapacity();
k++;
}
}
}
}
System.out.println("Delivering with " + deliveryCars.get(k).getPlate());
}
I tried to make a new list, but it has not been working out.
You can simplify your code to something that looks like that
public void deliver(double miles, int boxes){
// check if there are cars availible
if (!cars.isEmpty()) {
// assume that first car in a list is best for delivery
int smallest = cars.get(0).getBoxCapacity();
Car deliveryCar = cars.get(0);
// iterating over all cars in a list
// but still compares to the first car in a list
for (Car car : cars) {
if (car.getBoxCapacity() >= boxes
&& car.getBoxCapacity() < smallest) {
deliveryCar = car;
}
}
System.out.println("Delivering with " + deliveryCar.getPlate());
}
}
Using Java 8 streams...
Car deliveryVehicle = cars
.stream()
.filter(c -> c.getBoxCapacity() > boxes)
.min(Comparator.comparingInt(Car::getBoxCapacity))
.orElse(null);
Assuming your cars was an iterable/streamable collection, this creates a stream, filters it to extract all instances where the capacity is greater than boxes, finds the element with the smallest capacity, and returns it, or null if there were no cars with more than boxes capacity.
You can then do whatever you want with the returned Car object, like call getPlate(). Remember to check for null for the case where no acceptable car was found.
This answer builds from the clarifications that #DataDino provided, namely that the goal is to find the car with the lowest boxCapacity that is greater than the targetCapacity.
Given the list of cars, you can filter out any car with a boxCapacity smaller than the target, and then select the minimum boxCapacity from what is left.
List<Car> cars = List.of(new Car(8), new Car(3), new Car(5), new Car(6));
int suggestedCapacity = 4;
Optional<Car> bestFit = cars.stream()
.filter(car -> car.getBoxCapacity() >= suggestedCapacity)
.min(Comparator.comparing(Car::getBoxCapacity));
if (bestFit.isPresent()) {
System.out.println("found car with capacity " + bestFit.get().getBoxCapacity());
} else {
System.out.println("No suitable car found");
}
The Streams api takes care of the list manipulation and keeping track of the internal state of the minimum for you.
You do not need a new list. The task you've outlined is a sequential search through an unordered list, and unless I misunderstand your goal, you only need a single for loop -- that is, you only need to iterate through the list one time. Since you are looking for a single item and you don't need to look at more than one item at a time to see if it's the best one so far, you only need one variable to keep track of its location in the list.
Here's a working sample. Notice the variable names describe the purpose (e.g. "mimimumBoxCapacity" instead of the ambiguous "boxes"). This helps me better understand what my code is doing.
// print the plate number of the car with the smallest boxCapacity
// greater than a specified minimum
public void deliver(List<Car> cars, double miles, int minimumBoxCapacity)
{
if ((cars != null) && (cars.size() > 0))
{
int indexOfBestMatch = -1; // negative index means no match yet
for (int i = 0; i < cars.size(); i++)
{
if (cars.get(i).getBoxCapacity() > minimumBoxCapacity)
{
if (indexOfBestMatch < 0)
{
// this is the only match seen so far; remember it
indexOfBestMatch = i;
}
else
{
// found a better match; replace the old best match
if (cars.get(i).getBoxCapacity() < cars.get(indexOfBestMatch).getBoxCapacity())
{
indexOfBestMatch = i;
}
}
}
}
if (indexOfBestMatch >= 0)
{
System.out.println("Delivering with " + cars.get(indexOfBestMatch).getPlate());
}
}
}
This code illustrates how your algorithm would need to change to do what you want. DataDino's answer using a Car variable to keep track of the best fit is even clearer, especially where the method returns a Car result and lets the calling logic decide what to do with that result.
(Your original code didn't compile, because variables like "smallest" and "deliveryCars" weren't defined before they were used. It would be helpful in the future if you post code that compiles, even if it doesn't yet do what you want it to do.)
So I think what you're saying is that you have of list of values say [0, 1, 2, 3, 4, 5, 6] and then you are given another number say 4, and what you want to do is to select the number from the list that is the smallest of all the numbers greater than 4, so in this case you'd want to choose 5, right?
Well, there are a ton of ways to do that. But the fastest way to do it is to go through the list one time and keep track of the smallest number greater than your 'targetNumber':
...
public Integer getNextBiggest(List<Integer> numbers, int targetNumber)
{
// Set the default 'nextInt' to the largest possible value.
int nextInt = Integer.MAX_VALUE;
for (int i = 0; i < numbers.length; i++) {
// If the current number is greater than our targetNumber.
if (sortedList.get(i) > targetNumber) {
// Set the nextInt variable to the MINIMUM of current number
// and the current nextInt value.
nextInt = Math.min(nextInt, sortedList.get(i));
}
}
return nextInt;
}
...
So that would work, but your list is a bit more complicated since you're using Objects and not integers, that said, it's a simple conversion:
First some assumptions:
cars is a List.
Car object has a getBoxCapacity() method that returns an int.
Car object has a getPlate() method that returns a String.
Ok so it might look like this:
...
public Car getBestFitCar(List<Car> cars, int targetBoxCapacity)
{
// Default best fit is null. No best first yet.
Car bestFit = null;
for (int i = 0; i < cars.length; i++) {
Car current = cars.get(i);
// If the current Car box capacity is greater than our target box capacity.
if (current.getBoxCapacity() > targetBoxCapacity) {
// Set the bestFit variable to the Car that has the MINIMUM
// 'box capacity' between the current Car and the bestFit Car.
if (bestFit == null || bestFit.getBoxCapacity() > current.getBoxCapacity()) {
bestFit = current;
}
}
}
return bestFit;
}
public static void main(String[] args) {
List<Car> cars = new ArrayList<>();
// add some cars here...
int targetBoxCapacity = 5;
Car bestFit = getBestFitCar(cars, targetBoxCapacity);
if (bestFit != null) {
System.out.println("Best fit car is: " + bestFit.getPlate());
} else {
System.out.println("No car can fit " + targetBoxCapacity + " boxes.");
}
}
Update:
I've seen some nice responses using streams, but I'd to add some caution. Streams make writing the code faster/more readable but would end up being less efficient in time/space than a solution with simple loops. This solution only uses O(1) extra space, and O(n) time in the worst case.
I'd figure the stream answers would use O(n) extra space, and O(n * n log n) time in the worst case.
So, if you have a tiny list I'd say go with the simple really cool streams solutions, but you have a list of a lot of elements that you'll be better off with a more traditional approach.
So given a string such as: 0100101, I want to return a random single index of one of the positions of a 1 (1, 5, 6).
So far I'm using:
protected int getRandomBirthIndex(String s) {
ArrayList<Integer> birthIndicies = new ArrayList<Integer>();
for (int i = 0; i < s.length(); i++) {
if ((s.charAt(i) == '1')) {
birthIndicies.add(i);
}
}
return birthIndicies.get(Randomizer.nextInt(birthIndicies.size()));
}
However, it's causing a bottle-neck on my code (45% of CPU time is in this method), as the strings are over 4000 characters long. Can anyone think of a more efficient way to do this?
If you're interested in a single index of one of the positions with 1, and assuming there is at least one 1 in your input, you can just do this:
String input = "0100101";
final int n=input.length();
Random generator = new Random();
char c=0;
int i=0;
do{
i = generator.nextInt(n);
c=input.charAt(i);
}while(c!='1');
System.out.println(i);
This solution is fast and does not consume much memory, for example when 1 and 0 are distributed uniformly. As highlighted by #paxdiablo it can perform poorly in some cases, for example when 1 are scarce.
You could use String.indexOf(int) to find each 1 (instead of iterating every character). I would also prefer to program to the List interface and to use the diamond operator <>. Something like,
private static Random rand = new Random();
protected int getRandomBirthIndex(String s) {
List<Integer> birthIndicies = new ArrayList<>();
int index = s.indexOf('1');
while (index > -1) {
birthIndicies.add(index);
index = s.indexOf('1', index + 1);
}
return birthIndicies.get(rand.nextInt(birthIndicies.size()));
}
Finally, if you need to do this many times, save the List as a field and re-use it (instead of calculating the indices every time). For example with memoization,
private static Random rand = new Random();
private static Map<String, List<Integer>> memo = new HashMap<>();
protected int getRandomBirthIndex(String s) {
List<Integer> birthIndicies;
if (!memo.containsKey(s)) {
birthIndicies = new ArrayList<>();
int index = s.indexOf('1');
while (index > -1) {
birthIndicies.add(index);
index = s.indexOf('1', index + 1);
}
memo.put(s, birthIndicies);
} else {
birthIndicies = memo.get(s);
}
return birthIndicies.get(rand.nextInt(birthIndicies.size()));
}
Well, one way would be to remove the creation of the list each time, by caching the list based on the string itself, assuming the strings are used more often than they're changed. If they're not, then caching methods won't help.
The caching method involves, rather than having just a string, have an object consisting of:
current string;
cached string; and
list based on the cached string.
You can provide a function to the clients to create such an object from a given string and it would set the string and the cached string to whatever was passed in, then calculate the list. Another function would be used to change the current string to something else.
The getRandomBirthIndex() function then receives this structure (rather than the string) and follows the rule set:
if the current and cached strings are different, set the cached string to be the same as the current string, then recalculate the list based on that.
in any case, return a random element from the list.
That way, if the list changes rarely, you avoid the expensive recalculation where it's not necessary.
In pseudo-code, something like this should suffice:
# Constructs fastie from string.
# Sets cached string to something other than
# that passed in (lazy list creation).
def fastie.constructor(string s):
me.current = s
me.cached = s + "!"
# Changes current string in fastie. No list update in
# case you change it again before needing an element.
def fastie.changeString(string s):
me.current = s
# Get a random index, will recalculate list first but
# only if necessary. Empty list returns index of -1.
def fastie.getRandomBirthIndex()
me.recalcListFromCached()
if me.list.size() == 0:
return -1
return me.list[random(me.list.size())]
# Recalculates the list from the current string.
# Done on an as-needed basis.
def fastie.recalcListFromCached():
if me.current != me.cached:
me.cached = me.current
me.list = empty
for idx = 0 to me.cached.length() - 1 inclusive:
if me.cached[idx] == '1':
me.list.append(idx)
You also have the option of speeding up the actual searching for the 1 character by, for example, useing indexOf() to locate them using the underlying Java libraries rather than checking each character individually in your own code (again, pseudo-code):
def fastie.recalcListFromCached():
if me.current != me.cached:
me.cached = me.current
me.list = empty
idx = me.cached.indexOf('1')
while idx != -1:
me.list.append(idx)
idx = me.cached.indexOf('1', idx + 1)
This method can be used even if you don't cache the values. It's likely to be faster using Java's probably-optimised string search code than doing it yourself.
However, you should keep in mind that your supposed problem of spending 45% of time in that code may not be an issue at all. It's not so much the proportion of time spent there as it is the absolute amount of time.
By that, I mean it probably makes no difference what percentage of the time being spent in that function if it finishes in 0.001 seconds (and you're not wanting to process thousands of strings per second). You should only really become concerned if the effects become noticeable to the user of your software somehow. Otherwise, optimisation is pretty much wasted effort.
You can even try this with best case complexity O(1) and in worst case it might go to O(n) or purely worst case can be infinity as it purely depends on Randomizer function that you are using.
private static Random rand = new Random();
protected int getRandomBirthIndex(String s) {
List<Integer> birthIndicies = new ArrayList<>();
int index = s.indexOf('1');
while (index > -1) {
birthIndicies.add(index);
index = s.indexOf('1', index + 1);
}
return birthIndicies.get(rand.nextInt(birthIndicies.size()));
}
If your Strings are very long and you're sure it contains a lot of 1s (or the String you're looking for), its probably faster to randomly "poke around" in the String until you find what you are looking for. So you save the time iterating the String:
String s = "0100101";
int index = ThreadLocalRandom.current().nextInt(s.length());
while(s.charAt(index) != '1') {
System.out.println("got not a 1, trying again");
index = ThreadLocalRandom.current().nextInt(s.length());
}
System.out.println("found: " + index + " - " + s.charAt(index));
I'm not sure about the statistics, but it rare cases might happen that this Solution take much longer that the iterating solution. On case is a long String with only a very few occurrences of the search string.
If the Source-String doesn't contain the search String at all, this code will run forever!
One possibility is to use a short-circuited Fisher-Yates style shuffle. Create an array of the indices and start shuffling it. As soon as the next shuffled element points to a one, return that index. If you find you've iterated through indices without finding a one, then this string contains only zeros so return -1.
If the length of the strings is always the same, the array indices can be static as shown below, and doesn't need reinitializing on new invocations. If not, you'll have to move the declaration of indices into the method and initialize it each time with the correct index set. The code below was written for strings of length 7, such as your example of 0100101.
// delete this and uncomment below if string lengths vary
private static int[] indices = { 0, 1, 2, 3, 4, 5, 6 };
protected int getRandomBirthIndex(String s) {
int tmp;
/*
* int[] indices = new int[s.length()];
* for (int i = 0; i < s.length(); ++i) indices[i] = i;
*/
for (int i = 0; i < s.length(); i++) {
int j = randomizer.nextInt(indices.length - i) + i;
if (j != i) { // swap to shuffle
tmp = indices[i];
indices[i] = indices[j];
indices[j] = tmp;
}
if ((s.charAt(indices[i]) == '1')) {
return indices[i];
}
}
return -1;
}
This approach terminates quickly if 1's are dense, guarantees termination after s.length() iterations even if there aren't any 1's, and the locations returned are uniform across the set of 1's.
I found recently the default renderable sort function in LibGDX wasn't quite up to my needs. (see; Draw order changes strangely as camera moves? )
Essentially a few objects rendered in front when they should render behind.
Fortunately, the renderables in question always have a guarantied relationship. The objects are attached to eachother so when one moves the other moves. One object can be seen as being literally "pinned" to the other, so always in front.
This gave me the idea that if I specified a "z-index" (int) and "groupname" (String) for each object, I could manually take over the draw order, and for things with the same groupname, ensure they are positioned next to eachother in the list, in the order specified by the z-index. (low to high)
//For example an array of renderables like
0."testgroup2",11
1."testgroup",20
2."testgroup2",10
3.(no zindex attribute)
4."testgroup",50
//Should sort to become
0."testgroup",20
1."testgroup",50
2.(no zindex attribute)
3."testgroup2",10
4."testgroup2",11
// assuming the object2 in testgroup2 are closer to the camera, the one without a index second closest, and the rest furthest<br>
//(It is assumed that things within the same group wont be drastically different distances)
I implemented a sort system in libgdx to do this as followed;
/**
* The goal of this sorter is to sort the renderables the same way LibGDX would do normally (in DefaultRenderableSorter)<br>
* except if they have a ZIndex Attribute.<br>
* A Zindex attribute provides a groupname string and a number.<br>
* Renderables with the attribute are placed next to others of the same group, with the order within the group determined by the number<br>
*
* For example an array of renderables like;<br><br>
* 0."testgroup",20<br>
* 1."testgroup2",10<br>
* 2.(no zindex attribute)<br>
* 3."testgroup",50<br>
* <br>Should become;<br><br>
* 0."testgroup",20<br>
* 1."testgroup",50<br>
* 2.(no zindex attribute)<br>
* 3."testgroup2",10<br>
* <br>
* assuming the object in testgroup2 is closer to the camera, the one without a index second closest, and the rest furthest<br>
* (It is assumed that things within the same group wont be drastically different distances)<br>
*
* #param camera - the camera in use to determine normal sort order when we cant place in a existing group
* #param resultList - an array of renderables to change the order of
*/
private void customSorter(Camera camera, Array<Renderable> resultList) {
//make a copy of the list to sort. (This is probably a bad start)
Array <Renderable> renderables = new Array <Renderable> (resultList);
//we work by clearing and rebuilding the Renderables array (probably not a good method)
resultList.clear();
//loop over the copy we made
for (Renderable o1 : renderables) {
//depending of if the Renderable as a ZIndexAttribute or not, we sort it differently
//if it has one we do the following....
if (o1.material.has(ZIndexAttribute.ID)){
//get the index and index group name of it.
int o1Index = ((ZIndexAttribute)o1.material.get(ZIndexAttribute.ID)).zIndex;
String o1GroupName = ((ZIndexAttribute)o1.material.get(ZIndexAttribute.ID)).group;
//setup some variables
boolean placementFound = false; //Determines if a placement was found for this renderable (this happens if it comes across another with the same groupname)
int defaultPosition = -1; //if it doesn't find another renderable with the same groupname, this will be its position in the list. Consider this the "natural" position based on distance from camera
//start looping over all objects so far in the results (urg, told you this was probably not a good method)
for (int i = 0; i < resultList.size; i++) {
//first get the renderable and its ZIndexAttribute (null if none found)
Renderable o2 = resultList.get(i);
ZIndexAttribute o2szindex = ((ZIndexAttribute)o2.material.get(ZIndexAttribute.ID));
if (o2szindex!=null){
//if the renderable we are comparing too has a zindex, then we get its information
int o2index = o2szindex.zIndex;
String o2groupname = o2szindex.group;
//if its in the same group as o1, then we start the processing of placing them nexto eachother
if (o2groupname.equals(o1GroupName)){
//we either place it in front or behind based on zindex
if (o1Index<o2index){
//if lower z-index then behind it
resultList.insert(i, o1);
placementFound = true;
break;
}
if (o1Index>o2index){
//if higher z-index then it should go in front UNLESS there is another of this group already there too
//in which case we just continue (which will cause this to fire again on the next renderable in the inner loop)
if (resultList.size>(i+1)){
Renderable o3 = resultList.get(i+1);
ZIndexAttribute o3szindex = ((ZIndexAttribute)o3.material.get(ZIndexAttribute.ID));
if (o3szindex!=null){
String o3groupname = o3szindex.group;
if (o3groupname!=null && o3groupname.equals(o1GroupName)){
//the next element is also a renderable with the same groupname, so we loop and test that one instead
continue;
}
}
}
// Gdx.app.log("zindex", "__..placeing at:"+(i+1));
//else we place after the current one
resultList.insert(i+1, o1);
placementFound = true;
break;
}
}
}
//if no matching groupname found we need to work out a default placement.
int placement = normalcompare(o1, o2); //normal compare is the compare function in DefaultRenderableSorter.
if (placement>0){
//after then we skip
//(we are waiting till we are either under something or at the end
} else {
//if placement is before, then we remember this position as the default (but keep looking as there still might be matching groupname, which should take priority)
defaultPosition = i;
//break; //break out the loop
}
}
//if we have checked all the renderables positioned in the results list, and none were found with matching groupname
//then we use the defaultposition to insert it
if (!placementFound){
//Gdx.app.log("zindex", "__no placement found using default which is:"+defaultPosition);
if (defaultPosition>-1){
resultList.insert(defaultPosition, o1);
} else {
resultList.add(o1);
}
}
continue;
}
//...(breath out)...
//ok NOW we do placement for things that have no got a ZIndexSpecified
boolean placementFound = false;
//again, loop over all the elements in results
for (int i = 0; i < resultList.size; i++) {
Renderable o2 = resultList.get(i);
//if not we compare by default to place before/after
int placement = normalcompare(o1, o2);
if (placement>0){
//after then we skip
//(we are waiting till we are either under something or at the end)
continue;
} else {
//before
resultList.insert(i, o1);
placementFound = true;
break; //break out the loop
}
}
//if no placement found we go at the end by default
if (!placementFound){
resultList.add(o1);
};
} //go back to check the next element in the incomeing list of renderables (that is, the copy we made at the start)
//done
}
//Copy of the default sorters compare function
//;
private Camera camera;
private final Vector3 tmpV1 = new Vector3();
private final Vector3 tmpV2 = new Vector3();
public int normalcompare (final Renderable o1, final Renderable o2) {
final boolean b1 = o1.material.has(BlendingAttribute.Type) && ((BlendingAttribute)o1.material.get(BlendingAttribute.Type)).blended;
final boolean b2 = o2.material.has(BlendingAttribute.Type) && ((BlendingAttribute)o2.material.get(BlendingAttribute.Type)).blended;
if (b1 != b2) return b1 ? 1 : -1;
// FIXME implement better sorting algorithm
// final boolean same = o1.shader == o2.shader && o1.mesh == o2.mesh && (o1.lights == null) == (o2.lights == null) &&
// o1.material.equals(o2.material);
o1.worldTransform.getTranslation(tmpV1);
o2.worldTransform.getTranslation(tmpV2);
final float dst = (int)(1000f * camera.position.dst2(tmpV1)) - (int)(1000f * camera.position.dst2(tmpV2));
final int result = dst < 0 ? -1 : (dst > 0 ? 1 : 0);
return b1 ? -result : result;
}
As far as I can tell my customSorter function produces the order I want - the renderables now look like they are drawn in the right order.
However, this also seems like a hackjob, and I am sure my sorting algorithm is horrendously inefficient.
I would like advice on how to either;
a) Improve my own algorithm, especially in regards to any quirks to bare in mind when doing cross-platform LibGDX development (ie, array types, memory management in regards to android/web etc)
b) Alternative more efficient solutions having a similar "z index override" of the normal draw-order sorting.
Notes;
. The grouping is necessary. This is because while things are firmly stuck relatively to eachother within a group, groups themselves can also move about in front/behind eachother. (but not between). This makes it tricky to do a "global" override of the draw order, rather then a local one per group.
. If it helps, I can add/change the zindexattribute object in any way.
. I am thinking somehow "pre-storeing" each group of objects in a array could help things, but not 100% sure how.
First of all do never copy a list if not needed. The list with renderables could be really huge since it also could contain resources. Copying will be very very slow. If you need something local and you need performance try to make it final since it can improve the performance.
So a simple approach would be the default sorting of Java. You need to implement a Comperator for your class for example the Class with z index could look like this:
public class MyRenderable {
private float z_index;
public MyRenderable(float i)
{
z_index = i;
}
public float getZ_index() {
return z_index;
}
public void setZ_index(float z_index) {
this.z_index = z_index;
}
}
If you want a faster sort since your list wont change that much on runtime you could implement a insertion sort since it does a faster job if the list is kind of presorted. If it is not pre sorted it does take longer but in general it should only be the first sort call where it is alot disordered in your case.
private void sortList(ArrayList<MyRenderable> array) {
// double starttime = System.nanoTime();
for (int i = 1; i < array.size(); i++) {
final MyRenderable temp = array.get(i);
int j = i - 1;
while (j >= 0 && array.get(j).getZ_index() < temp.getZ_index()) {
array.set(j + 1, array.get(j));
j--;
}
array.set(j + 1, temp);
}
// System.out.println("Time taken: " + (System.nanoTime() - starttime));
}
To use this method you simply call it with your Array
sortList(renderbales);
In your case you need to take care of the ones that do not have a Z index. Maybe you could give them a 0 since they'll get sorted at the right position(i guess). Else you can use the given methods in z case and the regular in no z case as you do already.
After the conversation in the comments. I dont think it is a good idea to push everything into one list. It's hard to sort and would be very slow. A better approach would be a list of groups. Since you want to have groups, programm a group. Do not use String names, use IDs or types (way more easy to sort and it doesn't really matter). So a simple group would be this:
public class Group{
//think about privates and getters or methods to add things which also checks some conditions and so on
public int groupType;
public ArrayList<MyRenderable> renderables;
}
And now all your groups into a list. (this contains all your renderbales then)
ArrayList<Group> allRenderables = new ArrayList<>();
Last but not least sort the groups and sort the renderables. Since i dont think that your group ids/names will change on runtime, sort them once or even use a SortedSet instead of a ArrayList. But basically the whole sorting looks like this:
for(Group g: allRenderables)
sortRenderables(g.renderables); //now every group is sorted
//now sort by group names
sortGroup(allRenderables);
With the following insertionsorts as shown above
public static void sortRenderables(ArrayList<MyRenderable> array) {
for (int i = 1; i < array.size(); i++) {
final MyRenderable temp = array.get(i);
int j = i - 1;
while (j >= 0 && array.get(j).getZ_index() < temp.getZ_index()) {
array.set(j + 1, array.get(j));
j--;
}
array.set(j + 1, temp);
}
}
public static void sortGroup(ArrayList<Group> array) {
for (int i = 1; i < array.size(); i++) {
final Group temp = array.get(i);
int j = i - 1;
while (j >= 0 && array.get(j).groupType < temp.groupType) {
array.set(j + 1, array.get(j));
j--;
}
array.set(j + 1, temp);
}
}
Hi I am across this problem and trying to solve this
Take a second to imagine that you are in a room with 100 chairs arranged in a circle. These chairs are numbered sequentially from One to One Hundred.
At some point in time, the person in chair #1 will be told to leave the room. The person in chair #2 will be skipped, and the person in chair #3 will be told to leave. Next to go is person in chair #6. In other words, 1 person will be skipped initially, and then 2, 3, 4.. and so on. This pattern of skipping will keep going around the circle until there is only one person remaining.. the survivor. Note that the chair is removed when the person leaves the room.Write a program to figure out which chair the survivor is sitting in.
I made good progress but stuck with a issue, after the count reaches 100 and not sure how to iterate from here, can any one help me, this is my code
import java.util.ArrayList;
public class FindSurvivor {
public static void main(String[] args) {
System.out.println(getSurvivorNumber(10));
}
private static int getSurvivorNumber(int numChairs) {
// Handle bad input
if (numChairs < 1) {
return -1;
}
// Populate chair array list
ArrayList<Integer> chairs = new ArrayList<Integer>();
for (int i = 0; i < numChairs; i++) {
chairs.add(i + 1);
}
int chairIndex = 0;
int lr =0;
while (chairs.size() > 1) {
chairs.remove(lr);
chairIndex+=1;
System.out.println(lr+" lr, size "+chairs.size()+" index "+chairIndex);
if(lr==chairs.size()||lr==chairs.size()-1)
lr=0;
lr = lr+chairIndex;
printChair(chairs);
System.out.println();
}
return chairs.get(0);
}
public static void printChair(ArrayList<Integer> chairs){
for(int i : chairs){
System.out.print(i);
}
}
}
The answer is 31. Here are three different implementations
var lastSurvivor = function(skip, count, chairs) {
//base case checks to see if there is a lone survivor
if (chairs.length === 1)
return chairs[0];
//remove chairs when they are left/become dead
chairs.splice(skip, 1);
//increment the skip count so we know which chair
//to leave next.
skip = (skip + 1 + count) % chairs.length;
count++;
//recursive call
return lastSurvivor(skip, count, chairs);
};
/** TESTS *******************************************************************
----------------------------------------------------------------------------*/
var result = lastSurvivor(0, 0, chairs);
console.log('The lone survivor is located in chair #', result);
// The lone survivor is located in chair # 31
/** ALTERNATE IMPLEMENTATIONS ***********************************************
-----------------------------------------------------------------------------
/* Implemenation 2
-----------------*/
var lastSurvivor2 = function(chairs, skip) {
skip++;
if (chairs === 1)
return 1;
else
return ((lastSurvivor2(chairs - 1, skip) + skip - 1) % chairs) + 1;
};
/** Tests 2 *******************************************************************/
var result = lastSurvivor2(100, 0);
console.log('The lone survivor is located in chair #', result);
// The lone survivor is located in chair # 31
/* Implemenation 3
------------------*/
var chairs2 = [];
for (var i = 1; i <= 100; i++)
chairs2.push(i);
var lastSurvivor3 = function(chairs, skip) {
var count = 0;
while (chairs.length > 1) {
chairs.splice(skip, 1);
skip = (skip + 1 + count) % chairs.length;
count++;
}
return chairs[0];
};
/** Tests 3 *******************************************************************/
var result = lastSurvivor3(chairs2, 0);
console.log('The lone survivor is located in chair #', result);
// The lone survivor is located in chair # 31
I'm not sure what your removal pattern is but I'd probably implement this as a circular linked list where the 100th seat holder will connect back to the 1st seat holder. If you use an array, you will have to worry about re-organizing the seats after every removal.
There is elegant analytical solution:
Let's change numbering of people: #2 -> #1, #3 -> #2, ..., #1 -> #100 (in the end we just need to substract 1 to "fix" the result). Now first person remains instead or leaving. Suppose that there is only 64 people in circle. It's easy to see that after first elimination pass 32 people in circle will remain and numbering will start again from #1. So in the end only #1 will remain.
We have 100 people. After 36 people will leave the circle we will end up with 64 people - and we know how to solve this. For each person that leaves the room one person remains, so circle with 64 people will start from 1 + 2*36 = #73 (new #1). Because of changing indexes on first step final answer will be #72.
In general case res = 2*(N - closest_smaller_pow_2) = 2*N - closest_larger_pow_2. The code is trivial:
public static long remaining(long total) {
long pow2 = 1;
while (pow2 < total) {
pow2 *= 2;
}
return 2*total - pow2;
}
Also this algorithm has O(log(N)) complexity instead of O(N), so it's possible to calculate function for huge inputs (it can be easily adapted to use BigInteger instead of long).
First, let's assume the chairs are numbered from 0. We'll switch the numbering back at the end -- but usually things are simpler when items are enumerated from 0 rather than 1.
Now, if you've got n people and you start eliminating at chair x (x is 0 or 1) then in a single pass through you're going to eliminate half the people. Then you've got a problem of roughly half the size (possibly plus one), and if you solve that, you can construct the solution to the original problem by multiplying that sub-result by 2 and maybe adding one.
To code this, it's simply a matter of getting the 4 cases (n odd or even vs x 0 or 1) right. Here's a version that gets the 4 cases right by using bitwise trickery.
public static long j2(long n, long x) {
if (n == 1) return 0;
return j2(n/2 + (n&x), (n&1)^x) + 1-x;
}
A solution with chairs numbered from 1 and without the extra argument can now be written:
public static long remaining(long n) {
return 1 + j2(n, 0);
}
This runs in O(log n) time and uses O(log n) memory.
If your step is incremental you can you use the following code:
int cur = 0;
int step = 1;
while (chairs.size() > 1) {
chairs.remove(cur);
cur += ++step;
cur %= chairs.size();
}
return chairs.get(0);
If your step is fixed to 1 then based on explanation provided by #Jarlax you can solve the problem with one-line of code in O(log n) time:
//for long values
public static long remaining(long numChairs) {
return (numChairs << 1) - (long)Math.pow(2,Long.SIZE - Long.numberOfLeadingZeros(numChairs));
}
//for BigInteger values
public static BigInteger remaining(BigInteger numChairs) {
return numChairs.shiftLeft(1).subtract(new BigInteger("2").pow(numChairs.bitLength()));
}
However, if you stick with ArrayLists no extra variables are required to your code. Always remove the first element and remove-then-add the next at the end of the list. This is however O(n).
while (chairs.size() > 1) {
chairs.remove(0);
chairs.add(chairs.remove(0));
}
return chairs.get(0);