Finding the longest down sequence in a Java array - java

Given this array
int [] myArray = {5,-11,2,3,14,5,-14,2};
I must be able to return 3 because the longest down sequence is 14,5,-14.
What's the fastest way to do this?
PS: Down sequence is a series of non-increasing numbers.

another implementation in python:
def longest_down_sequence(seq):
max = 0
current_count = 0
last = None
for x in seq:
if x <= last: current_count += 1
else: current_count = 1
if current_count > max: max = current_count
last = x
return max

Just make one pass through the list of numbers. Pseudocode:
bestIndex = 0
bestLength = 0
curIndex = 0
curLength = 1
for index = 1..length-1
if a[index] is less than or equal to a[index-1]
curLength++
else
//restart at this index since it's a new possible starting point
curLength = 1
curIndex = index
if curLength is better than bestLength
bestIndex = curIndex
bestLength = curLength
next
Note: You can ditch any line containing bestIndex or curIndex if you don't care about knowing where that subsequence occurs, as seen in Gary's implementation.

In java:
int [] myArray = {5,-11,2,3,14,5,-14,2};
int downSequence = 1;
int longestDownSequence = 1;
for(int i = 1; i < myArray.length; i++) {
if(myArray[i] <= myArray[i-1]) downSequence++;
else {
if(downSequence > longestDownSequence)
longestDownSequence = downSequence;
downSequence = 1;
}
}
if(downSequence > longestDownSequence)
longestDownSequence = downSequence;
System.out.println(longestDownSequence);
Since you're asking for fastest or better performance, only check for the longest down sequence just before you reset the counter. Never on each iteration. However, you have to check again after the loop in case the longest sequence is at the end of the array.

Another solution in Java:
static int[] longestDownSequenceList(int[] array) {
if (array.length <= 1) {
return array;
}
int maxSize = 1;
int maxEnd = 0;
int curSize = 1;
for (int i = 1; i < array.length; i++) {
if (array[i] < array[i-1]) {
curSize++;
if (curSize > maxSize) {
maxSize = curSize;
maxEnd = i;
}
}
else {
curSize = 1;
}
}
return Arrays.copyOfRange(array, maxEnd-maxSize+1, maxEnd+1);
}

As Bill above said, this is essentially longest increasing subsequence. See the wikipedia entry for the optimal solution. This is quoted from there with small changes to work for the nondecreasing case
L = 0
for i = 1, 2, ... n:
binary search for the largest positive j ≤ L such that X[M[j]] >= X[i] (or set j = 0 if no such value exists)
P[i] = M[j]
if j == L or X[i] >= X[M[j+1]]:
M[j+1] = i
L = max(L, j+1)
See counterexample to other proposed solution in my comment above.

The fastest way might depend on the environment: computer and problemsize.
For a very large List (or array) it might be useful to parallelize the job, which could be implemented:
Split and split and split the List to simple elements.
Glue together elements which are down sequences (or non increasing) to chunks, and glue together chunks, if possible.
Search for the longest chunk.

Related

Array Sort by Remainder

