I'm writing an algorithm that will return an array with determined length and number of inversions (number pairs, where the left side number is larger than the right side number). I.e. array [3, 1, 4, 2] contains three inversions (3, 1), (3, 2) and (4, 2). So in practice, when given the length of n=3 and number of inversions k=3, the algorithm should generate an array [3, 1, 4, 2] (or another array that fulfills these requirements).
Since the number of inversions is also the number of swaps that has to be made for the array to be sorted in ascending order, I approached this problem by creating an array from 1 to n - 1 and using an insertion sort algorithm in reverse to make k swaps.
This approach works just fine for smaller inputs, but the algorithm should be able to efficiently generate arrays up to n=10^6 and k=n(n-1)/2 and anything in between, so the algorithm should be working in O(n log n) time instead of O(n^2). Below is the code:
import java.util.*;
public class Inversions {
public int[] generate(int n, long k) {
// Don't mind these special cases
if (n == 1) {
int[] arr = {1};
return arr;
}
if (k == 0) {
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = 1;
}
return arr;
}
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = i + 1;
}
int inversions = 0;
int i = 0;
while (inversions < k && i < n) {
int j = i - 1;
while (j >= 0 && arr[j] < arr[j + 1] && inversions < k) {
int helper = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = helper;
inversions++;
j--;
}
i++;
}
return arr;
}
}
And the main class for testing with different input arrays:
public class Main {
public static void main(String[] args) {
Inversions in = new Inversions();
int[] arr1 = in.generate(4,3);
int[] arr2 = in.generate(4,0);
int[] arr3 = in.generate(4,6);
System.out.println(Arrays.toString(arr1)); // [3,1,4,2]
System.out.println(Arrays.toString(arr2)); // [1,1,1,1]
System.out.println(Arrays.toString(arr3)); // [4,3,2,1]
}
}
The algorithm does not return exactly the same arrays as the sample results, but passes all the tests, except the ones where the input size is very large. I have also tried different variations with merge sort, since it's working in O(n log n time) but with no avail.
It would be great if you guys have some ideas. If you are not familiar with Java, doesn't matter, pseudocode or any other kinds of suggestions are more than welcome!
How many inversions are there in the array arr = {1,5,4,2,3}? Explanation: The necessary condition for an inversion is arr[i]>arr[j] and i<j. So there are 5 inversions in the array.
N(N-1)/3.
B. The array with elements from the set 1 , 2 , … , n {1, 2, \dots , n} 1,2,…,n with the most inversions will have the elements in reverse sorted order, i.e. ⟨ n , n − 1 , … , 2 , 1 ⟩ \langle n, n - 1, \dots, 2, 1 \rangle ⟨n,n−1,…,2,1⟩.
If you reverse the initial m elements in the array, you create m(m-1)/2 inversions.
If you reverse the initial m+1 elements, you create m(m+1)/2 inversions.
The difference between these is only m.
So:
This takes O(n) time, which is better than you require.
Another O(n)
algorithm: Start with a sorted array. When you swap the first and last elements, you get x = 2 * (n-2) + 1
inversions. Consider these two elements fixed and work on the remaining array only. If x
is too large, consider a smaller array. Repeat this as long as needed.
Untested code:
for (int first=0, last = n-1; remainingInversions>0; ) {
int x = 2 * (last-first-1) + 1;
if (x <= remainingInversion) {
first++;
last--;
remainingInversion -= x;
} else {
last--; // consider a smaller array
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With