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 in java that basically simulates a random walk by moving 1 x or y value for each step. Ex. starts at (0, 0), using Math.random, I can x++ or y-- which changes the x or y coordinate by one value. This walk terminates once the absolute value of both the x and y value added together gives me the number inputted (by command line argument). There are two command line integers, the first being the number just mentioned and the second being the trials, which is the number of times I have to simulate this walk. By the end of this program, a step counter keeps track of how many steps were taken in each trial, and it is then averaged to give the average number of steps it took for each trial to add up to the number.
The problem I am having is that when I run my program, no error shows up or anything, it just doesn't run which gives me the idea that the runtime is just too long and there are too many iterations to go through. The input for the trials variable is going to be around 100000 to 1000000. I would appreciate any help or ideas on fixing this problem.
Here is my code for the program:
public class RandomWalkers {
public static void main(String [] args) {
int r = Integer.parseInt(args[0]);
double trials = Double.parseDouble(args[1]);
double avgSteps = 0;
double totalSteps = 0;
int x = 0;
int y = 0;
int steps;
for(int j = 0; j < trials; j++ ) {
int trialDistance = 0;
for(steps = 0; trialDistance != r; steps++) {
double random = Math.random();
if(random < 0.25) {
x++;
} else if((0.25 < random) && (random < 0.50)) {
x--;
} else if((0.50 < random) && (random < 0.75)) {
y++;
} else if((0.75 < random) && (random < 1.00)) {
y--;
} else {}
totalSteps = totalSteps + steps;
trialDistance = Math.abs(x) + Math.abs(y);
}
}
avgSteps = totalSteps / trials;
System.out.println("average number of steps = " + avgSteps);
}
}
ok so here an updated version.
public class RandomWalkers {
public static void main(String[] args) {
int radius = Integer.parseInt(args[0]);
int trials = Integer.parseInt(args[1]);
walk(radius, trials);
}
public static void walk(int radius, int trials) {
//not really a radius, but nvm
int totalSteps = 0;
for (int t = 0; t < trials; t++) {
//you need to reset the x and y for every trial!
int x = 0;
int y = 0;
int steps = 0;
while (Math.abs(x) + Math.abs(y) != radius) {
double random = Math.random();
if (random < 0.25) {
x++;
} else if (random < 0.50) {
x--;
} else if (random < 0.75) {
y++;
} else {
y--;
}
steps++;
}
//take this out of the loop or increase only by one!
totalSteps += steps;
}
double avgSteps = totalSteps / (double) trials;
System.out.println("average number of steps = " + avgSteps);
}
}
i moved everthing to another method (doesnt change anything) but what you can change:
remove the first part of the else if's cause if the random is smaller than .25 it wont even trigger the else. You can also change the last one to a normal "else".
take the "totalsteps = totalsteps + steps" out of the loop -> you dont want to add it with every step.
i guess its better to handle the trials as an int and only cast it to a double for the calculation of the avg.
you need to update the x,y and steps with every trial, else the second trial would be immediatly done with the result of the first
it should be fast enough to make 1mil trials or whatever, just be carefull with the "radius" its going to increase the time dramatically
For an assignment I am doing for one of my classes, we have to implement a Sieve of Eratosthenes. I have tried seven times to get a code that works and have tried incorporating numerous solutions I've researched. I finally have one that will output numbers. Unfortunately, it prints both composite and prime numbers, and doesn't print 2.
My code is as follows:
public class EratosthenesSieveAttempt6 {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
Scanner keyboard = new Scanner(System.in);
int limit;
System.out.print("Please enter the highest number to check "
+ "(number must be greater than 2): ");
limit = keyboard.nextInt();
while (limit <= 2){
System.out.println("Error - number must be greater than 2.");
System.out.println("Please enter the highest number to check: ");
limit = keyboard.nextInt();
}
boolean[] numbers = new boolean[limit + 1];
int newPrime = 2;
for(int i = 0; i < limit + 1; i++){
numbers[i] = true;
}
for(int j = 1; j < limit + 1; j++) {
if (j % 2 == 0) {
numbers[j] = false;
}
for(int k = j + 1; k < limit + 1; k++) {
if(numbers[k] == true){
j = k;
System.out.println(k);
}
}
}
}
}
I'm suspecting that there is a problem with my loops. I fixed the i and j variables for my first two loops so that it would print out from 2 onward, the problem seems to be that it's not marking the composite numbers as false after I've initialized the array to true.
Thank you in advance for your help.
Here's an implementation of the Sieve of Eratosthenes I wrote the other day:
import java.util.BitSet;
public static BitSet composite(int max) {
BitSet composite = new BitSet(max);
max = composite.size();
for (int i = 4; i < max; i += 2) composite.set(i, true);
for (int i = 9; i < max; i += 6) composite.set(i, true);
int p = 5;
while (p*p < max) {
if (!composite.get(p)) {
for (int i = p*p; i < max; i += p*2) composite.set(i, true);
}
p += 2;
if (p*p >= max) break;
if (!composite.get(p)) {
for (int i = p*p; i < max; i += p*2) composite.set(i, true);
}
p += 4;
}
return composite;
}
Notes:
BitSet allocates 64-bit words, so the size may be larger than you requested (for example, if you ask it to go up to 1000, it will go up to 1024; that's the reason for max = composite.size() near the top)
Gets the 2's, 3's out of the way explicitly, and then
Relies on the fact that all primes larger than 3 are congruent to either 1 or 5 mod 6; this is the reason the final loop alternates between adding 2 and 4
It returns a BitSet that tells you which numbers are composite. One way to extract just the primes from it would be:
public static int[] primes(BitSet composite) {
int size = composite.size() - 2 - composite.cardinality();
int[] primes = new int[size];
int index = 0;
for (int i = 2; i < composite.size(); i++) {
if (!composite.get(i)) primes[index++] = i;
}
return primes;
}
I am working on problem twelve on project Euler. It is all about triangle numbers; I am trying to find the first triangle number with more than 500 divisors. I have written a program to find this, however, it is not giving me the correct answer and I can not see why. I have provided my code below:
public class problemTwelve {
public static void main(String [] args) {
int i = 1;
int number = 1;
while(getDivisors(number) < 500) {
number += i;
i++;
}
System.out.println("The first triangle number to have greater than 500 divisors is: " + number);
}
private static int getDivisors(int triangleNum) {
int noOfDivisors = 0;
int numToTest = (int) Math.sqrt(triangleNum);
for(int i = 1; i <= numToTest; i++) {
if((triangleNum % i) == 0) {
noOfDivisors += 2;
}
}
if((numToTest * numToTest) == triangleNum) {
noOfDivisors--;
}
return noOfDivisors;
}
}
The output given by the program upon running it is as follows:
The first triangle number to have greater than 500 divisors is: 146611080
Upon entering this number as the answer on project Euler, we can see that it is wrong. I don't know where I have gone wrong in my program...
It seems that the number you are checking are not triangle. Just at looking at the code, the second number checked is 2 which is not a triangle number.
Try moving the line i++; before the line number+=i;
you have to start your numbers from 0 not 1 , here is the correct code :
int i = 1;
int number = 0;
while(getDivisors(number) < 500) {
number += i;
i++;
}
System.out.println("The first triangle number to have greater than 500 divisors is: " + number);
}
private static int getDivisors(int triangleNum) {
int noOfDivisors = 0;
int numToTest = (int) Math.sqrt(triangleNum);
for(int i = 1; i <= numToTest; i++) {
if(triangleNum % i == 0) {
noOfDivisors += 2;
}
}
if((numToTest * numToTest) == triangleNum) {
noOfDivisors--;
}
return noOfDivisors;
}
This is for project euler problem 14.
When a number is even, you're supposed to divide the number by two, but when it is odd you multiply it by three and add one. Eventually it should reach one.
My task is to find the number that takes the largest amount of steps to get to 1.
Here's my code:
int currentNum = 0;
int iterator = 0;
int[] largestChain = new int[]{0,0};
for(int i = 10;i<=1000000;i++)
{
currentNum = i;
iterator = 0;
while(currentNum!=1)
{
iterator++;
if(currentNum%2==0)
{
currentNum/=2;
}
else
{
currentNum = (currentNum*3)+1;
}
}
if(iterator>largestChain[1])
{
largestChain[0] = i;
largestChain[1] = iterator;
}
}
System.out.println("largest iterator:"+largestChain[1]+"for num:"+largestChain[0]);
Can you please help me out by telling me what's slowing it down? (It's taking >30 minutes right now and it still hasn't come up with the answer).
Use long variables instead of int. currentNum goes so high the values wrap around into the negatives!
Once you do that change, your algorithm works just fine. (I tested it)
The reason it take so long is that you are performing this while loop operation on 1 million numbers. The solution to this is to create an algorithm which saves the number of steps dynamically.
int[] steps = new int[1000000];
steps[0] = 0;
steps[1] = 1;
Iterate through the rest of your numbers, adding back to this base case. By the end, many of your paths will be computed, and you will not need a nested loop.
However, if you want to stick to your way:
I'd recommend putting some debug print statements in there to see where it is getting caught up. My guess is that the 1 Million looped while statements are the culprit, but the easiest way to find out is progress check.
try adding System.out.println(i+":"); before the while and System.out.println(" current number: "+currentnum); inside the while.
Should print out something like:
1:
1
2:
2
1
etc.
I modified the code to print the interesting info and how its looping. The loop is crazy.
I would suggest converting currentNum to 'long' and re-run it as that number goes negative (beyond int capacity).
public class TestLoop{
public static void main(String[] args){
int currentNum = 0;
int iterator = 0;
int[] largestChain = new int[]{0,0};
for(int i = 10;i<=1000000;i++)
{
currentNum = i;
iterator = 0;
System.out.println("\nCurrently Running :" + i);
while(currentNum!=1)
{
iterator++;
if(currentNum%2==0)
{
currentNum/=2;
}
else
{
currentNum = (currentNum*3)+1;
}
System.out.print(currentNum + " ");
}
if(iterator>largestChain[1])
{
largestChain[0] = i;
largestChain[1] = iterator;
}
}
System.out.println("\nLargest iterator: "+largestChain[1]+" for num:"+largestChain[0]);
}
}
I ran it on linux and got below answer in 10 mins after I changed the currentNum to 'long'.
Largest iterator: 524 for num:837799
Still there is a flaw in your logic: you are NOT checking if any other number taking the same iterations. E.g. below loop gives two such numbers :
import java.util.ArrayList;
public class TestLoop {
public static void main(String[] args) {
long currentNum;
int iterator;
int[] largestChain = new int[]{0, 0};
ArrayList Nums = new ArrayList();
for (int i = 10; i <= 300; i++) {
currentNum = i;
iterator = 0;
System.out.println("\nCurrently Running :" + i);
while (currentNum != 1) {
iterator++;
if (currentNum % 2 == 0) {
currentNum /= 2;
} else {
currentNum = (currentNum * 3) + 1;
}
System.out.print(currentNum + " ");
}
if (iterator > largestChain[1]) {
largestChain[0] = i;
largestChain[1] = iterator;
Nums.clear();
Nums.add(i);
} else if (iterator == largestChain[1]) {
Nums.add(i);
}
}
System.out.println("\nLargest iterator: " + largestChain[1]);
//+ " for num:" + largestChain[0]);
if (Nums.size() == 1) {
System.out.println("There is only one number with " + largestChain[1] + " iterations:" + largestChain[0]);
} else {
System.out.print("Below numbers took " + largestChain[1] + " iterations:");
for (int i = 0; i < Nums.size(); i++) {
System.out.print(" " + Nums.get(i));
}
}
}
}