Whats wrong with quicksort3 - java

What's wrong with my 3-way partition quicksort? When input data less then 100000 it work normal time. When input data = 100000 it work around 9 sec.
I use Dijkstra 3-way partition. If input data consists of a large number of identical elements everything work normal, when input data random work too slow.
static void randomizedQuickSort(int[] a, int l, int r) {
if (l >= r) {
return;
}
int[] m = Partition3(a, l, r);
randomizedQuickSort(a, l, m[0] - 1);
randomizedQuickSort(a, m[1] + 1, r);
}
private static int[] Partition3(int[] nums, int l, int r) {
Random random = new Random();
int k = random.nextInt(r - l + 1) + l;
int mid = nums[k];
int m1 = 0;
int i = 0;
int m2 = r;
while (m1 <= m2) {
if (nums[m1] < mid) {
swap(nums, i, m1);
i++;
m1++;
} else if (nums[m1] > mid) {
swap(nums, m1, m2);
m2--;
} else {
m1++;
}
}
return new int[]{i, m2};
}

You are doing something wrong as the number of swaps is O(n^2) If you count the number of calls to swap you get something like (the first number is the number of elements)
10000: Took 0.078000 seconds, and 33432534 swaps
20000: Took 0.291000 seconds, and 166934755 swaps
40000: Took 1.102000 seconds, and 702291723 swaps
80000: Took 4.482000 seconds, and 2837543629 swaps
160000: Took 17.590000 seconds, and 11373050608 swaps
The problem is the lines
int m1 = 0;
int i = 0;
where you sort from the start of the array on every sort.
int m1 = l; // sort from the start of the section.
int i = l;
The full version is....
public static void main(String[] args) {
for (int t = 100_000; t <= 100_000_000; t *= 10) {
int[] nums = new int[t];
for (int i = 0; i < nums.length; i++) {
nums[i] = random.nextInt();
}
long start = System.currentTimeMillis();
swaps = 0;
randomizedQuickSort(nums, 0, nums.length - 1);
long time = System.currentTimeMillis() - start;
for (int i=0;i<nums.length-1;i++)
if (nums[0] > nums[1])
throw new AssertionError();
System.out.printf("%d: Took %f seconds, and %d swaps%n", t, time / 1e3, swaps);
}
}
static void randomizedQuickSort(int[] a, int l, int r) {
if (l >= r) {
return;
}
long m = Partition3(a, l, r);
int m0 = (int) (m >> 32);
int m1 = (int) m;
randomizedQuickSort(a, l, m0 - 1);
randomizedQuickSort(a, m1 + 1, r);
}
static final Random random = new Random();
static long swaps = 0;
private static long Partition3(int[] nums, int l, int r) {
int k = random.nextInt(r - l + 1) + l;
int mid = nums[k];
int m1 = l;
int i = l;
int m2 = r;
while (m1 <= m2) {
if (nums[m1] < mid) {
swap(nums, i, m1);
i++;
m1++;
} else if (nums[m1] > mid) {
swap(nums, m1, m2);
m2--;
} else {
m1++;
}
}
return ((long) i << 32) | m2;
}
private static void swap(int[] nums, int i, int j) {
int t = nums[i];
nums[i] = nums[j];
nums[j] = t;
swaps++;
}
prints
100000: Took 0.018000 seconds, and 2032183 swaps
1000000: Took 0.168000 seconds, and 24872604 swaps
10000000: Took 1.709000 seconds, and 287681791 swaps
100000000: Took 19.015000 seconds, and 3353327832 swaps

Related

I'am confused that the random quick sort is slower than normal quick sort

