Maximum sub-sum - java

Here is example of my input data:
5 // Number of 1D arrays, in this case we'll have array[5][3]
1 2 3 // Values of array[i][0..2]
1 1 1
1 0 0
1 1 0
2 1 0
And output is:
12 // Maximum sum ({1 2 3} + {1 1 1} + {2 1 0} = {4 4 4} = 12) - END SUM VALUES MUST BE EQUAL(4 4 4).
3 // Number of arrays used to get this sum
The problem is to find maximum sum using n arrays, and secod condition is to use minimum number of arrays. Also if sum > 300 we stop algorithm. (300 is maximum). Here is my code, it's I get good answers but it's time complexity is O(2^n-1). I'm thinking that it's possible to save results in some way and don't calculate same things many times, but I don't know how yet.
public static int[] fuel(int start, int[] sum, int counter) {
int[] val = { sum[0] + crystal[start][0], sum[1] + crystal[start][1], sum[2] + crystal[start][2] };
int newSum = val[0] + val[1] + val[2];
if(newSum > 300)
return null;
if(val[0] == val[1] && val[1] == val[2]) { // All 3 values have to be equal!
if(newSum > result[0]) {
result[0] = newSum;
result[1] = counter;
} else if(newSum == result[0] && result[1] > counter) {
result[1] = counter;
}
}
if(start + 1 < crystalNumber) {
fuel(start + 1, val, counter + 1);
fuel(start + 1, sum, counter);
}
return result;
}

This may not be the best algorithm to solve this but it should be quicker than O(2^N).
The idea is to record all reachable sums as you loop through the input array. You can use a dictionary whose key is a unique hash of the possible sums, for the sake of simplicity let's just assume the key is a string which concatenates the three sums, for example, the sums [3,5,4] we'll use the key "003005004" , the value of the dictionary will be the minimum numbers of arrays to reach that sum.
So in your case:
1 2 3 => [001002003] =1
1 1 1 => [001001001] =1, [002003004]=2 (itself and use [001002003] from above)
1 0 0 => [001000000] =1, [002002003] =2, [002001001] =2, [003003004] =3
1 1 0 ...
2 1 0 ...
In the end, you will find [004004004] =3 and that's your answer.
This may seems going through all combinations as well so why it's quicker, it's because the maximum sum is 300 for each number, so in the very worst case, we may have 301^3 keys filled and have to update their values for each new input array. This is however still O(n) and despite of the large constant, it should still run much faster than O(2^n). (If you solve 300^3*n = 2^n, n is around 30-ish)
A simple hash function would be a*301*301+b*301+c

I think the problem is given m 1-D arrays and a number n, find the maximum sum using n arrays from m;
The solution looks straight forward. keep sum of each 1-D array in a separate array, say sum[]
1 2 3 = 6
1 1 1 = 3
1 0 0 = 1
1 1 0 = 2
2 1 0 = 3
Sort this array sum
6,3,3,2,1
and return the sum of first n elements of this array.

Related

Improve performance of string to binary number conversion

