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))
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
I came across a problem to find the longest increasing path in a matrix. The Brute-Force solution to it is pretty straight forward:
public class Solution {
private static final int[][] dirs = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
private int m, n;
public int longestIncreasingPath(int[][] matrix) {
if (matrix.length == 0) return 0;
m = matrix.length;
n = matrix[0].length;
int ans = 0;
for (int i = 0; i < m; ++i)
for (int j = 0; j < n; ++j)
ans = Math.max(ans, dfs(matrix, i, j));
return ans;
}
private int dfs(int[][] matrix, int i, int j) {
int ans = 0;
for (int[] d : dirs) {
int x = i + d[0], y = j + d[1];
if (0 <= x && x < m && 0 <= y && y < n && matrix[x][y] > matrix[i][j])
ans = Math.max(ans, dfs(matrix, x, y));
}
return ++ans;
}
}
And the time complexity for this was given as O(2^(m+n)) where m is no. of rows, and n is no. of cols in the matrix.
I'm having a hard time understanding this. The first nested for loop is O(mn) which is fine. Now each cell is treated as a root, and a DFS is done on it. However the time complexity for a DFS is O(V + E), and here V = mn and E = 4*mn, so each dfs should be O(mn), so the total time complexity should be O(mn) x O(mn) = O(m^2.n^2) right?
Note: I am aware that this is not an optimal solution and this can be memoized, however my question is about understanding time complexity in this brute for method.
Your DFS isn't O(V+E) = O(nm) because you don't have a visited set. Without this set, the paths your various branches take can overlap and duplicate work such that you can potentially explore the same vertices and traverse the same edges many times over from any given DFS call from longestIncreasingPath. A memory-less search with a branching factor of 4 is what causes exponential behavior.
For example, consider the potential worst case of a perfectly convex matrix:
6 5 4 3 4 5 6
5 4 3 2 3 4 5
4 3 2 1 2 3 4
3 2 1 0 1 2 3
4 3 2 1 2 3 4
5 4 3 2 3 4 5
6 5 4 3 4 5 6
The worst case path for any vertex you search is to climb diagonally to the nearest corner, and such paths are O((n+m)/2) steps long. Every vertex has up to 4 options, and as there's no shared memory in the form of a visited set between recursive calls, you get a naive 4^((n+m)/2) = 2^(n+m) worst-case complexity for DFS. It would be more precise to say that most vertices in this worst case scenario only have 2 to 3 viable neighbors to recurse into, such that the actual worst-case complexity for each search would be between sqrt(2)^(n+m) and sqrt(3)^(n+m), but the exponential run-time is the same.
If you had a visited set, you would get a complexity more like the one you alluded to in your answer. It would be O(nm*((n+m)/2)) = O(n^2*m + m^2*n) because of the restriction that paths be increasing, but without that restriction it would be O((nm)^2).
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).
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
Here is a question from previous HackerEarth Challenge -
Roy has a matrix of size NxN. Rows and Columns are numbered from 0 to N-1.
jth column of ith row contains absolute difference between i and j.
In other words, Matrix[i][j] = abs(i-j) where 0 ≤ i, j < N.
Your task is to find sum of this matrix i.e.
sum = 0
for i=0 to N-1
for j=0 to N-1
sum += Matrix[i][j]
and here is my solution to this problem -
public static long getSum(int num, long acc) {
if (num == 1)
return acc;
long sum = 0;
for (int i = 0; i < num; i++) {
sum += i;
}
sum = sum * 2;
return getSum(num - 1, acc + sum);
}
But this function fails for large number like say anything greater than 4500. I get Stack Over Flow Error.
Here I have tried two things basically to keep the code optimized -
use tail recursion, and
keep running time of this function of order 'n'
So please tell me if I have achieved the two things here correctly. If yes what else can I do to optimize this code. Thanks for your help.
The matrix has very simple structure (I draw only top right half, bottom left is the same, mirrored)
0 1 2 3 4 5 ...
. 0 1 2 3 4 ...
. . 0 1 2 3 ...
. . . 0 1 2 ...
. . . . 0 1 ...
. . . . . 0 ...
It is clear that Kth row contains arithmetic progression 0..(N - K - 1), so it's sum is
Sk = (N - K - 1) * (N - K) / 2
and overall sum is (O(N) solution)
S = 2 * Sum[k = 0..N-1] (Sk)
Moreover, while sum of every row is 'triangular' number, sum of triangular numbers is 'Tetrahedral number', and there is closed formula, that leads to O(1) solution
S = 2 * ((N-1)*N*(N+1)/6) = N*(N+1)*(N-1)/3
Example: for N=4 S = 3*4*5/3 = 20
Tail recursion
Tail recursion does not protect you from stack overflow in Java. Some other languages can recognise tail-calls and optimize them during compilation so they do not extend the stack.
...tail call optimization is hard to do in the JVM because of the security model and the need to always have a stack trace available.
(From Does the JVM prevent tail call optimizations?)
It is however easy to replace tail recursions with a loop:
public static long getSum(int num, long acc) {
while (num > 1) {
long sum = 0;
for (int i = 0; i < num; i++) {
sum += i;
}
sum = sum * 2;
//set up values for next loop
num--;
acc += sum;
}
return acc;
}
Big-O
keep running time of this function of order 'n'
You have not achieved this. It is clear to see there are 2 nested loops over num, and num is decreasing, I think this makes it O(n log n)