How to know complexity of finding kth largest element in specific implementation - java

I am trying to teach myself order statistics by solving the problem
find the kth largest element in an array in O(n) time.
My Java implementation is as follows below.
Qn: I am unsure of how to determine the complexity of my code. From what I understand, it does not exceed n. Is this correct? Or how should I get this?
Have adapted the algorithm from pg 215, Intro to Algo MIT Press.
package intp;
public class IntP {
public static void main(String[] args) {
int[] qn = {10,22,33,4,5,6,1};
int[] res = {0,0};
int q;
int k =3;
q=k;
while (k>=1){
res = findMax(qn,k);
qn[res[1]]=0;
k=k-1;
}
System.out.println("Largest element number "+q+ " is: "+res[0]);
}
public static int[] findMax(int[] a,int k){
int pos=0;
int max = a[0];
int[] ans= {0,0};
for(int i= 1;i<a.length;i+=2){
if (i+1==a.length){
if (a[i]>max){
max=a[i];
pos=i;
}
break;
}
if (a[i]>a[i+1] && a[i]>max){
max=a[i];
pos=i;
}
else if (a[i+1]>max && a[i+1]>max){
max= a[i+1];
pos=i+1;
}
}
ans[0]=max;
ans[1]= pos;
return ans;
}
}

First Time complexity of findMax:
Time(findMax) = 3 + 1/2 n * (4 + 2) + 1 + 3
Time(findMax) = 3 n + 7
Time(findMax) ~ n
Time(findMax) ~ O(n)
Then Time complexity of main:
Time(main) = 5 + k * (3 + Time(findMax))
Time(main) = k * (3 n + 10) + 5
Time(main) = 3 k n + 10 k + 5
Time(main) ~ k * Time(findMax)
Time(main) ~ O(kn)
Note: I considered any managed instruction as 1 operation

Repetitive selection of the maximum is a poor way to implement selection of the Kth (except maybe for very small K). It takes worst-case time O(KN).
A better approach is to sort the array, for example using Quicksort, performing in expected O(N.Lg(N)). Anyway, the worst-case time is quadratic, O(N²).
A bit better, Quickselect, a "stripped-off" version of Quicksort. Closer to linear time O(N), but it keeps the worst-case O(N²).
The truly optimal approach (in the asymptotic sense) is that of the Median of Medians, with a guaranteed O(N) behavior.
My preferred implementation of the naïve approach:
for (i= 0; i < n; i++) // Try every element a[i]
{
int r= 0;
for (j= 0; j < n; j++) // Evaluate the rank by counting inferior elements
{
r+= a[j] < a[i] || (a[j] == a[i] && j < i); // Mind the equal elements
}
if (r == k) // Desired rank
return i;
}

Related

Why would a quadratic time algorithm execute faster than a linear time algorithm

Here are two different solutions for finding "Number of subarrays having product less than K", one with runtime O(n) and the other O(n^2). However, the one with O(n^2) finished executing about 4x faster than the one with linear runtime complexity (1s vs 4s). Could someone explain why this is the case, please?
Solution 1 with O(n) runtime:
static long countProductsLessThanK(int[] numbers, int k)
{
if (k <= 1) { return 0; }
int prod = 1;
int count = 0;
for (int right = 0, left = 0; right < numbers.length; right++) {
prod *= numbers[right];
while (prod >= k)
prod /= numbers[left++];
count += (right-left)+1;
}
return count;
}
Solution 2 with O(n^2) runtime:
static long countProductsLessThanK(int[] numbers, int k) {
long count = 0;
for (int i = 0; i < numbers.length; i++) {
int productSoFar = 1;
for (int j = i; j < numbers.length; j++) {
productSoFar *= numbers[j];
if (productSoFar >= k)
break;
count++;
}
}
return count;
}
Sample main program:
public static void main(String[] args) {
int size = 300000000;
int[] numbers = new int[size];
int bound = 1000;
int k = bound/2;
for (int i = 0; i < size; i++)
numbers[i] = (new Random().nextInt(bound)+2);
long start = System.currentTimeMillis();
System.out.println(countProductLessThanK(numbers, k));
System.out.println("O(n) took " + ((System.currentTimeMillis() - start)/1000) + "s");
start = System.currentTimeMillis();
System.out.println(countMyWay(numbers, k));
System.out.println("O(n^2) took " + ((System.currentTimeMillis() - start)/1000) + "s");
}
Edit1:
The array size I chose in my sample test program has 300,000,000 elements.
Edit2:
array size: 300000000:
O(n) took 4152ms
O(n^2) took 1486ms
array size: 100000000:
O(n) took 1505ms
O(n^2) took 480ms
array size: 10000:
O(n) took 2ms
O(n^2) took 0ms
The numbers you are choosing are uniformly distributed in the range [2, 1001], and you're counting subarrays whose products are less than 500. The probability of finding a large subarray is essentially 0; the longest possible subarray whose products is less than 500 has length 8, and there are only nine possible subarrays of that length (all 2s, and the eight arrays of seven 2s and a 3); the probability of hitting one of those is vanishingly small. Half of the array values are already over 500; the probability of finding even a length two subarray at a given starting point is less than one-quarter.
So your theoretically O(n²) algorithm is effectively linear with this test. And your O(n) algorithm requires a division at each point, which is really slow; slower than n multiplications for small values of n.
In the first one, you're dividing (slow), multiplying and doing multiple sums.
In the second one, the heavier operation is multiplication, and as the first answer says, the algorithm is effectively linear for your tests cases.

