Find the maximum consecutive elements matching the given condition.
I have a list of numbers called A, another list called B and a limit called Limit.
The task is find the maximum k consecutive elements in A such that they satisfy below condition.
Max(B[i],B[i+1],...B[i+k]) + Sum(A[i], A[i+1], ..., A[i+k]) * k ≤ Limit
Example:
A = [2,1,3,4,5]
B = [3,6,1,3,4]
Limit = 25
Take 2 consecutive elements:
Highest sum occurs with elements in A = 4,5. The corresponding max in B is Max(3,4) = 4.
So value = 4 + (4+5) * 2 = 22. Here 22 ≤ 25, so 2 consecutive is possible
Take 3 consecutive elements:
Taking sum for 1st 3 elements of A = 2,1,3. The corresponding max in B is Max(3,6,1) = 6.
So value = 6 + (2+1+3) * 3 = 24. Here 24 ≤ 25, so 3 consecutive is possible
Take 4 consecutive elements:
Taking sum for 1st 4 elements of A = 2,1,3,4. The corresponding max in B is Max(3,6,1,3) = 6.
So value = 6 + (2+1+3+4) * 4 = 46. Here 46 > 25, so 4 consecutive is not possible
So correct answer to this input is 3.
Constraints:
n (Size of A) is up to 10⁵, A elements up to 10¹⁴, B elements up to 10⁹, Limit up to 10¹⁴.
Here is my code:
public int getMax(List<Integer> A, List<Integer> B, long limit) {
int result = 0;
int n = A.size();
for(int len=1; len<=n; len++) {
for(int i=0; i<=n-len; i++) {
int j=i+len-1;
int max = B.get(i);
long total = 0;
for(int k=i; k<=j; k++) {
total += A.get(k);
max = Math.max(max, B.get(k));
}
total = max + total * len;
if(total < limit) {
result = len;
break;
}
}
}
return result;
}
This code works for smaller range of inputs.
But fails with a time out for larger inputs. How can I reduce time complexity of this code?
Updated:
Updated code based on dratenik answer, but the sample test case mentioned in my post itself is failing. The program is returning 4 instead of 3.
public int getMax(List<Integer> A, List<Integer> B, long limit) {
int from = 0, to = 0, max = -1;
int n = A.size();
for (; from < n;) {
int total = 0;
int m = B.get(from); // updated here
for (int i = from; i < to; i++) {
total += A.get(i); // updated here
m = Math.max(m, B.get(i)); // updated here
}
total = m + total * (to - from); // updated here
if (total <= limit && to - from + 1 > max) {
max = to - from + 1;
}
if (total < limit && to < n) { // below target, extend window
to++;
} else { // otherwise contract window
from++;
}
if (from > to) {
to = from;
}
}
return max;
}
Since all the elements of A and B are positive, you can solve this with the usual two-pointer approach to finding a maximum length subarray:
Initialize two pointers s and e to the start of the arrays, and then advance e as far as possible without violating the limit. This finds the longest valid subarray that starts at s.
While e isn't at the end of the arrays, advance s by one position, and then again advance e as far as possible without violating the limit. This finds the longest valid subarray that starts at every position. This leads to an O(n) algorithm, because e can advance monotonically.
Your answer is the longest valid sequence you see.
In order to determine in O(1) whether or not a particular range from s to e is valid, you need to track the cumulative sum of A elements and the current maximum of B elements.
The sum is easy -- just add elements that e passes and subtract elements that s passes.
To track the current maximum of elements in B, you can use the standard sliding-window-maximum algorithm described here: Sliding window maximum in O(n) time. It works just fine with expanding and contracting windows, maintaining O(1) amortized cost per operation.
Here's an O(n) solution in Java. Note that I multiplied the sum of A elements by the length of the sequence, because it's what you seem to intend, even though the formula you wrote multiplies by length-1:
public static int getMax(List<Integer> A, List<Integer> B, long limit) {
final int size = A.size();
// a Queue containing indexes of elements that may become max in the window
// they must be monotonically decreasing
final int maxQ[] = new int[size];
int maxQstart = 0, maxQend = 0;
// current valid window start and end
int s=0, e = 0;
int bestLen = 0;
long windowSum = 0;
while (s < size && e < size) {
// calculate longer window max
long nextMax = maxQstart < maxQend ? B.get(maxQ[maxQstart]) : 0;
nextMax = Math.max(nextMax, B.get(e));
long sumPart = (windowSum + A.get(e)) * (e+1-s);
if (nextMax + sumPart <= limit) {
// extending the window is valid
int lastB = B.get(e);
while (maxQstart < maxQend && B.get(maxQ[maxQend-1]) <= lastB) {
--maxQend;
}
maxQ[maxQend++] = e;
windowSum += A.get(e);
++e;
if (e-s > bestLen) {
bestLen = e-s;
}
} else if (e > s) {
// extending the window is invalid.
// move up the start instead
windowSum -= A.get(s);
++s;
while(maxQstart < maxQend && maxQ[maxQstart] < s) {
++maxQstart;
}
} else {
// we need to move the start up, but the window is empty, so move them both
++s;
++e;
}
}
return bestLen;
}
Sliding window approach? Slightly pseudocodey version:
int from=0, to=0, max = -1;
for(;from<n;) {
total = (target expression on elements between from-to inclusive)
if (total<=target && to-from+1 > max) {max = to-from+1;}
if (total<target && to<n) { // below target, extend window
to++;
} else { // otherwise contract window
from++;
}
if (from>to) {to=from;}
}
return max;
The sum could be updated incrementally, but I don't know how to sensibly update the max(B[i],B[i+1],...B[i+k]) part when contracting the window, so let's recompute the whole thing at each step.
I tried to use meanigful names to make the code readable. Don't hesitate to ask where it is not clear:
public int getMax(List<Integer> a, List<Integer> b, long limit) {
int max = -1;
int numberOfElements = 2;
boolean found;
do{
found = false;
for ( int index = 0; index <= a.size() - numberOfElements; index++) {
int totalA = 0;
int maxB = b.get(index);
for (int i = index; i < index + numberOfElements; i++) {
totalA += a.get(i);
maxB = Math.max(maxB,b.get(i)); // updated here
}
int total = maxB + totalA * numberOfElements;
if (total <= limit && numberOfElements >= max) {
max = numberOfElements;
found = true;
break;
}
}
numberOfElements++;
} while(found && numberOfElements <= a.size());
return max;
}
(more test cases can be helpful for further debugging)
I think the main obstacle there is how to efficiently track maximum over sliding window.
Easy optimization in this respect without diving into dynamic programming is to make use of MaxHeap.
In java it is implemented as PriorityQueue.
Please consider following code.
private int findMaxRange(List<Long> listA, List<Long> listB, long limit) {
int maxRange = 0;
while (maxRange < listA.size() && isRangePossible(listA, listB, limit, maxRange+1)) {
maxRange++;
}
return maxRange;
}
private boolean isRangePossible(List<Long> listA, List<Long> listB, long limit, int rangeSize) {
//calculate initial values of max and sum
PriorityQueue<Long> maxHeap = new PriorityQueue<>(rangeSize, Comparator.reverseOrder());
listB.stream().limit(rangeSize).forEach(maxHeap::add);
Long max = maxHeap.peek();
Long sum = listA.stream().limit(rangeSize).mapToLong(i->i).sum();
//iterate with sliding window
for (int i = 0; i < listA.size() - rangeSize; i++) {
if (isConditionMet(max, sum, rangeSize, limit)) {
return true;
}
sum = sum + listA.get(i+rangeSize) - listA.get(i);
maxHeap.remove(listB.get(i));
maxHeap.add(listB.get(i+rangeSize));
max = maxHeap.peek();
}
return isConditionMet(max, sum, rangeSize, limit);
}
private boolean isConditionMet(Long max, Long sum, int rangeSize, long limit) {
return max + sum * rangeSize < limit;
}
Also please pay attention to value ranges. Such big values can easily overflow long and may require specialized types like BigInteger. You should also consider how much memory is used by auxiliary datatypes.
The problem here seems to be use of three nested for loops for calculating max and sum for every window.
We can avoid this unnecessary iterations by using calculations of previous iteration in the new iteration with the help of Dynamic programming.
In my solution, I made 2 2d Arrays, one to store max values for each windows and other to store sums for each windows, storing values of previous iterations will greatly reduce the time complexity.
here is the Java code:
import java.util.*;
public class MyClass {
public static void main(String[] args) {
System.out.println("Hello, World!");
List A = Arrays.asList(2,1,3,4,5);
List B = Arrays.asList(3,6,1,3,4);
System.out.println(MyClass.getMax(A, B, 25L));
}
public static int getMax(List<Integer> A, List<Integer> B, long limit) {
int n = A.size();
int[][] dp1 = new int[n + 1][n + 1];
int[][] dp2 = new int[n + 1][n + 1];
for(int i = 1; i <= n; i++) {
for(int j = i; j <= n; j++) {
dp1[i][j] = Math.max(dp1[i - 1][j- 1], B.get(j - 1));
dp2[i][j] = dp2[i - 1][j- 1] + A.get(j - 1);
}
}
for(int i = 0; i <= n; i++) {
for(int j = 0; j <= n; j++) {
System.out.print("{" + dp1[i][j] + ", " + dp2[i][j] + "}, ");
}
System.out.println();
}
int kMax = 0;
for(int i = 0; i <= n; i++) {
for(int j = i; j <= n; j++) {
if(dp1[i][j] + dp2[i][j] * i <= limit) {
kMax = i;
}
}
}
System.out.println("Max K: " + kMax);
return 0;
}
}
if you are dependent only on algorithms and not making any app or game, it's not necessary that you have to use java, try using python or c++ (or even c, c#), python is used mostly for algorithms,
or if you need java only then add breakpoints or make the program to print all work it does (ask it to print j, i, k, result variables in console) then you can easily debug.
I'm writing a program which is supposed to find the 25 top numbers in a large array using threads. My algorithm seems to work fine, however when comparing the result to an Arrays.sort-ed version of the original array, it seems like my top 25-list misses some of the numbers. I really hate posting this much code in a question, but I'm completely stuck on this, and has been for a couple of hours now. I'd love some help figuring out what's wrong. Here are my classes:
Main.java
import java.util.Arrays;
import java.util.Random;
public class Main {
public static void main(String[] args) {
final int NUM_THRS = 4;
int[] numbers = new int[500];
Random generator = new Random(500);
for(int i = 0; i < numbers.length; i++) {
numbers[i] = Math.abs(generator.nextInt());
}
Thread[] thrs = new Thread[NUM_THRS];
NumberThread[] nthrs = new NumberThread[NUM_THRS];
long startTime = System.currentTimeMillis();
for(int i = 0; i < thrs.length; i++) {
int start = getStart(i, thrs.length, numbers.length);
int stop = getStop(i, thrs.length, numbers.length);
nthrs[i] = new NumberThread(numbers, start, stop);
thrs[i] = new Thread(nthrs[i]);
thrs[i].start();
}
for (int i = 0; i < thrs.length; i++) {
try {
thrs[i].join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
int[] top = new int[25];
int[] indices = new int[NUM_THRS];
for (int i = 0; i < indices.length; i++) {
indices[i] = 24;
}
for(int i = 0; i < top.length; i++) {
top[i] = getMax(nthrs, indices);
}
for (int i = 0; i < top.length; i++) {
System.out.println(top[i]);
}
}
public static int getMax(NumberThread[] thrs, int[] indices) {
int maxNum = 0;
int maxIdx = 0;
for(int i = 0; i < indices.length; i++) {
if(indices[i] >= 0) {
if(thrs[i].topNums[indices[i]] > maxNum) {
maxNum = thrs[i].topNums[indices[i]];
maxIdx = i;
}
}
}
System.out.println("iterate");
indices[maxIdx] = indices[maxIdx]-1;
return maxNum;
}
public static int getStart(int i, int total, int len) {
return i*len/total;
}
public static int getStop(int i, int total, int len) {
if(i != total-1) {
return (i+1)*len/total;
}
return len-1;
}
}
NumberThread.java
public class NumberThread implements Runnable {
int start, stop;
int[] numbers;
int[] topNums;
public NumberThread(int[] numbers, int start, int stop) {
this.numbers = numbers;
this.start = start;
this.stop = stop;
this.topNums = new int[25];
System.out.println(start + " " + stop);
}
#Override
public void run() {
for (int i = start; i <= stop; i++) {
inner: for (int j = topNums.length-1; j > 0; j--) {
if(numbers[i] > topNums[j]) {
topNums[j] = numbers[i];
break inner;
}
}
}
}
}
The numbers printed after main are not the same as the top numbers when I Arrays.sort the numbers-array and print the top 25. Some numbers seem to be missing.
Thanks a lot in advance.
I think that your NumberThread classes Run method isn't doing what it should do. It needs to find the 25 largest numbers in the partition you assign to it, for example if the array you are searching was already sorted then the 25 largest numbers could all be in 1 partition but what its's actually doing is overwriting the first number it finds that's smaller than the current number so you end up with less than 25 numbers and they might not be the largest.
For example consider the sequence 98 99 1 2 3... 98 would get written to topNums[19] but then overwritten with 99.
I'm also not sure about the getMax function, it seems to be trying to merge the different topNums arrays together; however the arrays aren't sorted so I don't see how it can work.
I've been writing the following code for my OS course and I got some weird results. The code creates x threads and runs them concurrently in order to multiply two squared matrices. Every thread will multiply Number_of_rows/Number_of_threads rows of the input matrices.
When running it on a 1024X1024 matrices, with 1...8 threads, I get that the fastest multiplication happens when using only one thread. I would expect that a MacBook pro with i5 processor (2-cores) will utilize the two cores and that will yield faster results when using two threads.
Running time goes from about ~9.2 seconds using one thread, ~9.6 seconds to 27 seconds using 8.
Any idea why this is happening?
BTW, A few things about the code:
a. Assume that both matrices have identical dimensions and are square.
b. Assume that number of threads <= number of rows/columns.
public class MatrixMultThread implements Runnable {
final static int MATRIX_SIZE = 1024;
final static int MAX_THREADS = MATRIX_SIZE;
private float[][] a;
private float[][] b;
private float[][] res;
private int startIndex;
private int endIndex;
public MatrixMultThread(float[][] a, float[][]b, float[][] res, int startIndex, int endIndex) {
this.a = a;
this.b = b;
this.res = res;
this.startIndex = startIndex;
this.endIndex = endIndex;
}
public void run() {
float value = 0;
for (int k = startIndex; k < endIndex; k++) {
for (int i = 0; i < a.length; i++) {
for (int j = 0; j < a.length; j++) {
value += a[k][j]*b[j][i];
}
res[k][i] = value;
value = 0;
}
}
}
public static float[][] mult(float[][] a, float[][] b, int threadCount){
// Get number of rows per each thread.
int rowsPerThread = (int) Math.ceil(MATRIX_SIZE / threadCount);
float[][] res = new float[MATRIX_SIZE][MATRIX_SIZE];
// Create thread array
Thread[] threadsArray = new Thread[threadCount];
int rowCounter = 0;
for (int i = 0; i < threadCount; i++) {
threadsArray[i] = new Thread(new MatrixMultThread(a,b,res,rowCounter, Math.max(rowCounter + rowsPerThread, MATRIX_SIZE)));
threadsArray[i].start();
rowCounter += rowsPerThread;
}
// Wait for all threads to end before finishing execution.
for (int i = 0; i < threadCount; i++) {
try {
threadsArray[i].join();
} catch (InterruptedException e) {
System.out.println("join failed");
}
}
return res;
}
public static void main(String args[]) {
// Create matrices and random generator
Random randomGenerator = new Random();
float[][] a = new float[MATRIX_SIZE][MATRIX_SIZE];
float[][] b = new float[MATRIX_SIZE][MATRIX_SIZE];
// Initialize two matrices with initial values from 1 to 10.
for (int i = 0; i < a.length; i++) {
for (int j = 0; j < a.length; j++) {
a[i][j] = randomGenerator.nextFloat() * randomGenerator.nextInt(100);
b[i][j] = randomGenerator.nextFloat() * randomGenerator.nextInt(100);
}
}
long startTime;
for (int i = 1; i <= 8; i++) {
startTime = System.currentTimeMillis();
mult(a,b,i);
System.out.println("Total running time is: " + (System.currentTimeMillis() - startTime) + " ms");
}
}
}
Firstly a bit of logging helps. I did logging for this and found out a bug in your logic.
Here is the log
Starting execution for thread count: 1
Start index: 0
End index: 1024
Starting execution: MatrixMultiplier: 0
Ending executionMatrixMultiplier: 0
Total running time is: 6593 ms
Starting execution for thread count: 2
Start index: 0
End index: 1024 <------ This is the problem area
Start index: 512
End index: 1024
Starting execution: MatrixMultiplier: 1
Starting execution: MatrixMultiplier: 0
Your first thread in all iterations is performing whole multiplication everytime. That's why you are not seeing results. Figure out the bug.
The below code is my implementation of temporal difference learning. The agent who uses the TD algorithm plays more than 750,000 games against an agent that uses mini-max procedure to play the game, But the problem is the TD-agent does not learn... What is wrong with this implementation?
updateToNextState is called when the agent choose a next move.
public void updateToNextState(int[] currentState, double[] nextStateOutput) {
double[] outputOfNext = nextStateOutput;
double[] outputOfCurrent = getOutput(currentState);
double[] error = getDifferenceOfOutputs(outputOfNext, outputOfCurrent);
lastHandledState = currentState;
for (int j = 0; j < layers[HIDDEN].neurons.length; j++) {
for (int k = 0; k < layers[OUTPUT].neurons.length; k++) {
double toBeUpdatedValueForJToK = BETA * error[k]
* eligibilityTraces.getEjk(j, k);
layers[HIDDEN].neurons[j].updateWeightToNeuron(
layers[OUTPUT].neurons[k].getNeuronId(),
toBeUpdatedValueForJToK);
for (int i = 0; i < layers[INPUT].neurons.length; i++) {
double toBeUpdatedValueForIToJ = ALPHA * error[k]
* eligibilityTraces.getEijk(i, j, k);
layers[INPUT].neurons[i].updateWeightToNeuron(
layers[HIDDEN].neurons[j].getNeuronId(),
toBeUpdatedValueForIToJ);
}
}
}
updateEligibilityTraces(currentState);
}
private void updateEligibilityTraces(int[] currentState) {
// to ensure that the values in neurons are originated from current
// state
feedForward(currentState);
for (int j = 0; j < layers[HIDDEN].neurons.length; j++) {
for (int k = 0; k < layers[OUTPUT].neurons.length; k++) {
double toBeUpdatedValueForJK = gradient(layers[OUTPUT].neurons[k])
* layers[HIDDEN].neurons[j].output;
eligibilityTraces.updateEjk(j, k, toBeUpdatedValueForJK);
for (int i = 0; i < layers[INPUT].neurons.length; i++) {
double toBeUpdatedValueForIJK = gradient(layers[OUTPUT].neurons[k])
* gradient(layers[HIDDEN].neurons[j])
* layers[INPUT].neurons[i].output
* layers[HIDDEN].neurons[j]
.getWeightToNeuron(layers[OUTPUT].neurons[k]
.getNeuronId());
eligibilityTraces.updateEijk(i, j, k,
toBeUpdatedValueForIJK);
}
}
}
}
private double gradient(Neuron neuron) {
return neuron.output * (1 - neuron.output);
}
public void updateToNextWhenOpponentEndsGame(double[] outputOfEndState) {
updateToNextState(lastHandledState, outputOfEndState);
}
private double[] getDifferenceOfOutputs(double[] outputNext,
double[] outputCurrent) {
double[] differencesVector = new double[outputNext.length];
for (int i = 0; i < outputNext.length; i++) {
double difference = outputNext[i] - outputCurrent[i];
differencesVector[i] = difference;
}
return differencesVector;
}
I have used this link as guide line. I have tried different values for ALPHA & BETA, amount of hidden neurons. Eligibility traces are initialized to 0.
The problem mainly is that you cannot tune your neural network function approximator, and from what you said I can assume that "it does not learn", means that the algorithm does not converge.
This happens when we use using TD and NN together. And this happened to me previously, and I searched for it for a long time. The lesson that I have learned is as follows:
According to Richard Sutton: Do not try to use Neural Networks as function approximators, together with TD methods, unless you know exactly how to tune your neural network. Otherwise, this will cause lots of problems.
To know more about it find Sutton's talk on youtube.
I would like to simulate a situation which is mentioned in books about concurrency - that without a proper synchronization one thread can see a stale value of a variable that has been already modified by a different thread. This could happen because for example a CPU cache.
To do this I have written the following program. The idea is that there are 4 threads that initialize a different part of a shared array. The 5th thread (main, parent thread) waits until all 4 previous threads are done, iterates over the shared array and adds its values (always 1 or if I'm lucky null, which would mean a stale value)
package p1;
class ArrFill implements Runnable {
int l, r;
Integer[] arr;
ArrFill(int l, int r, Integer[] arr) {
this.l = l;
this.r = r;
this.arr = arr;
}
#Override
public void run() {
for(int i = l; i < r; i++)
arr[i] = new Integer(1);
}
}
public class Main {
final static int MAX = 10000000;
final static int tnum = 4;
public static void main(String[] args) throws InterruptedException {
int cores = Runtime.getRuntime().availableProcessors();
System.out.println(cores);
Integer[] arr = new Integer[MAX];
Thread[] t = new Thread[tnum];
if(MAX % tnum != 0)
throw new IllegalStateException();
int step = MAX / tnum;
int l = 0, r = 0;
for(int i = 0; i < tnum; i++) {
l = r;
r += step;
t[i] = new Thread(new ArrFill(l, r, arr));
t[i].start();
}
for(int i = 0; i < tnum; i++)
t[i].join();
int res = 0;
for(int i = 0; i < MAX; i++)
if(arr[i] != null)
res += arr[i];
System.out.println(res == MAX);
}
}
I have run this program many times although I never seen a stale value (null). I have 2 cores. Do you have any idea how this program could be improved to actually present the cached value phenomena? Or maybe you have a completly different approach?
Thanks!