Related
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]
I have a 2D array where I want to find the middle position between two given position:
below eg shows the indexes at each position.
00, 01, 02, 03
10, 11, 12, 13
20, 21, 22, 23
30, 31, 32, 33
Inputs:
For position 23 and 33 I should get the output position 31.
Here the inputs are iStart is 2 and jStart is 3, similarly iEnd is 3 and jEnd is 3
The expected o/p is i_mid = 3 and j_mid = 1
For position 00 and 22 I should get the output position 11.
Here the inputs are iStart is 0 and jStart is also 0, similarly iEnd and jEnd are equal to 1
The expected o/p is i_mid = 1 and j_mid = 1
For position 02 and 23 I should get the output position 12.
Here the inputs are iStart is 0 and jStart is 2, similarly iEnd is 2 and jEnd is 3
The expected o/p is i_mid = 1 and j_mid = 2
For position 10 and 31 I should get the output position 20.
Here the inputs are iStart is 3 and jStart is 1, similarly iEnd is 3 and jEnd is 1
The expected o/p is i_mid = 2 and j_mid = 0
I released that to calculate we should also know the length of the array as it is nxm matrix, ie. max value of i can go is n and max value of j can go is m.
therefor for a nxm array iMax will be n and jMax will be m
Now the function can be represents as:
void getMiddle(int iStart, int jStart, int iEnd, int jEnd, int iMax, int jMax) {
//We need to find the mid_i and mid_j of the input positions
System.out.println("midd_i = "+ mid_i +" and midd_j = "+mid_j);
}
I solved this via a brute force approach, however I'm looking for an optimal solution.
In my approach, first I find the number of elements that can come between the given position and then I divided the number of element by 2 and traverse the position till half of the elements that can come as that point will be the mid, my solution is as followed:
void getMiddle(int iStart, int jStart, int iEnd, int jEnd, int jMax) {
int numberOfRows = (iEnd - iStart) + 1;
int totalElementsInRows = jMax * numberOfRows;
int eliminateStartElements = jStart;
int eliminateEndElements = (jMax - 1) - jEnd;
int totalElementsPresentBetweenPositions = totalElementsInRows - (eliminateStartElements + eliminateEndElements);
int halfElement = totalElementsPresentBetweenPositions/2;
int countElement = 0;
for(int i = iStart; i<= iEnd; i++) {
for(int j = 0; j< jMax; j++) {
countElement++;
if(halfElement == countElement) {
System.out.println("midd_i = "+ i +" and midd_j = "+j);
break;
}
}
}
}
I'm sure there should be an optimal solution, If anyone have the optimal solution, please help.
It's a wrong approach, please see the comment section below.
////////////////////////////
You can find the average between the points. When you have point A and point B. The program has to compute average in two dimensions: x=(Ax + Bx)/2. Similarly in the y-axis.
You don't have to worry about .5 appended to end of the number, because Java will truncate it for you.
There should be a way to do this with a geometric approach...
Find the midpoint between the two you want to get, so if you wanted to find the position between mat[0][0] and mat[2][2], you find the midpoints:
mid = (Math.abs(x1 - x2) / 2, Math.abs(y1 - y2) / 2)
x = (2 - 0) / 2 = 1
y = (2 - 0) / 2 = 1
so the answer there would be mat[1][1]
if it were a less straightforward example, such as mat[0][2] and mat[3][3]:
we first find the midpoint, such as:
x = (3 - 0) / 2 = 1.5
y = (3 - 2) / 2 = 0.5
round the answers to the nearest whole number to get the right indexes
You can simplify the task by imagining concrete numbers in the respective positions instead of the indices, which range from 0 to n x m - 1. Something like :
0, 1, 2, 3
4, 5, 6, 7
8, 9, 10, 11
12, 13, 14, 15
Then for each elemnt at m[i][j] applies
m[i][j] = i * m[i].length + j
and for each k, 0 <= k < n x m
i = k / m
j = k % m
which sums up in your method looking like:
static void getMiddle(int iStart, int jStart, int iEnd, int jEnd, int iMax, int jMax) {
int s = iStart * jMax + jStart;
int e = iEnd * jMax + jEnd;
int mid_i = ((e+s)/2)/jMax;
int mid_j = ((e+s)/2)%jMax;
System.out.println("midd_i = "+ mid_i +" and midd_j = "+mid_j);
}
Note: I'm not checking for valid inputs, for example if iStart <= iEnd <= iMax
Test out put with your input example:
getMiddle(0, 0, 2, 2, 4, 4);
getMiddle(0, 2, 2, 3, 4, 4);
getMiddle(1, 0, 3, 1, 4, 4);
midd_i = 1 and midd_j = 1
midd_i = 1 and midd_j = 2
midd_i = 2 and midd_j = 0
I wanna create a program that generates sets of consecutive numbers that add up to form a number. For example. if the input number is 15, it should give -
7, 8
4, 5, 6
1, 2, 3, 4, 5
Some formula/algorithm/loop that can do something that fits in. It could generate an array or print it. This may seem a math problem or silly question but I can't actually figure out how to do that programmatically in Java.
Please try to give exact code that can do the thing.
Say your input is N. You know each set of k consecutive numbers will be centered around N/k. A solution exists for even k if N/k ends with 0.5, and odd k if N/k is an integer. The solution, if one exists, is the k integers centered around N/k.
k=1: 15/1 = 15, so 15 (trivial; may want to omit)
k=2: 15/2 = 7.5, so 7,8
k=3: 15/3 = 5, so 4,5,6
k=4: 15/4 = 3.75, so no solution
k=5: 15/5 = 3, so 1,2,3,4,5
k=6: 15/6 = 2.5, so 0,1,2,3,4,5
etc...
k=15: 15/15 = 1, so -6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,7,8
You can easily modify this to limit to positive or nonnegative solutions.
I'll expand on #MBo's answer as it conveys a very clean algorithm. Wiki provides a good intro on arithmetic progressions, copied below for your convenience.
Sum
Derivation
The sum of a sequence starting with number a and consisting of n consecutive numbers:
S = (n/2) * [2 * a + (n-1) * d]
For consecutive numbers the step d is 1.
S = (n/2) * [2 * a + (n-1)]
Here we can transition to #MBo's post.
P = 2 * S = n * [2 * a + (n-1)]
We can iterate all possible counts of consecutive numbers n and check if the resulting a is valid (i.e. a is an integer).
Let's factor out a.
Say P = n * q => q = 2 * a + (n-1) => 2 * a = q - n + 1 => a = (q - n + 1) / 2
Filters
1) we mentioned we could iterate all possible counts of consecutive numbers n, but given p = n * q it's safe to say n needs to be a divisor of p.
p % n == 0
nMax = (int)Math.sqrt(p)
2) a is an integer and a = (q - n + 1) / 2 => (q - n + 1) is even => q - n is odd.
((q - n) & 1) == 1
Implementation
import java.util.*;
import java.lang.Math;
import java.util.stream.IntStream;
import static java.util.stream.Collectors.toList;
public class Progressions
{
public static void main(String[] args)
{
List<List<Integer>> list = Calculate(15);
System.out.print(list);
}
public static List<List<Integer>> Calculate(int s)
{
List<List<Integer>> list = new ArrayList<>();
int p = 2*s;
int nMax = (int)Math.sqrt(p);
for (int n=2; n<=nMax; n++) {
if(p % n == 0) {
int q = p / n;
if(((q - n) & 1) == 1) {
int a = (q - n + 1) / 2;
list.add(range(a,n));
}
}
}
return list;
}
public static List<Integer> range(int a, int n) {
return IntStream.range(a, a+n)
.boxed()
.collect(toList());
}
}
Consecutive numbers form arithmetic progression. If it starts from number a and has n members, it's sum is
S = n * (2 * b + (n-1)) / 2
so
P = 2 * S = n * (2 * b + (n-1))
So for given input S we can factorize 2*S into all possible pairs of integer factors P = n * q where n<=q, then get starting number
a = (q - n + 1) / 2
If a is integer (oddity of q and n differs) then pair (a, n) represents valid sequence starting from a with n members
Example for S = 15, 2S = 30:
30 = 2 * 15 => n = 2, a = 7 => (7,8)
30 = 3 * 10 => n = 3, a = 4 => (4,5,6)
30 = 5 * 6 => n = 5, a = 1 => (1,2,3,4,5)
Simple Python example:
import math
def getseqs(s):
print(s)
p = 2 * s
for n in range(2, math.ceil(math.sqrt(p))):
if (p % n == 0):
q = p // n
if (((q - n) & 1) == 1): #compare parity
a = (q - n + 1) // 2
seq = list(range(a, a+n))
print(seq, sum(seq))
getseqs(17)
getseqs(15)
getseqs(72)
17
[8, 9] 17
15
[7, 8] 15
[4, 5, 6] 15
[1, 2, 3, 4, 5] 15
72
[23, 24, 25] 72
[4, 5, 6, 7, 8, 9, 10, 11, 12] 72
Consider the int input is your input number (ex. 15) and List<int[]> list as a storage of the result consecutive numbers, here you go:
List<int[]> list = new ArrayList<>();
int lower = 1; // Start searching from 1
int upper = (int) Math.floor(input + 1 / 2); // Up to the half of input (8+9 > 15)
while (lower < upper) { // Iterate between the bounds
int sum = 0;
for (int i = lower; i <= upper; i++) { // Iterate and sum the numbers
sum += i;
if (sum == input) { // If it matches the input
// Add the range to the List
// You have to loop them by one and add to the
// List before version Java-8
list.add(IntStream
.range(lower, i + 1)
.toArray());
break; // Found, no reason to continue
}
if (sum > input) { // Terminate the loop if the sum overlaps
break;
}
lower++; // Increment and try the sums from
// a higher starting number
sum = 0; // Reset the sum
}
The result for the input 15 is a List of these arrays:
[1, 2, 3, 4, 5]
[4, 5, 6]
[7, 8]
Here's a suggestion:
For an input number N:
you only have to consider numbers between 1 and N.
you can maintain an interval that represents the current subset of [1,...,N]. Maintain the sum of the current interval. The first interval will be [1,1], and its sum is 1.
As long as the sum < N, increase the right end of the interval by one (for example, you start with the interval [1,1]. Since 1 < N, you extend it to [1,2].
If the sum of the current interval is equal to N, you add that interval to the output, remove the left end of the interval (also removing it from the current sum), and continue.
If the sum exceeds N, you also remove the left end of the interval (also removing it from the current sum), and continue.
You finish when the interval becomes [N,N] (which is the final interval you should add to the output).
For the input 15, here's how the interval will change over time:
Interval Sum
[1] 1
[1,2] 3
[1,2,3] 6
[1,2,3,4] 10
[1,2,3,4,5] 15 -> output [1,2,3,4,5]
[2,3,4,5] 14
[2,3,4,5,6] 20
[3,4,5,6] 18
[4,5,6] 15 -> output [4,5,6]
[5,6] 11
[5,6,7] 18
[6,7] 13
[6,7,8] 21
[7,8] 15 -> output [7,8]
[8] 8
[8,9] 17
[9] 9
[9,10] 19
[10]
...
[15] 15 -> output 15
You can probably make some optimization once the sum of two consecutive numbers becomes higher than the target sum, at which point you can terminate the loop, and just add the final set (which contains just the target sum).
It used a Window Sliding Technique/Algorithm. You can also google sliding window algorithm sum.
I am writing Implementation of the #Dave solution.
Try to Solve before asking... That's how we learn. (only if we can't get then ask)
Scanner s = new Scanner(System.in);
int inputNumber = s.nextInt();
int k = 1;
while(inputNumber/k >= .5){
Float sequenceMid = (float) inputNumber/k;
if( k%2 == 0 && (sequenceMid *2 == Math.ceil(sequenceMid *2)) ){
for(int i = ((int)Math.floor(sequenceMid) - (k/2)),count=0 ; count < k ; count++,i++ ){
System.out.print(i + " ");
}
System.out.println();
}else if( (k%2 == 1) && (sequenceMid == Math.ceil(sequenceMid))){
for(int i = (Math.round(sequenceMid) - ((k-1)/2)),count=0 ; count < k ; count++,i++ ){
System.out.print(i + " ");
}
System.out.println();
}
k++;
}
Here is an idea that is similar to Eran's solution.
Since we're dealing with consecutive numbers, a cummulative sum (cumsum) can usually help. The basic idea is that we want to find the difference between two cummulative sums that gives exactly K, where K is 15 in your example.
number: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
cumsum: 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55
differences:
15 - 0 = 15 -> [1, 2, 3, 4]
21 - 6 = 15 -> [4, 5, 6]
36 - 21 = 15 -> [7, 8]
The cummulative sum starts from 0 so we can do 15 - 0 subtraction. The number included as the solution will be left-exclusive and right-inclusive. That just means add 1 to the left index (index starts from 0). Hopefully the pattern is quite clear.
The next task is to create an algorithm that does some sliding window with varying width across the cummulative sum. The idea is to search for the difference with the exact value of K. We can start at the beginning where the left and right side of the window points to 0. While the difference is <= K, we want to increase the right side of the window, enlarging the window and the difference.
number: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
cumsum: 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55
1st: (] -> 0 - 0 = 0
2nd: (---] -> 3 - 0 = 3
3rd: (------] -> 6 - 0 = 0
Once the algorithm hit 15, it will print out the first answer, and then it will increase it one more time. However, once we have the difference > K, we want to increase the left number, reducing the difference.
number: 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
cumsum: 0, 1, 3, 6, 10, 15, 21, 28, 36, 45, 55
1st: (-----------------] -> 15 - 0 = 15 <print>
2nd: (---------------------] -> 21 - 0 = 21
3rd: (-----------------] -> 21 - 1 = 20
Notice that the left side is bounded to be < K/2 since K//2 + (K//2 + 1) >= K (where the equality is possible due to integer division denoted by //). So we can stop the loop early when the left side reaches K//2 (due to left-exclusive).
public static int cumsum(int index) {
return index * (index + 1) / 2;
}
public static String printRange(int left, int right) {
StringBuilder buffer = new StringBuilder();
buffer.append('[');
for (int i=left+1;i<=right;i++) {
buffer.append(i);
buffer.append(',');
}
buffer.deleteCharAt(buffer.length()-1);
buffer.append(']');
return buffer.toString();
}
public static void main(String[] args) {
int K = 15;
int K_ov_2 = K/2;
int left_index = 0;
int right_index = 0;
int diff;
while (left_index < K_ov_2) {
diff = cumsum(right_index) - cumsum(left_index);
System.out.println("diff = " + diff + ", left = " + left_index + ", right = " + right_index);
if (diff == K) {
System.out.println(printRange(left_index,right_index));
}
if (diff <= K) {
right_index++;
} else {
left_index++;
}
}
}
I added the debug line so the output can become more obvious.
diff = 0, left = 0, right = 0
diff = 1, left = 0, right = 1
diff = 3, left = 0, right = 2
diff = 6, left = 0, right = 3
diff = 10, left = 0, right = 4
diff = 15, left = 0, right = 5
[1,2,3,4,5]
diff = 21, left = 0, right = 6
diff = 20, left = 1, right = 6
diff = 18, left = 2, right = 6
diff = 15, left = 3, right = 6
[4,5,6]
diff = 22, left = 3, right = 7
diff = 18, left = 4, right = 7
diff = 13, left = 5, right = 7
diff = 21, left = 5, right = 8
diff = 15, left = 6, right = 8
[7,8]
diff = 24, left = 6, right = 9
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'm working on a problem to count the number of 2s between 0 and n in decimal representation, without using a recursive solution. I found the following solution and I'm debugging it, and my question is: what is the logical meaning of seendigits, and statements countof2s += digit * position * pow10_posMinus1 and seendigits = seendigits + pow10_pos * digit? If anyone could offer any insights, that would be great.
public static int count2sI(int num) {
int countof2s = 0, digit = 0;
int j = num, seendigits = 0, position = 0, pow10_pos = 1;
/* maintaining this value instead of calling pow() is an 6x perf
* gain (48s -> 8s) pow10_posMinus1. maintaining this value
* instead of calling Numof2s is an 2x perf gain (8s -> 4s).
* overall > 10x speedup */
while (j > 0) {
digit = j % 10;
int pow10_posMinus1 = pow10_pos / 10;
countof2s += digit * position * pow10_posMinus1;
/* we do this if digit <, >, or = 2
* Digit < 2 implies there are no 2s contributed by this
* digit.
* Digit == 2 implies there are 2 * numof2s contributed by
* the previous position + num of 2s contributed by the
* presence of this 2 */
if (digit == 2) {
countof2s += seendigits + 1;
}
/* Digit > 2 implies there are digit * num of 2s by the prev.
* position + 10^position */
else if (digit > 2) {
countof2s += pow10_pos;
}
seendigits = seendigits + pow10_pos * digit;
pow10_pos *= 10;
position++;
j = j / 10;
}
return (countof2s);
}
The digits are processed from right to left. For the input 2468, the first iteration processes digit 8, the second 6, then 4, and finally 2.
The variable seendigits keeps track of the combined value of the digits that have already been processed; e.g. for the input 2468, the value during the four iterations would be 0, 8, 68 and 468. This value is used when a digit equals 2 (see below).
It is important to note that when processing a digit, e.g. 4 in the example 2468, the number of twos in the lower digits for the range 1 to 68 have already been counted. This count is used for the range 401 to 468, so what still needs to be counted is the number of twos in the lower digits for the range 1 to 400, and the number of twos in the current digit for the range 1 to 468. This algorithm does these two things seperately.
Counting the twos in the lower digits is done in this line:
countof2s += digit * position * pow10_posMinus1;
In the first iteration this adds 0, because there are no lower digits. In the second iteration, this adds digit * 1 * 1, because there is 1 lower digit, and it equals 2 once for every value of the current digit. In the third iteration, this adds digit * 2 * 10, because there are 2 lower digits, and they each equal 2 ten times for every value of the current digit, and so on ...
For the example input 2468, this line would add:
iteration 1: digit = 8, add: 8 * 0 * 0 = 0
iteration 2: digit = 6, add: 6 * 1 * 1 = 6
iteration 3: digit = 4, add: 4 * 2 * 10 = 80
iteration 4: digit = 2, add: 2 * 3 * 100 = 600
e.g. the value 80 is the number of twos in the lower digits of numbers 1 to 400:
Lowest digit of x02, x12, x22, x32, ... x92
These occur 4 times: for 2 to 92, for 102 to 192, for 202 to 292 and for 302 to 392.
Second-lowest digit of x20, x21, x22, x23, ... x29
These occur 4 times: for 20 to 29, for 120 to 129, for 220 to 229 and for 320 to 329.
Counting the twos in the current digit is done in one of the following lines, depending on whether the current digit is greater than or equals 2.
if (digit == 2) { countof2s += seendigits + 1; }
else if (digit > 2) { countof2s += pow10_pos; }
If the current digit is greater than 2, the number of times the digit was two while counting up from 1 depends only on the position of the digit. For position 0, this adds +1, to count the number 2; for the position 1, this adds +10, for the highest digits in the range 20 to 29; for position 2, this adds +100, for the highest digit in the range 200 to 299, and so on ...
For the example input 2468, this line would add:
iteration 1: digit = 8, add: 1
iteration 2: digit = 6, add: 10
iteration 3: digit = 4, add: 100
iteration 4: digit = 2, add: 0 (digit is not greater than 2)
If the current digit equals 2, the number of times the digit was two while counting up from 1 equals the value of the lower digits + 1.
For the example input 2468, this line would add:
iteration 1: digit = 8, add: 0
iteration 2: digit = 6, add: 0
iteration 3: digit = 4, add: 0
iteration 4: digit = 2, add: 469
This is because the current digit equals 2 in the range 2000 to 2468.
UPDATE:
The algorithm is easier to follow if you process the digits left to right: (example in javascript)
function twosInRange(number) {
var digits = Math.floor(Math.log(number) / Math.log(10));
var count = 0;
for (var pos = digits; pos >= 0; --pos) { // 3, 2, 1, 0
var unit = Math.pow(10, pos); // 1000, 100, 10, 1
var digit = Math.floor(number / unit); // 2, 4, 6, 8
number -= digit * unit; // 468, 68, 8, 0
// COUNT OCCURRENCES IN LOWER DIGITS:
count += digit * pos * (unit / 10); // + 2*3*100, 4*2*10, 6*1*1, 8*0*0
// COUNT OCCURRENCES IN CURRENT DIGIT:
if (digit > 2) count += unit; // + (1000), 100, 10, 1
else if (digit == 2) count += number + 1; // + 469, (69), (9), (1)
}
return (count); // 600 + 80 + 6 + 100 + 10 + 1 + 469
}
document.write(twosInRange(2468)); // = 1266