A interviewer wrecked my life with this sort question today.
You have an array of a million integers and you need to sort them by remainder.
1. You don't know what integer they are going to divide by.
2. You can't use classes such as a Comparator to help.
3. Loop as little as possible.
4. Keep in mind to conserve memory.
For instance
int[] ints = {5434, 3454, 2, 0, 356, 896, 7324, 888, 99, 78365, 111};
int divider = 27;
Would be
int[] int2 = {0, 2, 111, 356, 896, 5434, 7324, 78365, 99, 888, 3454};
The method I came up with loops = divider / 2.
It works but if the divider is 250 then it loops 125 times.
public void sortByMod(int[] millionInts, int divideBy) {
long time = System.nanoTime();
int[] b = new int[millionInts.length];
int remainder, remainderMin = 0, remainderMax = divideBy, positionMin = 0, positionMax = millionInts.length - 1;
for (int i = 0; i < millionInts.length;) {
for (int j = 0; j < millionInts.length; j++) {
remainder = millionInts[j] % divideBy;
if (remainder == remainderMin) {
b[positionMin] = millionInts[j];
positionMin++;
i++;
} else if (remainder == remainderMax) {
b[positionMax] = millionInts[j];
positionMax--;
i++;
}
}
remainderMax--;
remainderMin++;
}
System.out.println("time = " + (System.nanoTime() - time));
System.out.println("loopcount = " + remainderMin);
}
I wrote another method that can do it in 2 loops but its confusing to read.
It violates the memory constraint but is extremely fast.
public void sortByModPro(int[] millionInts, int divideBy) {
int[] range = new int[divideBy];
int[] remainders = new int[millionInts.length];
int[] newArray = new int[millionInts.length];
long times = System.nanoTime();
for (int i = 0; i < millionInts.length; i++) {
remainders[i] = millionInts[i] % divideBy;
range[millionInts[i] % divideBy]++;
}
for (int i = range.length - 1, past = millionInts.length; i >= 0; i--) {
range[i] = past - range[i];
past = range[i];
}
for (int i = 0; i < millionInts.length; i++) {
newArray[range[remainders[i]]] = millionInts[i];
range[remainders[i]]++;
}
System.out.println("time = " + (System.nanoTime() - times));
}
How would you do this with 1 loop?
Speed > Memory
You can loop only once by using a bunch of buckets, one for each remainder. Simply dump the numbers in the buckets based on their remainder and then merge the buckets. Of course this violates the memory constraint.
Use your array to hold the buckets
The problem with the buckets is that you need to at least add a reference to each item in the array. What you could do to avoid that is partition the array into buckets and maintain a reference to the start and end index of each bucket. Of course this uses some memory, but the divideBy parameter should be rather small, right?
So, here's some pseudocode:
// init each bucket with 0 elements
for (remainder=0; remainder<divideBy; remainder++) {
buckets = {
start : 0, // startIndex in the array
end: 0, // the index after the last item actually placed in the bucket
count: 0 // how many items should be in the bucket
}
}
// count how many elements fit in each bucket
for (i=0; i<N; i++) {
buckets[array[i]%divideBy].count++;
}
// init the start and end points of each bucket
elementsCounted=0;
for (remainder=0; remainder<divideBy; remainder++) {
buckets[remainder].start = elementsCounted;
buckets[remainder].end = elementsCounted;
elementsCounted += buckets[remainder].count;
}
// at this point each bucket starts where it should in the array, but has no elements
// loop through the array and place items in the right bucket by swapping them
for (i=0; i<N; i++) {
remainder = array[i]%divideBy;
if (i < buckets[remainder].start || i >= buckets[remainder].end) {
// array[i] is in the wrong bucket, swap it at the end of the right bucket
swap(array[i], array[buckets[remainder].end]);
buckets[remainder].end++;
i--;
}
}
// everything is in the right place
You will note that there is an i-- in the final for, so it technically could go on forever. That is not the case, i will stay in place only if the array[i] is not in the right place. Each iteration will either place an element in the correct position or advance to the next position if the element is not in the correct position. All in all, it will iterate at most 2N times.
Total time complexity: O(3N+divideBy) = O(N + divideBy)
Total extra space used: divideBy*sizeof(bucket) = divideBy*12

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.

in Java, design linear algorithm that finds contiguous subsequence with highest sum