This is one of the questions that I faced in competitive programming.
Ques) You have an input String which is in binary format 11100 and you need to count number of steps in which number will be zero. If number is odd -> subtract it by 1, if even -> divide it by 2.
For example
28 -> 28/2
14 -> 14/2
7 -> 7-1
6 -> 6/2
3 -> 3-1
2 -> 2/2
1-> 1-1
0 -> STOP
Number of steps =7
I came up with the following solutions
public int solution(String S) {
// write your code in Java SE 8
String parsableString = cleanString(S);
int integer = Integer.parseInt(S, 2);
return stepCounter(integer);
}
private static String cleanString(String S){
int i = 0;
while (i < S.length() && S.charAt(i) == '0')
i++;
StringBuffer sb = new StringBuffer(S);
sb.replace(0,i,"");
return sb.toString();
}
private static int stepCounter(int integer) {
int counter = 0;
while (integer > 0) {
if (integer == 0)
break;
else {
counter++;
if (integer % 2 == 0)
integer = integer / 2;
else
integer--;
}
}
return counter;
}
The solution to this question looks quite simple and straightforward, however the performance evaluation of this code got me a big ZERO. My initial impressions were that converting the string to int was a bottleneck but failed to find a better solution for this. Can anybody please point out to me the bottlenecks of this code and where it can be significantly improved ?
If a binary number is odd, the last (least significant) digit must be 1, so subtracting 1 is just changing the last digit from 1 to 0 (which, importantly, makes the number even).
If a binary number is even, the last digit must be 0, and dividing by zero can be accomplished by simply removing that last 0 entirely. (Just like in base ten, the number 10 can be divided by ten by taking away the last 0, leaving 1.)
So the number of steps is two steps for every 1 digit, and one step for every 0 digit -- minus 1, because when you get to the last 0, you don't divide by 2 any more, you just stop.
Here's a simple JavaScript (instead of Java) solution:
let n = '11100';
n.length + n.replace(/0/g, '').length - 1;
With just a little more work, this can deal with leading zeros '0011100' properly too, if that were needed.
Number of times you need to subtract is the number of one bits which is Integer.bitCount(). Number of times you need to divide is the position of most-significant bit which is Integer.SIZE (32, total number of bits in integer) minus Integer.numberOfLeadingZeros() minus one (you don't need to divide 1). For zero input I assume, the result should be zero. So we have
int numberOfOperations = integer == 0 ? 0 : Integer.bitCount(integer) +
Integer.SIZE - Integer.numberOfLeadingZeros(integer) - 1;
As per the given condition, we are dividing the number by 2 if it is even which is equivalent to remove the LSB, again if number is odd we are subtracting 1 and making it an even which is equivalent to unset the set bit (changing 1 to 0). Analyzing the above process we can say that the total number of steps required will be the sum of (number of bits i.e. (log2(n) +1)) and number of set bits - 1(last 0 need not to be removed).
C++ code:
result = __builtin_popcount(n) + log2(n) + 1 - 1;
result = __builtin_popcount(n) + log2(n);

Calculate the values of counters after applying all alternating operations

I was trying to solve a problem from the Codility with a given solution. The problem is provided below:
You are given N counters, initially set to 0, and you have two possible operations on them:
increase(X) − counter X is increased by 1,
max counter − all counters are set to the maximum value of any counter.
A non-empty array A of M integers is given. This array represents consecutive operations:
if A[K] = X, such that 1 ≤ X ≤ N, then operation K is increase(X),
if A[K] = N + 1 then operation K is max counter.
For example, given integer N = 5 and array A such that:
A[0] = 3
A[1] = 4
A[2] = 4
A[3] = 6
A[4] = 1
A[5] = 4
A[6] = 4
the values of the counters after each consecutive operation will be:
(0, 0, 1, 0, 0)
(0, 0, 1, 1, 0)
(0, 0, 1, 2, 0)
(2, 2, 2, 2, 2)
(3, 2, 2, 2, 2)
(3, 2, 2, 3, 2)
(3, 2, 2, 4, 2)
The goal is to calculate the value of every counter after all operations.
Write a function:
class Solution { public int[] solution(int N, int[] A); }
that, given an integer N and a non-empty array A consisting of M integers, returns a sequence of integers representing the values of the counters.
The sequence should be returned as:
a structure Results (in C), or
a vector of integers (in C++), or
a record Results (in Pascal), or
an array of integers (in any other programming language).
For example, given:
A[0] = 3
A[1] = 4
A[2] = 4
A[3] = 6
A[4] = 1
A[5] = 4
A[6] = 4
the function should return [3, 2, 2, 4, 2], as explained above.
Assume that:
N and M are integers within the range [1..100,000];
each element of array A is an integer within the range [1..N + 1].
Complexity:
expected worst-case time complexity is O(N+M);
expected worst-case space complexity is O(N) (not counting the storage required for input arguments).
I have a solution provided,
public static int[] solution(int N, int[] A) {
int[] counters = new int[N];
int currMax = 0;
int currMin = 0;
for (int i = 0; i < A.length; i++) {
if (A[i] <= N) {
counters[A[i] - 1] = Math.max(currMin, counters[A[i] - 1]);
counters[A[i] - 1]++;
currMax = Math.max(currMax, counters[A[i] - 1]);
} else if (A[i] == N + 1) {
currMin = currMax;
}
}
for (int i = 0; i < counters.length; i++) {
counters[i] = Math.max(counters[i], currMin);
}
return counters;
}
It seems they use 2 storage to hold and update the min/max values and use them inside the algorithm. Obviously, there is a more direct way to solve the problem ie. increase the value by 1 or set all the values to max as suggested and I can do that. The drawback will be to lower perfromance and increased time complexity.
However, I would like to understand what is going on here. I spend times debugging with the example array but the algorithm is still little confusing.
Anyone understand it and can explain to me briefly?
It is quite simple, they do lazy update. You keep track at all times of what is the value of the counter that has the highest value (currMax). Then, when you get a command to increase all counters to that maxValue, as that is too expensive, you just save that the last time you had to increase all counters to maxValue, that value was currMin.
So, when do you update a counter value to that value? You do it lazily, you just update it when you get a command to update that counter (increase it). So when you need to increase a counter, you update the counter to the max between its old value and currMin. If this was the first update on this counter since a N + 1 command, the correct value it should have is actually currMin, and that will be higher (or equal) to its old value. One you updated it, you add 1 to it. If now another increase happens, currMin doesn't actually matter, as the max will take its old value until another N + 1 command happens.
The second for is to account for counters that did not get an increase command after the last N + 1 command.
Note that there can be any number of N + 1 commands between 2 increase operations on a counter. It still follows that the value it should have is the maxValue at the time of the last N + 1 command, it doesn't really matter that we didn't update it before with the other maxValue from a previous N + 1, we only care about latest.

Number of ways to form an amount with certain coins

I am trying to solve this problem. http://www.lintcode.com/en/problem/coin-change-ii/#
This is the standard coin change problem solvable with dynamic programming. The goal is to find the number of ways to create an amount using an infinite set of coins, where each has a certain value. I have created the following solution :
public int change(int amount, int[] coins) {
// write your code here
int[] dp = new int[amount + 1];
dp[0] = 1;
// for(int coin : coins) {
// for(int i = 1; i <= amount; i++) {
// if(i >= coin) dp[i] += dp[i-coin];
// }
// }
for(int i = 1; i <= amount; i++) {
for(int coin : coins) {
if(i >= coin) dp[i] += dp[i-coin];
}
}
return dp[amount];
}
Why does the first for loop give the correct answer, but the second one does not? What am I missing here? Shouldn't the answer be the same? Could you provide a visual to help me "see" why the second for loop is incorrect?
When the amount = 8 and coins = [2,3,8] the output is 5 when it should be 3 when using the 2nd for loop's technique which is not correct.
Thank you.
Let's consider the loop that works first:
for(int coin : coins) {
for(int i = 1; i <= amount; i++) {
if(i >= coin) dp[i] += dp[i-coin];
}
}
Each iteration of the outer loop takes a coin of one value and finds out the number of ways to reach any value between the coin value and amount, adding that coin to the result of the previous iterations.
Considering your amount = 8 and coins = [2,3,8] example:
The array is initialized to
index 0 1 2 3 4 5 6 7 8
value 1 0 0 0 0 0 0 0 0
which means that without any of the coins, the only amount we can reach is 0, and we have a single way to reach that amount (0 2s, 0 3s, 0 8s).
Now we find the amounts we can reach with just the coin of value 2:
index 0 1 2 3 4 5 6 7 8
value 1 0 1 0 1 0 1 0 1
It's not surprising that we can reach any even amount. For each such amount we have a single way to reach that amount (1 2s to reach 2, 2 2s to reach 4, etc...).
Now we find the amounts we can reach with coins of value 2 or 3. We can reach an amount k using a single coin of 3 if we already found ways to reach the amount k-3.
Below I show the number of ways to reach each value between 0 and 8, and specify how many coins of each type are used in each combination.
index 0 1 2 3 4 5 6 7 8
value 1 0 1 1 1 1 2 1 2
0x2 - 1x2 0x2 2x2 1x2 3x2 2x2 4x2
0x3 - 0x3 1x3 0x3 1x3 0x3 1x3 0x3
or or
0x2 1x2
2x3 3x3
Finally, in the last iteration we consider the coin of 8. It can only be used to reach the amount 8, so we get the final result:
index 0 1 2 3 4 5 6 7 8
value 1 0 1 1 1 1 2 1 3
When you swap the loops:
for(int i = 1; i <= amount; i++) {
for(int coin : coins) {
if(i >= coin) dp[i] += dp[i-coin];
}
}
you bring the order the coins are added into play. For example, the amount 5 can be reached by either first taking a coin of 2 and then a coin of 3, or by first taking a coin of 3 and then a coin of 5. Therefore the value of dp[5] is now 2.
Similarly, dp[8] results in 5 since you can take any of the following sequences of coins:
2+3+3
3+2+3
3+3+2
2+2+2+2
8
The original loop doesn't distinguish between 2+3+3, 3+2+3 and 3+3+2. Hence the different output.
private static int coinChange(int[] coins, int sum) {
int size = coins.length;
int[][] arr = new int[size + 1][sum + 1];
// Applying the recursive solution:
for(int i = 1; i < size +1; i++){
for(int j = 1; j < sum +1; j++) {
arr[i][0] = 1;
if (coins[i - 1] > j) {
arr[i][j] = arr[i - 1][j];
} else
arr[i][j] = arr[i - 1][j]+arr[i][j - coins[i - 1]] ;
}}
return arr[size][sum];enter code here

finding the pattern length in an integer array in java?

Suppose I have a Fibonacci sequence of the number x as follows and I want to detect a sequence in an array. Java method should return the length of sequence
x 0 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610
1)x mod 2 - 0 1 1 0 1 1 0 1 1 0 1 1 0 1 1 0
2)x mod 3 - 0 1 1 2 0 2 2 1 0 1 1 2 0 2 2 1
Answer 1) 3 (repetitive sequence 011 and length is 3)
2) 8 (repetitive sequence 01120221 and length is 8)
A very simple approach would be to create a copy of the array, and check position 0 of the first array against position 1 of the second array, and if they match, continue checking until the end. If the whole thing matches, then you have a repeating length of 1.
If not, then you compare position 0 of the first array to position 2 of the second array, and follow the same process as above. If this matches, then you have a repeating length of 2.
And you continue this process until you either find a match, or reach the end of the array and can't offset it any further, in which case there is no repeat.
If you are only intending to use this specifically for modulo values of numbers in the Fibonacci sequence, and not for arbitrary data, then the sequence will repeat as soon as you find the second occurrence of a 0 followed by a 1 in the modulo sequence. This is because mod(a + b, n) = mod(mod(a, n) + mod(b, n), n), so the modulo of a number in the Fibonacci sequence (which is the sum of the two previous values) is determined by the previous 2 modulo results. Therefore, once the original pattern of a 0 followed by a 1 reoccurs, the pattern will repeat.
The Following code works:
private static int detectSequence(int[] array) {
int count = 2;
for (int i = 2; i < array.length; i++) {
if(array[i] == 0 && array[i+1] == 1 && i+1 < array.length){
return count;
}
count++;
}
return count;
}

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6