With the Introduction to algorithm .The Professor intro that the random quick sort can decrease the percentage of bad situation and improve the performance of time cost.But I wonder if there any problem with my code that cause the result
in contrast.
Here are my java code
public class quickSort {
public final int size = 200000;
public final int times = 10;
#Test
public void quick_sort() {
int test1[] = new int[size];
int test2[];
int k = 0;
long t1 = 0;
long t2 = 0;
for (int i = 0; i < size; i++) {
test1[i] = i;
}
test2 = test1.clone();
while (k < times) {
shuffle(test1);
shuffle(test2);
long start1 = System.currentTimeMillis();
split1(test1, 0, test1.length -1);
t1 += System.currentTimeMillis() - start1;
long start2 = System.currentTimeMillis();
split2(test2, 0, test2.length -1);
t2 += System.currentTimeMillis() - start2;
k++;
}
System.out.println("normal quick sort time is" + t1 + "ms");
System.out.println("random quick sort time is " + t2 + "ms");
}
public void split1(int[] arr, int start, int end) {
if (start >= end) {
return;
} else {
int middle = sort(arr, start, end);
split1(arr, start, middle - 1);
split1(arr, middle + 1, end);
}
}
public void split2(int[] arr, int start, int end) {
if (start >= end) {
return;
} else {
int middle = random_sort(arr, start, end);
split2(arr, start, middle - 1);
split2(arr, middle + 1, end);
}
}
// random quick sort
public int random_sort(int[] arr, int start, int end) {
int random = start + new Random().nextInt(end - start + 1);
int key = arr[random];
int loop = start;
int front = random == start ? start + 1 : start;
while (loop <= end) {
if (loop == random) {
loop++;
continue;
}
if (arr[loop] < key) {
int temp = arr[loop];
arr[loop] = arr[front];
arr[front] = temp;
front = front + 1 == random ? front += 2 : front + 1;
}
loop++;
}
if (front < end) {
int temp = arr[random];
arr[random] = arr[front];
arr[front] = temp;
} else {
front = end;
}
return front;
}
// normal quick sort
public int sort(int[] arr, int start, int end) {
int loop = start;
int key = arr[start];
int front = start + 1;
while (loop <= end) {
if (key > arr[loop]) {
int temp = arr[loop];
arr[loop] = arr[front];
arr[front++] = temp;
}
loop++;
}
if (front != 1) {
int temp = arr[front - 1];
arr[front - 1] = arr[start];
arr[start] = temp;
}
return front - 1;
}
// shuffle the Array
public void shuffle(int[] arr) {
int length = arr.length;
int random_num = 0;
for (int i = 0; i < length; i++) {
random_num = new Random().nextInt(length);
int temp = arr[i];
arr[i] = arr[random_num];
arr[random_num] = temp;
}
}
}

TimeLimitExceeded in finding maximum sum of a subarray