this is the question, and yes it is homework, so I don't necessarily want anyone to "do it" for me; I just need suggestions: Maximum sum: Design a linear algorithm that finds a contiguous subsequence of at most M in a sequence of N long integers that has the highest sum among all such subsequences. Implement your algorithm, and confirm that the order of growth of its running time is linear.
I think that the best way to design this program would be to use nested for loops, but because the algorithm must be linear, I cannot do that. So, I decided to approach the problem by making separate for loops (instead of nested ones).
However, I'm really not sure where to start. The values will range from -99 to 99 (as per the range of my random number generating program).
This is what I have so far (not much):
public class MaxSum {
public static void main(String[] args){
int M = Integer.parseInt(args[0]);
int N = StdIn.readInt();
long[] a = new long[N];
for (int i = 0; i < N; i++) {
a[i] = StdIn.readLong();}}}
if M were a constant, this wouldn't be so difficult. For example, if M==3:
public class MaxSum2 {
public static void main(String[] args){
int N = StdIn.readInt(); //read size for array
long[] a = new long[N]; //create array of size N
for (int i = 0; i < N; i++) { //go through values of array
a[i] = StdIn.readLong();} //read in values and assign them to
//array indices
long p = a[0] + a[1] + a[2]; //start off with first 3 indices
for (int i =0; i<N-4; i++)
{if ((a[i]+a[i+1]+a[1+2])>=p) {p=(a[i]+a[i+1]+a[1+2]);}}
//if sum of values is greater than p, p becomes that sum
for (int i =0; i<N-4; i++) //prints the subsequence that equals p
{if ((a[i]+a[i+1]+a[1+2])==p) {StdOut.println((a[i]+a[i+1]+a[1+2]));}}}}
If I must, I think MaxSum2 will be acceptable for my lab report (sadly, they don't expect much). However, I'd really like to make a general program, one that takes into consideration the possibility that, say, there could be only one positive value for the array, meaning that adding the others to it would only reduce it's value; Or if M were to equal 5, but the highest sum is a subsequence of the length 3, then I would want it to print that smaller subsequence that has the actual maximum sum.
I also think as a novice programmer, this is something I Should learn to do. Oh and although it will probably be acceptable, I don't think I'm supposed to use stacks or queues because we haven't actually covered that in class yet.
Here is my version, adapted from Petar Minchev's code and with an important addition that allows this program to work for an array of numbers with all negative values.
public class MaxSum4 {
public static void main(String[] args)
{Stopwatch banana = new Stopwatch(); //stopwatch object for runtime data.
long sum = 0;
int currentStart = 0;
long bestSum = 0;
int bestStart = 0;
int bestEnd = 0;
int M = Integer.parseInt(args[0]); // read in highest possible length of
//subsequence from command line argument.
int N = StdIn.readInt(); //read in length of array
long[] a = new long[N];
for (int i = 0; i < N; i++) {//read in values from standard input
a[i] = StdIn.readLong();}//and assign those values to array
long negBuff = a[0];
for (int i = 0; i < N; i++) { //go through values of array to find
//largest sum (bestSum)
sum += a[i]; //and updates values. note bestSum, bestStart,
// and bestEnd updated
if (sum > bestSum) { //only when sum>bestSum
bestSum = sum;
bestStart = currentStart;
bestEnd = i; }
if (sum < 0) { //in case sum<0, skip to next iteration, reseting sum=0
sum = 0; //and update currentStart
currentStart = i + 1;
continue; }
if (i - currentStart + 1 == M) { //checks if sequence length becomes equal
//to M.
do { //updates sum and currentStart
sum -= a[currentStart];
currentStart++;
} while ((sum < 0 || a[currentStart] < 0) && (currentStart <= i));
//if sum or a[currentStart]
} //is less than 0 and currentStart<=i,
} //update sum and currentStart again
if(bestSum==0){ //checks to see if bestSum==0, which is the case if
//all values are negative
for (int i=0;i<N;i++){ //goes through values of array
//to find largest value
if (a[i] >= negBuff) {negBuff=a[i];
bestSum=negBuff; bestStart=i; bestEnd=i;}}}
//updates bestSum, bestStart, and bestEnd
StdOut.print("best subsequence is from
a[" + bestStart + "] to a[" + bestEnd + "]: ");
for (int i = bestStart; i<=bestEnd; i++)
{
StdOut.print(a[i]+ " "); //prints sequence
}
StdOut.println();
StdOut.println(banana.elapsedTime());}}//prints elapsed time
also, did this little trace for Petar's code:
trace for a small array
M=2
array: length 5
index value
0 -2
1 2
2 3
3 10
4 1
for the for-loop central to program:
i = 0 sum = 0 + -2 = -2
sum>bestSum? no
sum<0? yes so sum=0, currentStart = 0(i)+1 = 1,
and continue loop with next value of i
i = 1 sum = 0 + 2 = 2
sum>bestSum? yes so bestSum=2 and bestStart=currentStart=1 and bestEnd=1=1
sum<0? no
1(i)-1(currentStart)+1==M? 1-1+1=1 so no
i = 2 sum = 2+3 = 5
sum>bestSum? yes so bestSum=5, bestStart=currentStart=1, and bestEnd=2
sum<0? no
2(i)-1(currentStart)+1=M? 2-1+1=2 so yes:
sum = sum-a[1(curentstart)] =5-2=3. currentStart++=2.
(sum<0 || a[currentStart]<0)? no
i = 3 sum=3+10=13
sum>bestSum? yes so bestSum=13 and bestStart=currentStart=2 and bestEnd=3
sum<0? no
3(i)-2(currentStart)+1=M? 3-2+1=2 so yes:
sum = sum-a[1(curentstart)] =13-3=10. currentStart++=3.
(sum<0 || a[currentStart]<0)? no
i = 4 sum=10+1=11
sum>bestSum? no
sum<0? no
4(i)-3(currentStart)+1==M? yes but changes to sum and currentStart now are
irrelevent as loop terminates
Thanks again! Just wanted to post a final answer and I was slightly proud for catching the all negative thing.
Each element is looked at most twice (one time in the outer loop, and one time in the while loop).
O(2N) = O(N)
Explanation: each element is added to the current sum. When the sum goes below zero, it is reset to zero. When we hit M length sequence, we try to remove elements from the beginning, until the sum is > 0 and there are no negative elements in the beginning of it.
By the way, when all elements are < 0 inside the array, you should take only the largest negative number. This is a special edge case which I haven't written below.
Beware of bugs in the below code - it only illustrates the idea. I haven't run it.
int sum = 0;
int currentStart = 0;
int bestSum = 0;
int bestStart = 0;
int bestEnd = 0;
for (int i = 0; i < N; i++) {
sum += a[i];
if (sum > bestSum) {
bestSum = sum;
bestStart = currentStart;
bestEnd = i;
}
if (sum < 0) {
sum = 0;
currentStart = i + 1;
continue;
}
//Our sequence length has become equal to M
if (i - currentStart + 1 == M) {
do {
sum -= a[currentStart];
currentStart++;
} while ((sum < 0 || a[currentStart] < 0) && (currentStart <= i));
}
}
I think what you are looking for is discussed in detail here
Find the subsequence with largest sum of elements in an array
I have explained 2 different solutions to resolve this problem with O(N) - linear time.

Java: Parallelise loop and merge results for calculating entropy

I have an algorithm that does the following:
Given I have an array array of length n It's goal is to merge certain elements based on some condition (it this case entropy). It calculates the entropy e_all of the entire array and calculates the entropy e_merged of the array where element n and n+1 are merged. It does that for each pair of adjacent elements. The pair where the difference in e_all - e_merged is greatest are marged. If there is a merge, the algorithm is applied again on the new array with length n-1.
As you can see, this takes in the worst case n^2 - 1 iterations and if n is big it might take minutes or even hours to complete.
Therefore I was wondering how I can parallelise this algorithms. Basically it should be able calculate the entropies on i cores and when all the elements are evaluated the results should be merged and a conclusion can be drawn.
How can I do such a thing? Which kinds of code pieces or idea's must I implement for it to work this way? Or is there a better way?
public double[] applyAlgorithm(double[] array) {
boolean merging = false;
for (int i = 0; i < array.length - 1; i++) {
double[] entropy = getEntropy(array); // returns list of entropy for all adjacent intervals
int idx = 0;
double max = Double.NEGATIVE_INFINITY;
for (int j = 0; j < entropy.length; j++) {
if (entropy[j] > max) {
max = entropy[j];
idx = j;
}
}
if (max > 0) {
array = mergeAdjacentIntervals(array, idx); //merge intervals that have the max entropy, if the entropy is > 0
merging = true;
break;
}
}
if (merging) {
array = applyAlgorithm(array);
}
return array;
}
private double[] getEntropy(double[] array) {
double[] entropy = new double[array.length - 1];
double[] tempArray = new double[array.length - 1];
double baseEntropy = calculateEntropy(array);
for (int i = 0; i < entropy.length; i++) {
tempArray = mergeAdjacentIntervals(array, idx);
entropy[i] = baseEntropy - calculateEntropy(tempArray);
}
return entropy;
}

finding all prime numbers in a given range

I'm writing this Java program that finds all the prime numbers between a given range. Because I'm dealing with really big numbers my code seems to be not fast enough and gives me a time error. Here is my code, does anyone know to make it faster? Thanks.
import java.util.*;
public class primes2
{
private static Scanner streamReader = new Scanner(System.in);
public static void main(String[] args)
{
int xrange = streamReader.nextInt();
int zrange = streamReader.nextInt();
for (int checks = xrange; checks <= zrange; checks++)
{
boolean[] checkForPrime = Primes(1000000);
if (checkForPrime[checks])
{
System.out.println(checks);
}
}
}
public static boolean[] Primes(int n)
{
boolean[] isPrime = new boolean[n + 1];
if (n >= 2)
isPrime[2] = true;
for (int i = 3; i <= n; i += 2)
isPrime[i] = true;
for (int i = 3, end = sqrt(n); i <= end; i += 2)
{
if (isPrime[i])
{
for (int j = i * 3; j <= n; j += i << 1)
isPrime[j] = false;
}
}
return isPrime;
}
public static int sqrt(int x)
{
int y = 0;
for (int i = 15; i >= 0; i--)
{
y |= 1 << i;
if (y > 46340 || y * y > x)
y ^= 1 << i;
}
return y;
}
}
You'll get an enormous improvement just by changing this:
for (int checks = xrange; checks <= zrange; checks++)
{
boolean[] checkForPrime = Primes(1000000);
to this:
boolean[] checkForPrime = Primes(1000000);
for (int checks = xrange; checks <= zrange; checks++)
{
Your current code regenerates the sieve zrange - xrange + 1 times, but you actually only need to generate it once.
The obvious problem is that you're computing the primes up to 1000000 many time (zrange - xrange times). Another is that you dont need to compute the primes up to 1000000, you just need to check to primes up to zrange, so you're wasting time when zrange < 1000000, and getting a buffer overflow when zrange > 1000000.
You can start your inner loop from i*i, i.e. instead of for (int j = i * 3; j <= n; j += i << 1) you can write for (int j = i * i; j <= n; j += i << 1) for a minor speed-up.
Also, you have to be sure that your zrange is not greater than 1000000.
If xrange is much greater than sqrt(zrange), you can also split your sieve array in two, for an offset sieve scheme. The lower array will span from 2 to sqrt(zrange). The upper one will span from xrange to zrange. As you sieve your lower array, as each new prime becomes identified by it, inside your inner loop, in addition to marking the lower array up to its end also sieve the upper array. You will have to calcuate the starting offset for each prime i, and use the same step of 2*i as you do for the lower half. If your range is wider than a few primes, you will get speed advantage (otherwise just trial division by odds will suffice).
Another thing to try is, if evens > 2 are not primes anyway, why represent them in the array and waste half of the space? You can treat each i as representing an odd number, 2*i+1, thus compressing your array in half.
Last simple trick is to eliminate the multiples of 3 in advance as well, by marking ON not just odds (i.e. coprimes with 2), by { ... i+=2; ...}, but only coprimes with 2 and 3, by { ... i+=2; ... i+=4; ... } instead. Also, when marking OFF multiples of primes > 3, use { ... j+=2*i; ... j+=4i; ...} too. E.g., in 5*5, 5*7, 5*9, 5*11, ... you don't need to mark OFF 5*9, if no multiple of 3 was marked ON in the first place.

Categories

Resources