Reputation: 156
The problem is to remove every 2nd element until the last one remains. (Assuming the elements are placed in circle)
My program works fine for small values of n but when i use a large value it gives me a java.lang.OutOfMemoryError: Java heap space
message.
Can some help me out as to what I need to do to make my code more efficient and solve this error.
Thank You!
import java.util.*;
public class ArrayLists {
public static void main(String[] args) {
ArrayList myList = new ArrayList();
int n = 23987443;
for (int i = 1; i <= n; i = i + 2) {
myList.add("" + i);
}
Object x = myList.get(myList.size() - 1);
Object y = myList.get(myList.size() - 1);
while (myList.size() != 1) {
if (x == y) {
for (int i = 0; i <= myList.size() - 1; i++) {
myList.remove(i);
}
}
else {
x = y;
for (int i = 1; i <= myList.size() - 1; i++) {
myList.remove(i);
}
}
y = myList.get(myList.size() - 1);
}
System.out.println("Winner:" + myList);
}
}
Upvotes: 0
Views: 1081
Reputation: 837
The answer to your problem can be computed with a closed form, for if you write the number of elements in your list as follows:
n = 2^m + l
where m is the largest power of 2, such that 2^m <= n
then the winner is w = 2*l + 1.
So here is a more efficient program:
public class ArrayLists {
public static void main(String[] args) {
int n = 23987443;
int pow = Integer.highestOneBit(n);
int l = n - pow;
System.out.println("Winner:[" +((2*l)+1)+"]" );
}
}
The answer for that value of n:
Winner:[14420455]
To be honest I did not come up with the solution myself, but remembered reading about the Josephus problem in the book "Concrete Mathematics" (Google will find a PDF, check out section 1.3)
Upvotes: 0
Reputation: 533502
This uses less memory but is quote a bit faster as it is O(N * log N)
instead of O(N^2 * log N)
public static void main(String[] args) {
// for (int n = 1; n < 400; n++) {
int n = 23987443;
System.out.print(n + ": ");
result(n);
// }
}
private static void result(int n) {
List<Integer> myList = new ArrayList<>(), myList2 = new ArrayList<>();
for (int i = 1; i <= n; i = i + 2) {
myList.add(i);
}
Integer x = myList.get(myList.size() - 1);
Integer y = myList.get(myList.size() - 1);
while (myList.size() != 1) {
if (x == y) {
for (int i = 1; i < myList.size(); i += 2) {
myList2.add(myList.get(i));
}
} else {
x = y;
for (int i = 0; i <= myList.size() - 1; i += 2) {
myList2.add(myList.get(i));
}
}
List<Integer> tmp = myList2;
myList2 = myList;
myList = tmp;
myList2.clear();
y = myList.get(myList.size() - 1);
// System.out.println("\t" + myList);
}
System.out.println("Winner:" + myList.get(0));
}
Note: if you use TIntArrayList instead of ArrayList<Integer>
it will use about 1/5 of the memory.
Upvotes: 1
Reputation: 691715
You're building a huge list of strings. So you'll need enough memory to hold all the list. You could make it less consuming by initializing the list with the appropriate size, avoiding temporary copies to enlarge the list:
int n = 23987443;
List<String> myList = new ArrayList<String>(23987443);
You could also use a List<Integer>
instead, since that's what the list contains actually.
But a huge list of strings needs lots of memory. Enlarge the heap size with
java -Xmx1024m ...
for example (1024 MB of heap)
Upvotes: 2