This is my solution for finding out the maximum sum of a subarray for a given array using Segment Tree (http://www.spoj.com/problems/GSS1/) but I am getting TLE (Time Limit Exceeded error) for this. Can anyone suggest more optimizations for this code?
public class segmentTree {
int st[];
segmentTree(int arr[], int n){
int x = (int)Math.ceil(Math.log(n)/Math.log(2));
int maxSize = (int)Math.pow(2, x)<<1 - 1;
st = new int[maxSize];
constructSTUtil(arr, 0, n-1, 0);
}
int getMid(int s, int e) {
return s + (e - s)>>1;
}
int getSumUtil(int ss, int se, int qs, int qe, int si)
{
if (qs <= ss && qe >= se)
return st[si];
if (se < qs || ss > qe)
return 0;
int mid = getMid(ss, se);
return getSumUtil(ss, mid, qs, qe, si<<1 + 1) +
getSumUtil(mid + 1, se, qs, qe, si<<1 + 2);
}
int getSum(int n, int qs, int qe)
{
if (qs < 0 || qe > n - 1 || qs > qe) {
return -1;
}
return getSumUtil(0, n - 1, qs, qe, 0);
}
int constructSTUtil(int arr[], int ss, int se, int si)
{
if (ss == se) {
st[si] = arr[ss];
return arr[ss];
}
int mid = getMid(ss, se);
st[si] = constructSTUtil(arr, ss, mid, si<<1 + 1) +
constructSTUtil(arr, mid + 1, se, si<<1 + 2);
return st[si];
}
public static void main(String[] args) throws IOException {
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(in.readLine().trim());
int array[] = new int[n];
String arr[] = new String[n];
arr = in.readLine().split(" ");
for(int i=0; i<n; i++){
array[i] = Integer.parseInt(arr[i].trim());
}
segmentTree tree = new segmentTree(array, n);
int m = Integer.parseInt(in.readLine());
while(m-- > 0){
String ind[] = new String[2];
ind = in.readLine().split(" ");
int x = Integer.parseInt(ind[0].trim());
int y =Integer.parseInt(ind[1].trim());
int maxSum = -999999999;
for(int i=x-1; i<=y-1; i++){
for(int j=x-1; j<=y-1; j++){
if(tree.getSum(n, i, j) > maxSum)
maxSum = tree.getSum(n, i, j);
}
}
System.out.println(maxSum);
}
}
}

MergeSort shows index out of bound

I am creating a mergesort method in Java, but I can't seem to know what's wrong. I have tried .add and .set, but it doesn't seem to work. From what I can see in the debugger, the numbers are correct. Please help me!
mergesort(list, 0, list.size() - 1);
private void mergesort(ArrayList<Integer> list, int l, int h) {
if (l < h) {
int m = (l + h) / 2;
mergesort(list, l, m);
mergesort(list, m + 1, h);
merge(list, l, m, h);
}
}
Here is my merge method.
private void merge(ArrayList<Integer> list, int low, int middle, int high) {
ArrayList<Integer> temp = new ArrayList<Integer>();
int low_end = (middle - 1);
int tmp_pos = low;
int num_elements = (high - low + 1);
while ((low <= low_end) && (middle <= high)) {
if (list.get(low) <= list.get(middle)) {
temp.add(tmp_pos++, list.get(low++));
}
else {
temp.add(tmp_pos++, list.get(middle++));
}
}
while (low <= low_end) {
temp.add(tmp_pos++, list.get(low++));
}
while (middle <= high) {
temp.add(tmp_pos++, list.get(middle++));
}
for (int i = 0; i < num_elements; i++) {
list.set(high, temp.get(high));
high--;
}
}
Any help appreciated.

Java: Is Merge-Sort is O(N^2) or O(N Log(N))

I created my own implementation of merge sort, I tested it that it works. How ever I am not sure if it's O(N Log(N)) as it should be, or it's O(N^2), can you please look at my code and tell?
SortedList
public abstract class SortedList {
public final ArrayList<Integer> params = new ArrayList<Integer>();
public void add(int... params) {
for (int parameter : params) {
this.params.add(parameter);
}
}
abstract public void sort();
public void print() {
for (int parameter : params) {
System.out.print(parameter + " ");
}
System.out.println();
}
}
MargeSort
public class MargeSort extends SortedList{
private int buffer[];
#Override
public void sort() {
buffer = new int[params.size()];
for(int i = 1; i < params.size(); i *= 2){
sort(i);
}
}
private void sort(int interval) {
for(int i = 0; i < params.size() - interval; i += interval * 2){
sort(i, i + interval, interval);
}
}
private void sort(int index1, int index2, int interval) {
int startingIndex = index1;
int index1MaxValue = index1 + interval;
int index2MaxValue = index2 + interval;
if(index2MaxValue >= params.size()){
index2MaxValue = params.size();
}
int counter = 0;
for(counter = 0; index1 < index1MaxValue && index2 < index2MaxValue; counter++){
int param1 = params.get(index1);
int param2 = params.get(index2);
if(param1 < param2){
buffer[counter] = param1;
index1++;
}
else{
buffer[counter] = param2;
index2++;
}
}
int index, indexMaxValue;
if(index1 < index1MaxValue){
index = index1;
indexMaxValue = index1MaxValue;
}
else{
index = index2;
indexMaxValue = index2MaxValue;
}
while(index < indexMaxValue){
int param = params.get(index);
buffer[counter] = param;
index++;
counter++;
}
for(int i = 0; i < interval * 2 && i + startingIndex < params.size(); i++){
params.set(i + startingIndex, buffer[i]);
}
}
}
sort(int) is called lg N times, where N = params.size(). [lg N here and everywhere further means ceil(lg N)]
Loop in sort(int) loops N / (interval / 2) times, where interval in [1 .. lgN], calling sort(...), which takes nr of steps, which is lineary depends on its' interval arg.
So, nr of steps is:
Sigma(k in from 1 to lgN): (N / (interval / 2)) * (C * interval) = C * N/2 * Sigma(1..lgN) 1 = C * N * lgN /2
[ C is constant for accounting of inner sort(...) cost ]

Segment Tree Codechef TLE

I am trying to solve this CodeChef problem:
There are N coins kept on the table, numbered from 0 to N - 1. Initially, each coin is kept tails up.
You have to perform two types of operations:
Flip all coins numbered between A and B inclusive. This is represented by the command "0 A B"
Answer how many coins numbered between A and B inclusive are heads up. This is represented by the command "1 A B".
Input: The first line contains two integers, N and Q. Each of the next Q lines are either of the form "0 A B" or "1 A B" as mentioned above.
Output: Output 1 line for each of the queries of the form "1 A B" containing the required answer for the corresponding query.
What I have used is a segment tree. So that every time user enter a query of type 1 A B the output is the sum at that interval [A,B]. However I am getting a Time Limit Exceeded error. I believe the error is due to the update step 0 A B. After updating the elements in the array I reconstruct the tree. The code is given below. Can someone help me with a faster way to update?
BTW - I am getting the desired output for the sample input.
public class SegmentTree
{
private int[] tree;
private int maxsize;
private int height;
private static int elems[];
private final int STARTINDEX = 0;
private final int ENDINDEX;
private final int ROOT = 0;
public SegmentTree(int size)
{
height = (int)(Math.ceil(Math.log(size) / Math.log(2)));
maxsize = 2 * (int) Math.pow(2, height) - 1;
tree = new int[maxsize];
ENDINDEX = size - 1;
}
private int leftchild(int pos)
{
return 2 * pos + 1;
}
private int rightchild(int pos)
{
return 2 * pos + 2;
}
private int mid(int start, int end)
{
return (start + (end - start) / 2);
}
private int getSumUtil(int startIndex, int endIndex, int queryStart, int queryEnd, int current)
{
if (queryStart <= startIndex && queryEnd >= endIndex)
{
return tree[current];
}
if (endIndex < queryStart || startIndex > queryEnd)
{
return 0;
}
int mid = mid(startIndex, endIndex);
return getSumUtil(startIndex, mid, queryStart, queryEnd, leftchild(current))
+ getSumUtil( mid + 1, endIndex, queryStart, queryEnd, rightchild(current));
}
public int getSum(int queryStart, int queryEnd)
{
if(queryStart < 0 || queryEnd > tree.length)
{
return -1;
}
return getSumUtil(STARTINDEX, ENDINDEX, queryStart, queryEnd, ROOT);
}
private int constructSegmentTreeUtil(int startIndex, int endIndex, int current)
{
if (startIndex == endIndex)
{
tree[current] = elems[startIndex];
return tree[current];
}
int mid = mid(startIndex, endIndex);
tree[current] = constructSegmentTreeUtil(startIndex, mid, leftchild(current))
+ constructSegmentTreeUtil(mid + 1, endIndex, rightchild(current));
return tree[current];
}
public void constructSegmentTree()
{
constructSegmentTreeUtil(STARTINDEX, ENDINDEX, ROOT);
}
public static void main(String[]args) throws IOException
{
BufferedReader buf = new BufferedReader(new InputStreamReader(System.in));
StringTokenizer str = new StringTokenizer(buf.readLine());
int n = Integer.parseInt(str.nextToken());
int q = Integer.parseInt(str.nextToken());
SegmentTree segmentTree = new SegmentTree(n);
int elements[] = new int[n];
for(int i = 0; i < n; i++) {
elements[i] = 0;
}
elems = elements;
segmentTree.constructSegmentTree();
while (q-- > 0) {
str = new StringTokenizer(buf.readLine());
int x = Integer.parseInt(str.nextToken());
int a = Integer.parseInt(str.nextToken());
int b = Integer.parseInt(str.nextToken());
if(x == 0) {
for(int j = a; j <= b; j++)
{
elems[j] = elems[j]^1;
}
segmentTree.constructSegmentTree();
}
else {
int num = segmentTree.getSum(a, b);
System.out.println(num);
}
}
}
}
EDIT:
According to GeeksForGeeks, tree construction costs O(n) and the update method is O(log n). So here are the new methods for update:
private void updateTreeUtil(int startIndex, int endIndex, int updatePos, int update, int current)
{
if ( updatePos < startIndex || updatePos > endIndex)
{
return;
}
tree[current] = tree[current] + update;
if (startIndex != endIndex)
{
int mid = mid(startIndex, endIndex);
updateTreeUtil(startIndex, mid, updatePos, update, leftchild(current));
updateTreeUtil(mid+1, endIndex, updatePos, update, rightchild(current));
}
}
public void update(int update, int updatePos)
{
int updatediff = update - elems[updatePos];
elems[updatePos] = update;
updateTreeUtil(STARTINDEX, ENDINDEX, updatePos, updatediff, ROOT);
}
And now the if loop in main method modified to this:
if(x == 0) {
for(int j = a; j <= b; j++)
{
segmentTree.update(elems[j]^1, j);
}
}
But still getting TLE error.
In the tutorial of GeeksForGeeks, their running time of update is O(log n), in case of updating a single element. However, when doing update for an interval, you have to use Lazy Propagation to ensure O(log n) update time, which is basically only update nodes which are visited, and hence ensure the sum of visited nodes are correct. You may search for many good tutorial on Lazy Propagation, for example:
http://se7so.blogspot.hk/2012/12/segment-trees-and-lazy-propagation.html
Wish that helps.

Categories