public class IntegerSet {
int dMax;
boolean[] set;
public IntegerSet(int domainMax) {
dMax = domainMax + 1;
set = new boolean[dMax];
}
...some methods...
public static IntegerSet union(IntegerSet s1, IntegerSet s2) {
IntegerSet union = new IntegerSet(Math.max(s1.dMax, s2.dMax));
for (int i = 0; i < (Math.max(s1.dMax, s2.dMax)); i++) {
union.set[i] = (s1.set[i] || s2.set[i]);
}
return union;
}
Can anyone tell me whats wrong with this?
I can't understand why I'm getting the error message after hours: Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 6
As the two sets supplied as arguments may have different domain max; the resulting union should have whichever of these two domain max is larger hence why I am using Math.max.
Any help would be appreciated.
The problem arises when s1 and s2 have different lengths. These lines are flawed:
IntegerSet union = new IntegerSet(Math.max(s1.dMax, s2.dMax));
for (int i = 0; i < (Math.max(s1.dMax, s2.dMax)); i++) {
Think about what two sets of unequal length look like:
(using 1 and 0 in place of true and false)
s1: {0 1 0 0 1 1} length = 6
s2: {1 1 1 0 0 0 1 0 1 1 0 1} length = 12
You've programmed your loop to iterate from 0 up to the maximum length of the two sets, which in the above example is 12 (meaning that the last value your loop will use is 11). But s1 is only 6 elements long--its last valid index is 5! As the loop erroneously attempts to access elements 7 through 12 (indices 6 through 11) of s1, it throws an ArrayIndexOutOfBoundsException.
To fix this, you should use the minimum length of the two sets in both of the lines where you use the maximum. This way, you will be taking the union:
s1: {0 1 0 0 1 1} length = 6
s2: {1 1 1 0 0 0 1 0 1 1 0 1} length = 12
union: {1 1 1 0 1 1} length = min(6, 12) = 6
Instead of
s1: {0 1 0 0 1 1}! ! ! ! ! ! length = 6; no elements 7 through 12!
s2: {1 1 1 0 0 0 1 0 1 1 0 1} length = 12
union: {1 1 1 0 1 1 ! ! ! ! ! !} length = max(6, 12) = 12 -> errors
This leaves the later section of s2 out of the union, as there are no corresponding elements in s1 to perform a boolean || with. However, you could also do something like this:
IntegerSet union = new IntegerSet(Math.max(s1.dMax, s2.dMax));
for (int i = 0; i < (Math.max(s1.dMax, s2.dMax)); i++) {
if (s2.set.length > s1.set.length)
{
union.set[i] = s2.set[i] || (i < s1.set.length ? s1.set[i] : false);
}
else
{
union.set[i] = s1.set[i] || (i < s2.set.length ? s2.set[i] : false);
}
}
This will use false's for every missing element in the shorter set, resulting in the union being:
s1: {0 1 0 0 1 1} length = 6
s2: {1 1 1 0 0 0 1 0 1 1 0 1} length = 12
union: {1 1 1 0 1 1 1 0 1 1 0 1} length = max(6, 12) = 12
All entries are simply copied from the longer set, since anything || false is itself.
This:
dMax = domainMax + 1;
should be:
dMax = domainMax;
Or just use set.length.
Your for-loop goes from 0 to the max size of s1 and s2. But because (in the situation that throws errors), one of your IntegerSets is smaller, the for-loop goes past the size of the smaller IntegerSet when retrieving all of the booleans from the larger IntegerSet
for (int i = 0; i < (Math.max(s1.dMax, s2.dMax)); i++) {
union.set[i] = (s1.set[i] || s2.set[i]);
}
If the size of one set is smaller than the max, then you are going to get an IndexOutOfBoundsException.
Example:
Say you have the following conditions:
s1 = {0, 1, 1, 0, 1}
s2 = {0, 0, 0}
s1.dMax = 5, so s1.set has a size of 5
s2.dMax = 3, so s2.set has a size of 3
In your for-loop, you are going to iterate from 0 to 4. When the following occurs:
i = 3
s1.set[i] = s1.set[3] //IndexOutOfBoundsException because the size of s1.set is 3.
You have a couple options of how you can fix this:
You can add a check in your for-loop to ensure that you aren't
going to get an IndexOutOfBoundsException for your smaller
IntegerSet and just set the index in union to the boolean value of your larger set.
You can pad to your smaller IntegerSet depending on what you want the
union to be for the blank indicies.
Use the min size of the two IntegerSets if you want to ignore the
extra booleans (Judging from the fact that you made the union set
the size of the max, I am guess that is not the case)
Side Note: You also do not need to add the +1 to the dMax variable when you create your union IntegerSet. That is going to result in your having an extra index that you don't need and could cause problems later.
You are using dMax as the number of boolean array elements in one place, and as the domain maximum (wrong) when you create the new union set. (That causes you to make union 1 element too large.)
Several things to consider when you rewrite your code:
Assuming domainMax implies a set of integers from [0, domainMax], do define a boolean array with domainMax+1 elements (don't bother with a dMax variable, you can get that info by checking set.length
In the "union" function, be sure to handle the case where one or both of the input IntegerSets is null
Use protected set to make sure the internal representation of the IntegerSet is not known or accessed outside your class
When you perform the union of two non-null IntegerSets, only perform the or operation (||) for the case where both sets have values; then for the "bigger" set, simply copy over the remaining items.

Categories

Resources