This isn't really a homework, but rather a practice and optimization, but this seemed like the best section for this type of questions.
It is a dynamical programming issue, and it's the following:
-Given an unsorted array of N elements, pick K number of elements from it, such that their absolute difference is the largest.
An absolute difference is calculated between adjacent elements here. So if we have an array of 5 elements: 1 5 3 2 1, and k = 3, the absolute differences would be:
1 5 3 = |5-1| + |3-5| = 6
1 5 2 = |5-1| + |2-5| = 7
1 5 1 = [5-1| + |1-5| = 8
etc
With 1 5 1 being the largest and needed one with 8
What i've tried so far is solving this by finding all possible combinations of K numbers with a recursion and then returning the biggest(brute force).
This showed as a terrible idea, because when tried with an array of N=50 and k=25 for example, there are 1.264106064E+14 combinations.
The recursion i used is a simple one used for printing all K-digit integers from an array, just instead of printing them, keeping them in an array:
static void solve(int[] numbers, int k, int startPosition, int[] result) {
if (k == 0) {
System.out.println(absoluteDifferenceSum(result));
return;
}
for (int i = startPosition; i <= numbers.length - k; i++) {
result[result.length - k] = numbers[i];
solve(numbers, k - 1, i + 1, result);
}
}
What I want to achieve is the optimal complexity (which i suppose can't be lower than O(n^2) here, but i'm out of ideas and don't know how to start. Any help is appreciated!
Generally, we can have a naive O(n^2 * k) formulation, where f(i, k) represents the best result for selecting k elements when the ith element is rightmost in the selection,
f(i, k) = max(
f(j, k - 1) + abs(Ai - Aj)
)
for all j < i
which we can expand to
max( f(j, k - 1) + Ai - Aj )
= Ai + max(f(j, k - 1) - Aj)
when Ai >= Aj
and
max( f(j, k - 1) + Aj - Ai )
= -Ai + max(f(j, k - 1) + Aj)
when Ai < Aj
Since the right summand is independent of Ai, we can build a tree with nodes that store both f(j, k - 1) - Aj, as well as f(j, k - 1) + Aj. Additionally, we'll store in each node both maxes for each subtree. We'll need O(k) trees. Let's skip to the examination of the tree for k = 2 when we reach the last element:
1
5 -> 4 -> (-1, 9)
3 -> 2 -> (-1, 5)
2 -> 3 -> (-1, 5)
3 -> (-3, 5)
tree for k = 2 so far:
3 (-1, 5)
/ \
2 (-1, 5) 5 (-1, 9)
1 is less than 3 so we first add -1
to the max for the right subtree
stored for f(j, k - 1) + Aj
-1 + 9 = 8
(note that this represents {1,5,1})
then we continue left in the tree
and compare 8 to a similar calculation
with node 2: -1 + 5 = 4
(note that this represents {5,2,1})
This way, we can reduce the time complexity to O(n log n * k) with space O(n * k).
Related
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
This may sound very naive question. Please excuse me for that. I was working on a problem that involved binary search. And the general way to do that I had learned is
set low = 0, set high = array.Length - 1
while(low < high)
mid = (low+high)/2
if (array[mid] == element) return mid
if (array[mid] > element) set start = mid +1
if array[mid] < element ) set high = mid
Please note the way i am setting the mid point. But some of example/solution that I saw sets the mid differently and I am not able to wrap my head around that. Please see the code snippet below. Any explanation what does setting the mid = l + (r-l)/2 mean would be greatly appreciated.
int BinarySearch(int A[], int l, int r, int key)
{
int m;
while( l <= r )
{
m = l + (r-l)/2;
if( A[m] == key ) // first comparison
return m;
if( A[m] < key ) // second comparison
l = m + 1;
else
r = m - 1;
}
return -1;
}
The goal in both cases is to find the middle element.
(low + high) / 2: The sum of the left and right indices is divided by 2 to get the mid point. The problem with this is that the sum could overflow.
If the sum is an even number, we pick the lower one. Example - With left and right as [2, 5] - We pick mid as 3
l + (r - l) / 2: The difference between the right and the left index is found and is divided by 2. It is then added to the left index to find the midpoint. It might be easier to visualize this with an example.
Example : [5, 11] -> (11 - 5) / 2 = 3 The middle in this interval is now 3 places or hops from the left index. So, add the left index to find the index of the mid element which is 5 + 3 = 8
Middle is equal to the starting index of the left value plus the range between right and left divided by two.
Say our range is 5 to 10.
5 + (10-5)/2 == 5 + 5/2 == 5 + 2.5 == 7.5, the middle of our range
This trick is done to avoid integer overflow problem .
(i)use unsinged right shift operator
int mid=(high+low)>>>1
or
(ii) int mid=low +(high-low)/2
(low+high)/2 will overflow if the sum exceeds Integer.MAX_VALUE and you will get wrong results or an RuntimeException
You can read this article by Josh Bloch:
https://ai.googleblog.com/2006/06/extra-extra-read-all-about-it-nearly.html
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
How do I get the kth combination inNCR. without iterating through all possible outcomes. e.g. say I have 3C2 for 3 positions and 2identical-items. I am aware it's [011],[101] and [110]. how do I get e.g. the 2nd term(k=1) which is [101] using a method?
constraints(R < N k >= 0 and k < P where P = NCR).
NB:[101] is the 2nd term(in ascending/lexicographical order) because 011 = 3,101 = 5 ,110 = 6
in decimal. so basically the goal is to get what number k in NCR is,
because every kth output from NCR can be represented as a number.
Yes, you are correct when you say:
because every kth output from NCR can be represented as a number.
There is a bijection from the set of integers 1 to # of combs/perms to the entire set of combs/perms. Finding the specific index of a particular comb/perm is sometimes referred to as getting the rank. According to the example that you have in your question, these are ordinary permutations. Moreover when you mention ascending order, you are referring to the lexicographical order.
It is a straightforward exercise in counting to obtain the nth ordinary permutation of a given set. We first need to obtain the total number of permutations using the well established formula:
P(n, r) = n! / (n - r)!
This next part is the key observation that allows us to quickly obtain each element of our target permutation.
If we look at all permutations of our set of n choose r, there will be n groups that are only different by a permutation of the n elements.
For example, if we look at the first two group of the permutations of [0 1 2 3] choose 3, we have:
[,0] [,1] [,2]
[0,] 0 1 2
[1,] 0 1 3
[2,] 0 2 1
[3,] 0 2 3
[4,] 0 3 1
[5,] 0 3 2
[6,] 1 0 2
[7,] 1 0 3
[8,] 1 2 0
[9,] 1 2 3
[10,] 1 3 0
[11,] 1 3 2
Note that the last permutations are simply the first 6 permutations of the set [1 0 2 3].. that is, 0 is mapped to 1, 1 is mapped to 0, and the final 2 elements are mapped to themselves.
This pattern continues as we move to the right only instead of n identical groups, we will get n - 1 similar groups for the second column, n -2 for the third, and so on.
So to determine the first element of our permutation, we need to determine the 1st group. We do that by simply dividing the number of permutations by n. For our example above of permutations of 4 choose 3, if we were looking for the 15th permutation, we have the following for the first element:
Possible indices : [0 1 2 3]
P(4, 3) = 24
24 / 4 = 6 (elements per group)
15 / 6 = 2 (integer division) 2 means the 3rd element here (base zero)
Now that we have used the 3rd element, we need to remove it from our array of possible indices. How do we get the next element?
Easy, we get our next subindex by subtracting the product of the group we just found and the elements per group from our original index.
Possible indices : [0 1 3]
Next index is 15 - 6 * 2 = 3
Now, we just repeat this until we have filled all entries:
Possible indices : [0 1 3]
Second element
6 / 3 = 2 (elements per group)
3 / 2 = 1
Next index is 3 - 3 * 1 = 0
Possible indices : [0 3]
Third element
2 / 2 = 1
0 / 1 = 0
So our 15th element is : [2 1 0]
Here is a C++ implementation that should be pretty easy to translate to Java:
double NumPermsNoRep(int n, int k) {
double result = 1;
double i, m = n - k;
for (i = n; i > m; --i)
result *= i;
return result;
}
std::vector<int> nthPermutation(int n, int r, double myIndex) {
int j = 0, n1 = n;
double temp, index1 = myIndex;
std::vector<int> res(r);
temp = NumPermsNoRep(n, r);
std::vector<int> indexVec(n);
std::iota(indexVec.begin(), indexVec.end(), 0);
for (int k = 0; k < r; ++k, --n1) {
temp /= n1;
j = (int) std::trunc(index1 / temp);
res[k] = indexVec[j];
index1 -= (temp * (double) j);
indexVec.erase(indexVec.begin() + j);
}
}
These concepts extends to other types of combinatorial problems, such as finding the nth combination, or permutation with repetition, etc.
The time complexity is O(kn), space is O(n)
public static void main(String[] args) {
//n = 4, r = 2, k = 3
int[] ret1 = getKthPermutation(4, 2, 3);
//ret1 is [1,0,0,1]
//n = 3, r = 2, k = 1
int[] ret2 = getKthPermutation(3, 2, 1);
//ret2 is [1,0,1]
}
static int[] getKthPermutation(int n, int r, int k) {
int[] array = new int[n];
setLastN(array, r, 1);
int lastIndex = n - 1;
for(int count = 0; count < k; count++) {
int indexOfLastOne = findIndexOfLast(array, lastIndex, 1);
int indexOfLastZero = findIndexOfLast(array, indexOfLastOne, 0);
array[indexOfLastOne] = 0;
array[indexOfLastZero] = 1;
//shortcut: swap the part after indexOfLastZero to keep them sorted
int h = indexOfLastZero + 1;
int e = lastIndex;
while(h < e) {
int temp = array[h];
array[h] = array[e];
array[e] = temp;
h++;
e--;
}
}
return array;
}
//starting from `from`, and traveling the array forward, find the first `value` and return its index.
static int findIndexOfLast(int[] array, int from, int value) {
for(int i = from; i > -1; i--)
if(array[i] == value) return i;
return -1;
}
//set the last n elements of an array to `value`
static void setLastN(int[] array, int n, int value){
for(int i = 0, l = array.length - 1; i < n; i++)
array[l - i] = value;
}
This is an adaption of the very typical "find the kth permation" algorithm.
I will try to explain the general idea (yours is a special case as there are only two types of elements: 0 and 1).
Lets say I have [2,1,6,4,7,5]. What is the next smallest permutation that is bigger than the current one? Why do I concern the next smallest permutation bigger than current one? Because if you start with the smallest permutation [1,2,4,5,6,7] and you repeat the action (find the smallest bigger than current) k times, you will find k+1 th smallest permutation.
Now, since the one I am looking for needs to be bigger than current one, I need to increment the current one. To keep the incrementation as small as possible, I am going to try to modify 5 (last one). Now, I cannot just change 5 to a random value, I can only swap it with some digit before it.
If I swap 5 with a bigger number before it, say 7, then I will get [2,1,6,4,5,7], which is smaller than current one. Now obviously I need to swap 5 with some smaller digit before it, but which one? If I swap 5 with 2, I get [5,1,6,4,7,2], this increment is too big. I need to swap 5 with a "lower digit" to keep the increment as small as possible. Thats leads us to find the first(lowest) digit (from right to left) that is smaller than 5. In this case I would need to swap 5 with 4 and get [2,1,6,5,7,4]. This way, I can make the impact of "swap" small. Now the prefix is decided [2,1,6,5. There is no smaller prefix. We need to deal with suffix 7,4]. Clearly, if we sort the suffix and make it 4,7], then we are done.
In our case, there are two differences:
1. we need to swap the last 1, because you cannot make the permutation bigger by swapping the a zero with any digit before it.
2. we can always sort the suffix using a shortcut as shown in the code. I will leave it to you:)
public static String lexicographicPermutation(String str, long n) {
final long[] factorials = { 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600 };
n--;
char[] arr = str.toCharArray();
for (int i = 0; i < arr.length - 1; i++) {
long fact = factorials[arr.length - i - 2];
long p = i + n / fact;
n %= fact;
for (int j = i + 1; j <= p; j++)
swap(arr, i, j);
}
return new String(arr);
}
private static void swap(char[] arr, int i, int j) {
char tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
You can replace STR with required string. In the given example, 1st permutation is "abcdefghijklm" (this is a string with 13 chars), 13!st permutation is reverse string "mlkjihgfedcba" and 100st permutation is "abcfklgmeihjd".
To realise this soulution just google Factorial number system. This is a key to solve this problem. This is a Project Euler: Problem 24.
Demo:
for(int i = 1; i <= 6; i++)
System.out.println(lexicographicPermutation("110", i));
1 - 110
2 - 101
3 - 110
4 - 101
5 - 011
6 - 011
for(int i = 1; i <= 6; i++)
System.out.println(lexicographicPermutation("abc", i));
1 - abc
2 - acb
3 - bac
4 - bca
5 - cab
6 - cba
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.
Given a string of even size, say:
abcdef123456
How would I interleave the two halves, such that the same string would become this:
a1b2c3d4e5f6
I tried attempting to develop an algorithm, but couldn't. Would anybody give me some hints as to how to proceed? I need to do this without creating extra string variables or arrays. One or two variable is fine.
I just don't want a working code (or algorithm), I need to develop an algorithm and prove it correctness mathematically.
You may be able to do it in O(N*log(N)) time:
Want: abcdefgh12345678 -> a1b2c3d4e5f6g7h8
a b c d e f g h
1 2 3 4 5 6 7 8
4 1-sized swaps:
a 1 c 3 e 5 g 7
b 2 d 4 f 6 h 8
a1 c3 e5 g7
b2 d4 f6 h8
2 2-sized swaps:
a1 b2 e5 f6
c3 d4 g7 h8
a1b2 e5f6
c3d4 g7h8
1 4-sized swap:
a1b2 c3d4
e5f6 g7h8
a1b2c3d4
e5f6g7h8
Implementation in C:
#include <stdio.h>
#include <string.h>
void swap(void* pa, void* pb, size_t sz)
{
char *p1 = pa, *p2 = pb;
while (sz--)
{
char tmp = *p1;
*p1++ = *p2;
*p2++ = tmp;
}
}
void interleave(char* s, size_t len)
{
size_t start, step, i, j;
if (len <= 2)
return;
if (len & (len - 1))
return; // only power of 2 lengths are supported
for (start = 1, step = 2;
step < len;
start *= 2, step *= 2)
{
for (i = start, j = len / 2;
i < len / 2;
i += step, j += step)
{
swap(s + i,
s + j,
step / 2);
}
}
}
char testData[][64 + 1] =
{
{ "Aa" },
{ "ABab" },
{ "ABCDabcd" },
{ "ABCDEFGHabcdefgh" },
{ "ABCDEFGHIJKLMNOPabcdefghijklmnop" },
{ "ABCDEFGHIJKLMNOPQRSTUVWXYZ0<({[/abcdefghijklmnopqrstuvwxyz1>)}]\\" },
};
int main(void)
{
unsigned i;
for (i = 0; i < sizeof(testData) / sizeof(testData[0]); i++)
{
printf("%s -> ", testData[i]);
interleave(testData[i], strlen(testData[i]));
printf("%s\n", testData[i]);
}
return 0;
}
Output (ideone):
Aa -> Aa
ABab -> AaBb
ABCDabcd -> AaBbCcDd
ABCDEFGHabcdefgh -> AaBbCcDdEeFfGgHh
ABCDEFGHIJKLMNOPabcdefghijklmnop -> AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPp
ABCDEFGHIJKLMNOPQRSTUVWXYZ0<({[/abcdefghijklmnopqrstuvwxyz1>)}]\ -> AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz01<>(){}[]/\
Generically that problem is quite hard -- and it reduces to finding permutation cycles. The number and length of those varies quite a lot depending on the length.
The first and last cycles are always degenerate; the 10 entry array has 2 cycles of lengths 6 and 2 and the 12 entry array has a single cycle of length 10.
Withing a cycle one does:
for (i=j; next=get_next(i) != j; i=next) swap(i,next);
Even though the function next can be implemented as some relatively easy formula of N, the problem is postponed to do book accounting of what indices have been swapped. In the left case of 10 entries, one should [quickly] find the starting positions of the cycles (they are e.g. 1 and 3).
Ok lets start over. Here is what we are going to do:
def interleave(string):
i = (len(string)/2) - 1
j = i+1
while(i > 0):
k = i
while(k < j):
tmp = string[k]
string[k] = string[k+1]
string[k+1] = tmp
k+=2 #increment by 2 since were swapping every OTHER character
i-=1 #move lower bound by one
j+=1 #move upper bound by one
Here is an example of what the program is going to do. We are going to use variables i,j,k. i and j will be the lower and upper bounds respectively, where k is going to be the index at which we swap.
Example
`abcd1234`
i = 3 //got this from (length(string)/2) -1
j = 4 //this is really i+1 to begin with
k = 3 //k always starts off reset to whatever i is
swap d and 1
increment k by 2 (k = 3 + 2 = 5), since k > j we stop swapping
result `abc1d234` after the first swap
i = 3 - 1 //decrement i
j = 4 + 1 //increment j
k= 2 //reset k to i
swap c and 1, increment k (k = 2 + 2 = 4), we can swap again since k < j
swap d and 2, increment k (k = 4 + 2 = 6), k > j so we stop
//notice at EACH SWAP, the swap is occurring at index `k` and `k+1`
result `ab1c2d34`
i = 2 - 1
j = 5 + 1
k = 1
swap b and 1, increment k (k = 1 + 2 = 3), k < j so continue
swap c and 2, increment k (k = 3 + 2 = 5), k < j so continue
swap d and 3, increment k (k = 5 + 2 = 7), k > j so were done
result `a1b2c3d4`
As for proving program correctness, see this link. It explains how to prove this is correct by means of a loop invariant.
A rough proof would be the following:
Initialization: Prior to the first iteration of the loop we can see that i is set to
(length(string)/2) - 1. We can see that i <= length(string) before we enter the loop.
Maintenance. After each iteration, i is decremented (i = i-1, i=i-2,...) and there must be a point at which i<length(string).
Termination: Since i is a decreasing sequence of positive integers, the loop invariant i > 0 will eventually equate to false and the loop will exit.
The solution is here J. Ellis and M. Markov. In-situ, stable merging by way of perfect shuffle.
The Computer Journal. 43(1):40-53, (2000).
Also see the various discussions here:
https://cs.stackexchange.com/questions/332/in-place-algorithm-for-interleaving-an-array/400#400
https://cstheory.stackexchange.com/questions/13943/linear-time-in-place-riffle-shuffle-algorithm.
Alright, here's a rough draft. You say you don't just want an algorithm, but you are taking hints, so consider this algorithm a hint:
Length is N.
k = N/2 - 1.
1) Start in the middle, and shift (by successive swapping of neighboring pair elements) the element at position N/2 k places to the left (1st time: '1' goes to position 1).
2) --k. Is k==0? Quit.
3) Shift (by swapping) the element at N/2 (1st time:'f' goes to position N-1) k places to the right.
4) --k.
Edit: The above algorithm is correct, as the code below shows. Actually proving that it's correct is waaay beyond my capabilities, fun little question though.
#include <iostream>
#include <algorithm>
int main(void)
{
std::string s("abcdefghij1234567890");
int N = s.size();
int k = N/2 - 1;
while (true)
{
for (int j=0; j<k; ++j)
{
int i = N/2 - j;
std::swap(s[i], s[i-1]);
}
--k;
if (k==0) break;
for (int j=0; j<k; ++j)
{
int i = N/2 + j;
std::swap(s[i], s[i+1]);
}
--k;
}
std::cout << s << std::endl;
return 0;
}
Here's an algorithm and working code. It is in place, O(N), and conceptually simple.
Walk through the first half of the array, swapping items into place.
Items that started in the left half will be swapped to the right
before we need them, so we use a trick to determine where they
were swapped to.
When we get to the midpoint, unscramble the unplaced left items that were swapped to the right.
A variation of the same trick is used to find the correct order for unscrambling.
Repeat for the remaining half array.
This goes through the array making no more than N+N/2 swaps, and requires no temporary storage.
The trick is to find the index of the swapped items. Left items are swapped into a swap space vacated by the Right items as they are placed. The swap space grows by the following sequence:
Add an item to the end(into the space vacated by a Right Item)
Swap an item with the oldest existing (Left) item.
Adding items 1..N in order gives:
1 2 23 43 435 465 4657 ...
The index changed at each step is:
0 0 1 0 2 1 3 ...
This sequence is exactly OEIS A025480, and can be calculated in O(1) amortized time:
def next_index(n):
while n&1: n=n>>1
return n>>1
Once we get to the midpoint after swapping N items, we need to unscramble. The swap space will contain N/2 items where the actual index of the item that should be at offset i is given by next_index(N/2+i). We can advance through the swaps space, putting items back in place. The only complication is that as we advance, we may eventually find a source index that is left of the target index, and therefore has already been swapped somewhere else. But we can find out where it is by doing the previous index look up again.
def unscramble(start,i):
j = next_index(start+i)
while j<i: j = next_index(start+j)
return j
Note that this only an indexing calculation, not data movement. In practice, the total number of calls to next_index is < 3N for all N.
That's all we need for the complete implementation:
def interleave(a, idx=0):
if (len(a)<2): return
midpt = len(a)//2
# the following line makes this an out-shuffle.
# add a `not` to make an in-shuffle
base = 1 if idx&1==0 else 0
for i in range(base,midpt):
j=next_index(i-base)
swap(a,i,midpt+j)
for i in range(larger_half(midpt)-1):
j = unscramble( (midpt-base)//2, i);
if (i!=j):
swap(a, midpt+i, midpt+j)
interleave(a[midpt:], idx+midpt)
The tail-recursion at the end can easily be replaced by a loop. It's just less elegant with Python's array syntax. Also note that for this recursive version, the input must be a numpy array instead of a python list, because standard list slicing creates copies of the indexes that are not propagated back up.
Here's a quick test to verify correctness. (8 perfect shuffles of a 52 card deck restore it to the original order).
A = numpy.arange(52)
B = A.copy()
C =numpy.empty(52)
for _ in range(8):
#manual interleave
C[0::2]=numpy.array(A[:26])
C[1::2]=numpy.array(A[26:])
#our interleave
interleave(A)
print(A)
assert(numpy.array_equal(A,C))
assert(numpy.array_equal(A, B))