O(n log n) Time Complexity Algorithm?

I created this algorithm to find the best trade between 3 numbers. It goes through the program and finds the best day to sell, buy, and profit from stock. I need to explain the algorithm used and how the time complexity is O(n log n) but I have a lot of trouble determining that. I was hoping someone could explain O(n log n) and relate it to the method I have.
Here's my method:
public static Trade bestTrade(int[] a)
{
int lowest = a[0];
int lowestIndex = 0;
int highest = a[a.length - 1];
int highestIndex = a.length - 1;
int profit = 0;
for(int i = 1; i < a.length; i++)
{
if (a[i] < lowest && i < highestIndex)
{
lowest = a[i];
lowestIndex = i;
}
}
for(int i = a.length - 2; i >= 0; i--)
{
if (a[i] > highest && i > lowestIndex)
{
highest = a[i];
highestIndex = i;
}
}
for(int i = 1; i < a.length; i++)
{
if (a[i] < lowest && i < highestIndex)
{
lowest = a[i];
lowestIndex = i;
}
}
if (highestIndex > lowestIndex)
{
profit = highest - lowest;
return new Trade(lowestIndex, highestIndex, profit);
}
return new Trade(lowestIndex, highestIndex, profit);
}
}
This function is of O(n) which is superior to O(n log n) .
In general you just look at the loops, since there is no nested loops and you only have loops which go through all elements of a The function is considered n.
The complexity is O(n), where n the length of array a.
You loop 3 times over a, so the running time is roughly 3n, so it is of the order n: O(n).
Try finding the answer to this by yourself. It will help a lot in the future. Also this looks like a O(N) , I am not sure why you are convinced that it is O(NlogN).
This link might be useful,
http://pages.cs.wisc.edu/~vernon/cs367/notes/3.COMPLEXITY.html
O(n)
It is directly proportional to the number of a.length. Each time the for function is run, it runs through every day of data. If there were a method where the number of processes went up by more than the pure number (nested fors) then it could be O(n log n) or O(n^2). But in this case, it's pretty clearly just big O of n.

Can this solution be optmized? [duplicate]

This question already has answers here:
Linear time algorithm for 2-SUM
(13 answers)
Closed 7 years ago.
Given an array say L= [3,4,6,7,2,1] and an Integer Z = 8, find 2 integers X,Y belonging to L such that X + Y = Z
Here is one possible solution -
public static void findIntegersSum(List<Integer> list, int z) {
for(int i = 0 ;i < list.size(); i++) {
for(int j = i+1 ; j< list.size(); j++) {
if(list.get(i) + list.get(j) == z) {
System.out.println(" X = " + list.get(i) + "\t Y=" + list.get(j));
return;
}
}
}
System.out.println(" No match found !!");
}
Question is - Can we optimize the above solution?
You can do this using an implementation of the Set data type. A Set is most often implemented as either a Hash table (HashSet in Java) or a Binary Search Tree (TreeSet in Java). Hash table implementations often use more memory, but have constant time O(1) lookups and insertions in the average case, tree implementations use linear memory, but have O(log n) lookups and insertions in the average case.
Pseudocode for using this data structure is as follows:
S = set(L) // Iterate through L and put it in a set, O(n) or O(n log n)
for element in L: // O(n)
if S.has(Z - element): // O(1) or O(log n) depending on HashSet or TreeSet
X = element
Y = Z - element
break
This solution is O(n) or O(n log n) instead of O(n^2).
This method can be solved on O(n) time if we are allowed to use additional space of O(R) where R is range of numbers. Here is the implementation
#include <stdio.h>
#define MAX 100000
void printPairs(int arr[], int arr_size, int sum)
{
int i, temp;
bool binMap[MAX] = {0}; /*initialize hash map as 0*/
for(i = 0; i < arr_size; i++)
{
temp = sum - arr[i];
if(temp >= 0 && binMap[temp] == 1)
{
printf("Pair with given sum %d is (%d, %d) \n", sum, arr[i], temp);
}
binMap[arr[i]] = 1;
}
}
/* Driver program to test above function */
int main()
{
int A[] = {1, 4, 45, 6, 10, 8};
int n = 16;
int arr_size = 6;
printPairs(A, arr_size, n);
getchar();
return 0;
}
Sort it, afterwards you only have to iterate over the array once, and check whether the matching value is in the array. This will only be more efficient for big arrays.
Following solution will be more optimized, as it is reduce List size at every steps.
static void findIntegersSum(List<Integer> list, int z) {
for(int i = 0 ;i < list.size(); i++) {
int X = list.remove(i); // Reduce your list
int Y = z - X;
if (list.contains(Y)) {
System.out.println(" X = " + X + "\t Y=" + Y);
return;
}
}
System.out.println(" No match found !!");
}

