Expected number of maxima - java

I have is algorithm, which takes an array as an argument, and returns its maximum value.
find_max(as) :=
max = as[0]
for i = 1 ... len(as) {
if max < as[i] then max = as[i]
}
return max
My question is: given that the array is initially in a (uniformly) random permutation and that all its elements are distinct, what's the expected number of times the max variable is updated (ignoring the initial assignment).
For example, if as = [1, 3, 2], then the number of updates to max would be 1 (when reading the value 3).

Assume the original array contains the values 1, 2, ..., N.
Let X_i, i = 1..N be random variables that take the value 1 if i is, at some point during the algorithm, the maximum value.
Then the number of maximums the algorithm takes is the random variable: M = X_1 + X_2 + ... + X_N.
The average is (by definition) E(M) = E(X_1 + X_2 + ... + X_N). Using linearity of expectation, this is E(X_1) + E(X_2) + .. + E(X_N), which is prob(1 appears as a max) + prob(2 appears as a max) + ... + prob(N appears as a max) (since each X_i takes the value 0 or 1).
When does i appear as a maximum? It's when it appears first in the array amongst the i, i+1, i+2, ..., N. The probability of this is 1/(N-i+1) (since each of those numbers are equally likely to be first).
So... prob(i appears as a max) = 1/(N-i+1), and the overall expectation is 1/N + 1/(N-1) + ..+ 1/3 + 1/2 + 1/1
This is Harmonic(N) which is approximated closely by ln(N) + emc where emc ~= 0.5772156649, the Euler-Mascheroni constant.
Since in the problem you don't count the initial setting of the maximum to the first value as a step, the actual answer is Harmonic(N) - 1, or approximately ln(N) - 0.4227843351.
A quick check for some simple cases:
N=1, only one permutation, and no maximum updates. Harmonic(1) - 1 = 0.
N=2, permutations are [1, 2] and [2, 1]. The first updates the maximum once, the second zero times, so the average is 1/2. Harmonic(2) - 1 = 1/2.
N=3, permutations are [1, 2, 3], [1, 3, 2], [2, 1, 3], [2, 3, 1], [3, 1, 2], [3, 2, 1]. Maximum updates are 2, 1, 1, 1, 0, 0 respectively. Average is (2+1+1+1)/6 = 5/6. Harmonic(3) - 1 = 1/2 + 1/3 = 5/6.
So the theoretical answer looks good!

Empirical Solution
A simulation of many different array sizes with multiple trials each can be performed and analyzed:
#include <iostream>
#include <fstream>
#include <cstdlib>
#define UPTO 10000
#define TRIALS 100
using namespace std;
int arr[UPTO];
int main(void){
ofstream outfile ("tabsep.txt");
for(int i = 1; i < UPTO; i++){
int sum = 0;
for(int iter = 0; iter < TRIALS; iter++){
for(int j = 0; j < i; j++){
arr[j] = rand();
}
int max = arr[0];
int times_changed = 0;
for(int j = 0; j < i; j++){
if (arr[j] > max){
max = arr[j];
times_changed++;
}
}
sum += times_changed;
}
int avg = sum/TRIALS;
outfile << i << "\t" << avg << "\n";
cout << "\r" << i;
}
outfile.close();
cout << endl;
return 0;
}
When I graphed these results, the complexity appeared to be logarithmic:
I think it's safe to conclude that the time complexity is O(log n).
Theoretical solution:
Assume that the numbers are in the range 0...n
You have a tentative maximum m
The next maximum will be a random number in the range m+1...n, which averages out to be (m+n)/2
This means that each time you find a new maximum, you are dividing the range of possible maximums by 2
Repeated division is equivalent to a logarithm
Therefore the number of times a new maximum is found is O(log n)

Worst case scenario (which is often what is sought) is O(n). If the list is sorted in reverse order every single one will result in an assignment.
HOWEVER, if your assignment is the most expensive operation why don't you just store it's index and only ever copy once, if at all? In that case, you will have exactly 1 assignment and n-1 comparisons.

Related

Calculate all possible sums in an array from its sub arrays

I have an array of numbers, now I have to find sum of elements by generating all the possible subarrays of the given array and applying some conditions.
The condition is for each subarray get the minimum and also find the total of elements in it and multiply both (minimum * total). Finally, add all these multiplied values for all subarrays.
Here is the problem statement:
Find the sum of all possible sub-arrays using the below formula:
Sum(left, right) = (min of arr[i]) * (∑ arr[i]), where i ranges from
left to right.
Example:
Array = [2,3,2,1]
The sub arrays are: [start_index, end_index]
[0,0] subarray = [2], min is 2 and total of items = 2. min * total = 2*2=4
[0,1] subarray = [2,3], min is 2 and total of items = 5. min * total = 2*5=10
[0,2] subarray = [2,3,2], min is 2 and total of items = 7. min * total = 2*7=14
[0,3] subarray = [2,3,2,1], min is 1 and total of items = 8. min * total = 1*8=8
[1,1] subarray = [3], min is 3 and total of items = 3. min * total = 3*3 = 9
[1,2] subarray = [3,2], min is 2 and total of items = 5. min * total = 2*5 = 10
[1,3] subarray = [3,2,1], min is 1 and total of items = 6. min * total = 1*6 = 6
[2,2] subarray = [2], min is 2 and total of items = 2. min * total = 2*2 = 4
[2,3] subarray = [2,1], min is 1 and total of items = 3. min * total = 1*3 = 3
[3,3] subarray = [1], min is 1 and total of items = 1. min * total = 1*1 = 1
Total = 4 + 10 + 14 + 8 + 9 + 10+ 6 + 4 + 3 + 1 = 69
So the answer is 69 in this case.
Constraints:
Each array element is in range 1 to 10^9. Array size 1 to 10^5. Return response in modulo 10^9+7
This is the code I tried.
public static int process(List<Integer> list) {
int n = list.size();
int mod = 7 + 1000_000_000;
long result = 0;
for (int i = 0; i < n; i++) {
long total = 0;
int min = list.get(i);
for (int j = i; j < n; j++) {
int p = list.get(j);
total = (total + p) % mod;
min = Math.min(min, p);
result = (result + (min * total) % mod) % mod;
}
}
return (int) result;
}
I want to reduce the time complexity of this algorithm?
What can be a better approach to solve this task?
Update:
David Eisenstat has given a great answer, but Im finding it to difficult to understand and come with a Java program, can someone provide a java solution for the approach or provide a pseudo code so i can come up with a program.
As user1984 observes, we can't achieve o(n²) by doing constant work for each sub-array. Here's how we get to O(n).
The sub-array minimum is the hardest factor to deal with, so we factor it out. Assume that the elements are pairwise distinct to avoid double counting in the math below (the code won't change). Letting A range over sub-arrays and x over elements,
sum_{A} [(sum_{y in A} y) (min A)] =
sum_{x} [x sum_{A such that min(A) = x} (sum_{y in A} y)].
Focusing on sum_{A | min(A) = x} (sum_{y in A} y) first, the picture is that we have a sub-array like
a b x c d e
where the element to the left of a (if it exists) is less than x, the element to the right of e (if it exists) is less than x, and all of the elements shown are greater than x. We want to sum over all sub-sub-arrays containing x.
a b x
b x
x
a b x c
b x c
x c
a b x c d
b x c d
x c d
a b x c d e
b x c d e
x c d e
We still don't have time to sum over these sub-sub-arrays, but fortunately there's a pattern. Here are the number of times each element appears in a sub-sub-array.
a: 4 = 1 * 4 appearances
b: 8 = 2 * 4 appearances
x: 12 = 3 * 4 appearances
c: 9 = 3 * 3 appearances
d: 6 = 3 * 2 appearances
e: 3 = 3 * 1 appearances
This insight reduces the processing time for one sub-array to O(n), but there are still n sub-arrays, so we need two more ideas.
Now is the right time to figure out what the sub-arrays look like. The first sub-array is the whole array. We split this array at the minimum element and recursively investigate the left sub-array and the right separately.
This recursive structure is captured by the labeled binary tree where
The in-order traversal is the array elements in order;
Every node has a label less than its children. (I'm still assuming distinct elements. In practice what we can do is to declare the array index to be a tiebreaker.)
This is called a treap, and it can be constructed in linear time by an algorithm with a family resemblance to precedence parsing. For the array [3,1,4,5,9,2,6], for example, see below.
1
/ \
3 2
/ \
4 6
\
5
\
9
The final piece is being able to aggregate the sum patterns above. Specifically, we want to implement an API that might look like this in C++:
class ArraySummary {
public:
// Constructs an object with underlying array [x].
ArraySummary(int x);
// Returns an object representing the concatenation of the underlying arrays.
ArraySummary Concatenate(ArraySummary that);
// Returns the sum over i of (i+1)*array[i].
int WeirdSum();
};
The point of this interface is that we don't actually need to store the whole array to implement WeirdSum(). If we store
The length length of the underlying array,
The usual sum sum of the underlying array,
The weird sum weird_sum of the underlying array;
then we can implement the constructor as
length = 1;
sum = x;
weird_sum = x;
and Concatenate() as
length = length1 + length2;
sum = sum1 + sum2;
weird_sum = weird_sum1 + weird_sum2 + length1 * sum2;
We need two of these, one in each direction. Then it's just a depth-first traversal (actually if you implement precedence parsing, it's just bottom-up).
Your current solution has time complexity O(n^2), assuming that list.get is O(1). There are exactly 1 + 2 + ... + n-1 + n operations which can be expressed as n * (n + 1)/2, hence O(n^2).
Interestingly, n * (n + 1)/2 is the number of sub arrays that you can get from an array of length n, as defined in your question and evident from your code.
This implies that you are doing one operation per sub array and this is the requird minimum operations for this task, since you need to look at least once at each sub array.
My conclusion is that it isn't possible to reduce the time complexity of this task, unless there is some mathematical formula that helps to do so.
This doesn't necessary mean that there aren't ways to optimize the code, but that would need testing and may be language specific. Regardless, it wouldn't change the time complexity in terms of n where n is the length of the input array.
Appreciate any input on my logic. I'm learning myself.
The answer provided by David Eisenstat is very efficient with complexity of O(n).
I would like to share another approach, that although it has time complexity of O(n^2), it may be more simple and may be easier for some (me included) to fully understand.
Algorithm
initiate two dimensional array of size matrix[n][n], each cell will hold pair of Integers <sum, min>. we will denote for each Matrix[i, j] the first element of the pair as Matrix[i, j].sum and the second as Matrix[i, j].min
Initiate the diagonal of the matrix as follows:
for i in [0, n-1]:
Matrix[i][i] = <arr[i], arr[i]>
for i in [0, n-1]:
for j in[i, n-1]:
Matrix[i, j] = <
Matrix[i - 1, j].sum + arr[i, j],
Min(Matrix[i - 1, j].min, arr[i, j])
>
Calculate the result:
result = 0
for i in [0, n-1]:
for j in[i, n-1]:
result += Matrix[i, j].sum * Matrix[i, j].min
Time Complexity Analysis
Step 1: initiating two dimensional array ofsize [n,n] will take in theory O(n^2) as it may require to initiate all indices to 0, but if we skip the initialization of each cell and just allocate the memory this could take O(1)
Step 2 : Here we iterate from 0 to n-1 doing constant work each iteration and therefore the time complexity is O(n)
Step 3: Here we iterate over half of the matrix cells(all that are right of the diagonal), doing constant work each iteration, and therefore the time complexity is O((n - 1) + (n - 2) + .... + (1) + (0)) = O(n^2)
Step 4: Similar analysis to step 3, O(n^2)
In total we get O(n^2)
Explanation for solution
This is simple example of Dynamic programming approach.
Let's define sub[i, j] as the subarray between index i and j while 0 =< i, j <= n-1
Then:
Matrix[i, j].sum = sum x in sub[i, j]
Matrix[i, j].min = min x in sub[i, j]
Why?
for sub[i,i] it's obvious that:
sum x in sub[i, i] = arr[i]
min x in sub[i, i] = arr[i]
Just like we calculate in step 2.
Convince yourself that:
sum sub[i,j] = sum sub[i-1,j] + arr[i, j]
min sub[i,j] = Min(min sub[i-1,j], arr[i, j])
This explains step 3.
In Step 4 we just sums up everything to get the required result.
It can be with the O(n) solution.
Intuition
First of all, we want to achieve all subarrays like this.
a1 a2 a3 min b1 b2 b3 where min is minimum. We will use a monotonic increasing stack to achieve it. In every iteration, if the stack's top value is greater than the next element, we will pop the stack and calculate the sum until the condition is not met.
Secondly, we want to figure out how to calculate the total sum if we have an a1 a2 a3 min b1 b2 b3 subarray. Here, we will use a prefix of prefix sum.
Prefix Sum
At first, we need the prefix sum. Assume that p indicates prefix sum, we want to achieve p1 p2 p3 p4 p5 p6 p7. Our prefix sum will be like this;
p1: a1
p2: a1 + a2
p3: a1 + a2 + a3
.
p6 : a1 + a2 + a3 + min + b1 + b2
p7: a1 + a2 + a3 + min + b1 + b2 + b3
Within prefix sum now we can calculate the sum of between two indexes. The sum of (start, end] is pend - pstart. If start: 1 and end: 3 that means p3 - p1 = (a1 + a2 + a3) - (a1) = a2 + a3.
Prefix of Prefix Sum
How can we calculate all possible subarray sums that include our min value?
We separate this calculation to the left side and right side.
The left side included min will be a1 a2 a3 min.
The right side included min will be min b1 b2 b3.
For example, some of the possible sums can be:
a1 + a2 + a3 + min
a1 + a2 + a3 + min + b1
a3 + min + b1 + b2 + b3
min + b1 + b2 + b3
We need to find all the [bj, ai] sums. Where i means all the left side indexes and j means all the right side indexes. Now We need to use the prefix of prefix sum. It will give us all possible sums between two indexes. Let's say P. It will be sum(Pj) - sum(Pi).
Now, how do we calculate our sum(Pj) - sum(Pi)?
So Pj is P7 - P4. It is the right side possible sum.
Same way Pi is P4 - P1. It is the left side possible sum.
How many combinations for sum(Pj) are there?
leftSize * (P7 - P4). Same way for sum(Pi) it will be rightSize * (P4 - P1).
Final equation to calculate subarray [a1 a2 a3 min b1 b2 b3] is: min * ((leftSize * (P7 - P4)) - (rightSize * (P4 - P1))).
Algorithm
public static int process(List<Integer> list) {
int n = list.size();
int mod = (int) 1e9 + 7;
int[] preSum = new int[n + 2];
Deque<Integer> stack = new ArrayDeque<>();
int pre = 0;
int result = 0;
for (int i = 0; i <= n; i++) {
int num = i < n ? list.get(i) : 0;
// current prefix sum
pre = (pre + num) % mod;
// prefix of prefix sum array
preSum[i + 1] = (preSum[i] + pre) % mod;
while (!stack.isEmpty() && list.get(stack.peek()) > num) {
int mid = stack.pop();
int left = stack.isEmpty() ? -1 : stack.peek();
int lSize = mid - left;
int rSize = i - mid;
long lSum = left < 0 ? preSum[mid] : preSum[mid] - preSum[left];
long rSum = preSum[i] - preSum[mid];
result = (int) (result + (list.get(mid) * ((rSum * lSize - lSum * rSize) % mod)) % mod) % mod;
}
stack.push(i);
}
return (result + mod) % mod;
}
Time complexity: O(n)
Space complexity: O(n)
References
Thanks to #lee215 for one pass solution.
Thanks to #forAc for the explanation of the final equation.
https://leetcode.com/problems/sum-of-total-strength-of-wizards/discuss/2061985/JavaC%2B%2BPython-One-Pass-Solution

How can I efficiently compute the sum of parts of an array of sizes in an arithmetic sequence?

I am trying to find the sum of parts of a given array with a length that is the sum of the first N positive integers for some whole number N. The size of each part for which I am to find the sum are the numbers in said arithmetic sequence. For instance, for an array of length 10, I need to find the sum of the first number, the next two numbers, and so on, until the next N numbers.
Example Input:
[1,4,5,2,6,7,9,8,7,10]
Example Output:
[1,9,15,34]//1, 4+5, 2+6+7, 9+8+7+10
Explanation:
The first sum is 1, the first element (index 0). The sum of the next two numbers is 4 + 5 = 9 (index 1 and 2). The sum of the next three numbers is 2 + 6 + 7 = 15 (index 3, 4, and 5). The sum of the last four numbers is 9 + 8 + 7 + 10 = 34 (index 6, 7, 8, 9).
You can compute the size of the result array using the formula for the sum of an arithmetic sequence, i.e. n(n + 1) / 2.
A prefix sum array can be applied here so so that any range sum can be computed in O(1) time with O(n) precomputation time and space (which is also the overall complexity of this algorithm).
final int[] input = { 1, 4, 5, 2, 6, 7, 9, 8, 7, 10 };
// size * (size + 1) / 2 = input.length
final int size = (-1 + (int) Math.sqrt(1 + 8 * input.length)) / 2;
// derived by quadratic formula
final int[] result = new int[size];
final int[] sum = new int[input.length + 1];
for (int i = 1; i <= input.length; i++) {
sum[i] = sum[i - 1] + input[i - 1];
}
for (int i = 1, j = 0; i <= input.length; i += ++j) {
result[j] = sum[i + j] - sum[i - 1];
}
System.out.println(Arrays.toString(result));
Ideone Demo
The following algorithm is very efficient and does not rely on the summation formula to work (as you had asked about) other than to compute the length of the result array. This should not be a problem since it is basic algebra. If you use a List implementation you would not have to do that.
It also only sums only to the max allowed by the given array. So if you provide an array like
1 2 3 4 5 6 7 8 9 10 11 12 13
It will silently ignore 11 12 and 13 since they don't comprise enough values to continue.
Here is the algorithm with your original data set and the output.
int[] arr = { 1, 4, 5, 2, 6, 7, 9, 8, 7, 10 };
int start = 0; // start of group
int end = 0; // end of group
int[] sol = new int[(int)(-.5 + Math.sqrt(2*arr.length + .25))];
for (int i = 1; i <= sol.length; i++) {
// initialize the sum
int sum = 0;
// compute next end
end += i;
// and sum from start to end
for (int k = start; k < end; k++) {
sum += arr[k];
}
// old end becomes next start
start = end;
sol[i-1] = sum;
}
Prints
[1, 9, 15, 34]

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.

Finding a Prime Numbers

I have an array of length N=10^5 For each index 1<=i<=n I have to calculate the difference between A[j]-A[i] and (j-i) is prime and j>i
Here is my code:
for(int i=1;i<=n;i++){
for(int j=0;j<prime.size();j++){
int x = prime.get(j);
if(x+i>n) break;
ans+= A[x+i]-A[i];
}
}
How should i make this work even faster ? I think the time complexity is O(N*prime.size)
First, I will rephrase your question so that it states what I believe you want to achieve. You are probably looking for the sum of the differences of the form A[j]-A[i], where (j-i) is a "positive" prime and 1<=i<j<=N. With this statement in mind...
We count the number of times A[k] is added to the sum (denoted by p) and the number of times A[k] is subtracted from the sum (denoted by m). Well, m is equal to the number of primes in the interval [1,N-k], while p is equal to the number of primes in the interval [1,k-1]. If you don't believe me, simulate step-by-step what your code does. Then you can do:
S = 0
for k = 1 to N do
S = S + (p(k) - m(k)) * A[k]
endfor
Now, we need to find a way to determine p and m efficiently for each k in the interval [1,N]. I see you have already constructed what seems to be an ordered list of primes. So, to answer a query of the form 'how many primes in the interval [1,t]?' you could perform a binary search on that list for t. This would get the complexity down to O(N*log(prime.size)).
As an alternative, you can pre-compute the answers to the queries of the form 'how many primes in the interval [1,t]?'. You need an extra array nrPrimesLessThan of size N to keep the results, doing something like this to compute its values:
count = 0
for i = 1 to N do
if i < prime.get(count) then
nrPrimesLessThan[i] = count
else
count = count + 1
nrPrimesLessThan[i] = count
endif
endfor
The pre-computation part takes O(N) steps, but now one query takes O(1) steps, thus the calculating the sum takes O(N) steps. Overall, linear time in N.
Judging from your code example, you want to sum the differences of all value pairs in the array for which the difference of the indices is prime. You've got already an array of primes.
The diagram below shows how the elements get subtracted and added:
0 1 2 3 4 5 6 7 8 9
- + + + + 0
- + + + + 1
- + + + + 2
- + + + 3
- + + + 4
- + + 5
- + + 6
- + 7
- 8
- 9
A + means an element is added to the overall sum. A - means the element is subtracted from the sum. This is not a single subtraction; the subtraction happens for each addition to its left, so A[0] is subtracted 4 times. It is never added.
On the other hand, A[9] is never subtracted, but added four times. In general, each element is subtracted as many times as there are plusses in a row and it is added as many times as there are plusses in a columns. There is a symmetry here:
add[i] = sub[N - i - 1]
for zero-based indices. What is the value of add[i]? It is the number of primes that are smaller or equal to i.
Here's a code example where the add array is called m:
int psum2(const int A[], int n)
{
int sum = 0;
int m[n];
int j = 0;
int k = 0;
for (int i = 0; i < n; i++) {
if (i == prime[j]) {
k++;
j++;
}
m[i] = k;
}
for (int i = 0; i < n; i++) {
sum += (m[i] - m[n - i - 1]) * A[i];
}
return sum;
}
The array m is always the same and can be precalculated if you need to perform the sum more often. The algorithm is O(n).
The problem is also unrelated to primes at its core. The method above works for all conditional sums of differences where the difference of the indices must be conteined in a certain set of numbers.

Java performance exercise: 0%

I made this test to challenge myself while learning a bit of Java, and I got the worst possible result in the performance test, 0%.
This was the exercise:
You are given two non-empty zero-indexed arrays A and B consisting of
N integers. These arrays represent N planks. More precisely, A[K] is
the start and B[K] the end of the K−th plank.
Next, you are given a non-empty zero-indexed array C consisting of M
integers. This array represents M nails. More precisely, C[I] is the
position where you can hammer in the I−th nail.
We say that a plank (A[K], B[K]) is nailed if there exists a nail C[I]
such that A[K] ≤ C[I] ≤ B[K].
The goal is to find the minimum number of nails that must be used
until all the planks are nailed. In other words, you should find a
value J such that all planks will be nailed after using only the first
J nails. More precisely, for every plank (A[K], B[K]) such that 0 ≤ K
< N, there should exist a nail C[I] such that I < J and A[K] ≤ C[I] ≤
B[K].
For example, given arrays A, B such that:
A[0] = 1 B[0] = 4
A[1] = 4 B[1] = 5
A[2] = 5 B[2] = 9
A[3] = 8 B[3] = 10 four planks are represented: [1, 4], [4, 5], [5, 9] and [8, 10].
Given array C such that:
C[0] = 4
C[1] = 6
C[2] = 7
C[3] = 10
C[4] = 2 if we use the following nails:
0, then planks [1, 4] and [4, 5] will both be nailed. 0, 1, then
planks [1, 4], [4, 5] and [5, 9] will be nailed. 0, 1, 2, then planks
[1, 4], [4, 5] and [5, 9] will be nailed. 0, 1, 2, 3, then all the
planks will be nailed. Thus, four is the minimum number of nails that,
used sequentially, allow all the planks to be nailed.
Write a function:
class Solution { public int solution(int[] A, int[] B, int[] C); }
that, given two non-empty zero-indexed arrays A and B consisting of N
integers and a non-empty zero-indexed array C consisting of M
integers, returns the minimum number of nails that, used sequentially,
allow all the planks to be nailed.
If it is not possible to nail all the planks, the function should
return −1.
For example, given arrays A, B, C such that:
A[0] = 1 B[0] = 4
A[1] = 4 B[1] = 5
A[2] = 5 B[2] = 9
A[3] = 8 B[3] = 10
C[0] = 4
C[1] = 6
C[2] = 7
C[3] = 10
C[4] = 2 the function should return 4, as explained above.
Assume that:
N and M are integers within the range [1..30,000]; each element of
arrays A, B, C is an integer within the range [1..2*M]; A[K] ≤ B[K].
Complexity:
expected worst-case time complexity is O((N+M)*log(M)); expected
worst-case space complexity is O(M), beyond input storage (not
counting the storage required for input arguments). Elements of input
arrays can be modified.
Here is my solution:
class Solution {
public int solution(int[] A, int[] B, int[] C) {
int result = 0;
int empties = 0;
for(int i = 0; i < C.length; i ++){
for(int j = 0; j < A.length; j ++){
if(A[j] != 0){
if(C[i] >= A[j] && C[i] <= B[j]){
A[j] = B[j] = 0;
empties ++;
}
}
if(empties == A.length){
return i + 1;
}
}
}
return -1;
}
}
This is the link of the result: https://codility.com/demo/results/trainingXXEXMW-KVJ/
Questions:
First, I don't understand why my performance is measured O((N + M) * N) and not O(M * N), since I'm doing a for (M) and inside a for(N). Disclaimer, I only learned about Big O notation a couple of days ago.
Second, most likely the reason why the performance was bad was because I didn't use a binary search to find the nail-able elements, instead I looped through them.
However, I did that on purpose since in no part of the exercise is mentioned that the A and B arrays are sorted, in a way that 1 >= A[K] >= A[K+1]. And if I sorted those arrays, then the performance would be bad (I guess, no idea how much the sort hurts the performance honestly, just a guesstimate).
What is your opinion about it?.
I don't understand why my performance is measured O((N + M) * N) and not O(M * N)
They are probably doing curve fitting against a limited number of curves. (This is the problem with trying to determine complexity empirically.)
And if I sorted those arrays, then the performance would be bad (I guess, no idea how much the sort hurts the performance honestly, just a guesstimate).
Actually, sorting will be O(NlogN) if done with a good algorithm. So from a complexity perspective you could achieve the O(NlogN) overall worst-case by sorting and then doing a binary search. (I'm not saying it is the right solution though ....)

Categories

Resources