Improving the algorithm for removal of element

Problem
Given a string s and m queries. For each query delete the K-th occurrence of a character x.
For example:
abcdbcaab
5
2 a
1 c
1 d
3 b
2 a
Ans abbc
My approach
I am using BIT tree for update operation.
Code:
for (int i = 0; i < ss.length(); i++) {
char cc = ss.charAt(i);
freq[cc-97] += 1;
if (max < freq[cc-97]) max = freq[cc-97];
dp[cc-97][freq[cc-97]] = i; // Counting the Frequency
}
BIT = new int[27][ss.length()+1];
int[] ans = new int[ss.length()];
int q = in.nextInt();
for (int i = 0; i < q; i++) {
int rmv = in.nextInt();
char c = in.next().charAt(0);
int rr = rmv + value(rmv, BIT[c-97]); // Calculating the original Index Value
ans[dp[c-97][rr]] = Integer.MAX_VALUE;
update(rmv, 1, BIT[c-97], max); // Updating it
}
for (int i = 0; i < ss.length(); i++) {
if (ans[i] != Integer.MAX_VALUE) System.out.print(ss.charAt(i));
}
Time Complexity is O(M log N) where N is length of string ss.
Question
My solution gives me Time Limit Exceeded Error. How can I improve it?
public static void update(int i , int value , int[] arr , int xx){
while(i <= xx){
arr[i ]+= value;
i += (i&-i);
}
}
public static int value(int i , int[] arr){
int ans = 0;
while(i > 0){
ans += arr[i];
i -= (i &- i);
}
return ans ;
}
There are key operations not shown, and odds are that one of them (quite likely the update method) has a different cost than you think. Furthermore your stated complexity is guaranteed to be wrong because at some point you have to scan the string which is at minimum O(N).
But anyways the obviously right strategy here is to go through the queries, separate them by character, and then go through the queries in reverse order to figure out the initial positions of the characters to be suppressed. Then run through the string once, emitting characters only when it fits. This solution, if implemented well, should be doable in O(N + M log(M)).
The challenge is how to represent the deletions efficiently. I'm thinking of some sort of tree of relative offsets so that if you find that the first deletion was 3 a you can efficiently insert it into your tree and move every later deletion after that one. This is where the log(M) bit will be.

I want to calculate total number of iterations a code will take

This is my code for a simple selection sort.usually the complexity (time) for a sort is number of iterations it has taken for sorting O(n^2) in case of selection sort
When I dry ran this code against sample string of 98765, it gave me 25 iterations.
Just to cross check with my dry ran output i put 2 vbl- noi and noj in my code.
Q: will the number of total iterations be = noi*noj or noi+noj;
int index = 0; int noi = 0, noj = 0;
for (j = 0; j < 5; j++)
{
noj++;
index = j;
for (i = j; i < 5; i++)
{
if (a[index] > a[i])
{
a[index] = a[index] + a[i];
a[i] = a[index] - a[i];
a[index] = a[index] - a[i];
noi++;
}
}
}
number of iterations is always 15 (5+4+3+2+1) because in your loops there are j<5 and i<5. So your code complexity is O(n^0) because in your case n is 5
Complexity doesn't depend from n because there's no n. The complexity is always exactly 15 (1+2+3+4+5 as said shift66)
it is: noj [for first loop] + (( noj * (noj + 1) ) / 2) [for inner loop]
as first loops is from 1-noj and second is j-noj (where j depends on first loop)

Categories

Resources