I am stuck with one problem :
Finding all the possible ways of dividing a group of size 'n' into subgroups of size 'k'. (here n%k =0)
e.g., let set be {1,2,3,4,5,6} to divide into subgroups of 3 (k = 3,n = 6), possible sets are
a) {1,2,3},{4,5,6}
b) {1,3,5},{2,4,6}
c) {1,3,6},{2,4,5}
d) {1,3,4},{2,5,6} etc....
What i tried doing was, to first find all the combination of size k from the set.
Then loop through these combinations and find out which all combinations can be grouped together to find the list of subgroups.
But I believe the time complexity is pretty bad in this approach. Is there any better approach for this problem?
I would use a recursive method. I think this one has the optimal running time, since it exactly produces all needed subsets.
public static void solve(int[] a, int k, int i, List<List<Integer>> subsets) {
if (i == a.length) {
for (List<Integer> subset : subsets) {
System.out.print(subset);
}
System.out.println();
} else {
// loop over all subsets and try to put a[i] in
for (int j = 0; j < subsets.size(); j++) {
if (subsets.get(j).size() < k) {
// subset j not full
subsets.get(j).add(a[i]);
solve(a, k, i+1, subsets); // do recursion
subsets.get(j).remove((Integer)a[i]);
if (subsets.get(j).size() == 0) {
// don't skip empty subsets, so you won't get duplicates
break;
}
}
}
}
}
Usage:
public static void main(String[] args) {
int[] a = {1, 2, 3, 4, 5, 6};
int k = 3;
List<List<Integer>> subsets = new ArrayList<List<Integer>>(a.length / k);
for (int i = 0; i < a.length / k; i++)
subsets.add(new ArrayList<Integer>(k));
solve(a, k, 0, subsets);
}
Prints:
[1, 2, 3][4, 5, 6]
[1, 2, 4][3, 5, 6]
[1, 2, 5][3, 4, 6]
[1, 2, 6][3, 4, 5]
[1, 3, 4][2, 5, 6]
[1, 3, 5][2, 4, 6]
[1, 3, 6][2, 4, 5]
[1, 4, 5][2, 3, 6]
[1, 4, 6][2, 3, 5]
[1, 5, 6][2, 3, 4]
Think about it combinatorially. If n % k != 0, you can't do it because you'll end up with one set that has less than k elements, so start off with checking whether that is the case.
Afterwards, all you need to do is recursively produce k-combinations from an n-i*k set for all i in [0; n/k]. Algorithms for producing all k-combinations of a given set can be found on SO easy enough. The idea is: there are (n choose k) possible such sets you can choose for your first set; from the remaining n-k elements, you can choose ((n-k) choose k) sets); from the remaining n-2k elements, you can choose ((n-2k) choose k) sets and so on. Assuming the order of your sets doesn't matter, you have (n choose k) * ((n-k) choose k) * ... * ((n-(n-1)k) choose k) / ((n/k)!) possibilities to choose your sets, which depending on k can be exponential in the number of elements your original set has, so if you really want to produce each and every one of them, you won't get below exponential complexity.
I found a formula for the # of possible subgroupings, in case anyone finds it interesting. (Would this be considered too off-topic? Am I posting this correctly?)
First let m = n/k be the # of subgroups. Now let the first subgroup be fixed as the first k elements of the group, the second subgroup be the next k, and so on. If we consider all possible permutations of the group, this will give us all the different subgroupings. There are n! permutations of the n elements, but we don't care about ordering, so we factor out the k! permutations of each of the m subgroups and the m! permutations of the subgroups themselves. This gives us:
n!/(m!*(k!)^m).
As a check, if k = 1 or k = n, this gives us 1 subgrouping. In the original example, n = 6, k = 3, m = 2, and we get 10 possible subgroupings (which Heuster's code found).
Now, if you compare this expression to the one G. Bach gave and use (n choose k) = n!/(k!*(n-k)!), you will see that all the (n-k)! terms cancel and it reduces to the expression above.
Bonus: if you use Stirling's approximation for n!, then the expression simplifies nicely, and you get that the # of subgroupings scales as (m^n)/m!.
Related
I have a java problem at codewars.
In this kata, you will write a function that returns the positions and
the values of the "peaks" (or local maxima) of a numeric array.
For example, the array arr = [0, 1, 2, 5, 1, 0] has a peak at
position 3 with a value of 5 (since arr[3] equals 5).
The output will be returned as a Map<String,List>with two key-value pairs:"pos"and"peaks". If there is no peak in the given array, simply
return `{"pos" => [], "peaks" => []}.
Example: pickPeaks([3, 2, 3, 6, 4, 1, 2, 3, 2, 1, 2, 3]) should
return {pos: [3, 7], peaks: [6, 3]} (or equivalent in other
languages)
All input arrays will be valid integer arrays (although it could
still be empty), so you won't need to validate the input.
The first and last elements of the array will not be considered as
peaks (in the context of a mathematical function, we don't know what
is after and before and therefore, we don't know if it is a peak or
not).
Also, beware of plateaus! [1, 2, 2, 2, 1] has a peak while [1, 2, 2, 2, 3] and [1, 2, 2, 2, 2] do not. In case of a plateau-peak,
please only return the position and value of the beginning of the
plateau. For example: pickPeaks([1, 2, 2, 2, 1]) returns {pos: [1], peaks: [2]} (or equivalent in other languages)
Here is my answer:
public static Map<String, List<Integer>> getPeaks(int[] arr) {
HashMap<String, List<Integer>> stringListHashMap = new HashMap<>();
List<Integer> positions = new ArrayList<>();
List<Integer> values = new ArrayList<>();
values.add(arr[1]);
positions.add(1);
for (int i = 2; i < arr.length - 1; i++) {
if (values.get(values.size() - 1) < arr[i]) {
values.clear();
positions.clear();
values.add(arr[i]);
positions.add(i);
}
}
stringListHashMap.put("pos", positions);
stringListHashMap.put("peaks", values);
return stringListHashMap;
}
The above code works fine, but I don't pass the unit tests because of this ->
should support finding peaks expected:<{pos=[3, 7], peaks=[6, 3]}> but was:<{pos=[3], peaks=[6]}>
I have narrowed down the problem to this ->
Example: pickPeaks([3, 2, 3, 6, 4, 1, 2, 3, 2, 1, 2, 3]) should return {pos: [3, 7], peaks: [6, 3]} (or equivalent in other languages)
There is clearly only one peak which is 6 and its index 3. Then what is the person asking the question trying to say? I feel like I'm missing something.
The question is asking for local maxima, not global maxima. If a value is larger than its neighbours (and is not the first or last value), it's a peak, even if there is another value elsewhere that is even greater. Hence, with the input [3, 2, 3, 6, 4, 1, 2, 3, 2, 1, 2, 3], the eighth value (pos 7) is a peak, since the value 3 is greater than both the preceding neighbour (2) and following neighbour (2).
From what I understand, peak is surrounded by valley. So if you go up than down you have a peak, if you again go up and again down you have another peak. If you go up and than horizontal and than down, first position is peak, you just walk on plateau. It's like when you go mountain. You can climb 2 or 3 peaks in one trip.
Here is the solution:
public static Map<String, List<Integer>> getPeaks(int[] arr) {
Map<String, List<Integer>> result = new HashMap<>();
int peak = 0;
for (int i = 1; i < arr.length - 1; i++) {
if (arr[i-1] < arr[i]){
peak = i;
}
if(arr[i] > arr[i+1] && peak !=0) {
result.computeIfAbsent("pos", k -> new ArrayList<>()).add(peak);
result.computeIfAbsent("peaks", k -> new ArrayList<>()).add(arr[i]);
peak = 0;
}
}
return result;
}
I had to keep the current peak, as you can go horizontal and we need first position when you reach peak.
And I reset it when I go down.
So if arr[i-1] < arr[i] it means I reach a peak and if arr[i] > arr[i+1] && peak !=0 it means I went down.
Another mention, use Map as reference instead of HashMap. Same as you proceed for List.
I'm working on the following task.
Given an array of n integers and two integer numbers m and k.
You can add any positive integer to any element of the array such that
the total value does not exceed k.
The task is to maximize the
multiples of m in the resultant array.
Consider the following example.
Input:
n = 5, m = 2, k = 2, arr[] = [1, 2, 3, 4, 5]
Let's add 1 to the element arr[0] and 1 to arr[2] then the final array would be:
[2, 2, 4, 4, 5]
Now there are four (4) elements which are multiples of m (2).
I am not getting correct output.
My code:
public class Main {
public static void main(String[] args) {
int n = 5;
int m = 4;
int k = 3;
int count = 0;
int[] arr = {17, 8, 9, 1, 4};
for (int i = 0; i < n; i++) {
for (int j = 0; j <= k; j++) {
// check initial
if (arr[i] % m == 0) {
break;
}
// add
arr[i] = arr[i] + j;
// check again
if (arr[i] % m == 0) {
count++;
break;
}
}
}
System.out.println("Final Array : " + Arrays.toString(arr));
System.out.println("Count : " + count);
}
}
This task boils down to a well-known Dynamic programming algorithm called Knapsack problem after a couple of simple manipulations with the given array.
This approach doesn't require sorting and would be advantages when k is much smaller n.
We can address the problem in the following steps:
Iterate over the given array and count all the numbers that are already divisible by m (this number is stored in the variable count in the code below).
While iterating, for every element of the array calculate the difference between m and remainder from the division of this element by m. Which would be equal to m - currentElement % m. If the difference is smaller or equal to k (it can cave this difference) it should be added to the list (differences in the code below) and also accumulated in a variable which is meant to store the total difference (totalDiff). All the elements which produce difference that exceeds k would be omitted.
If the total difference is less than or equal to k - we are done, the return value would be equal to the number of elements divisible by m plus the size of the list of differences.
Otherwise, we need to apply the logic of the Knapsack problem to the list of differences.
The idea behind the method getBestCount() (which is an implementation Knapsack problem) boils down to generating the "2D" array (a nested array of length equal to the size of the list of differences +1, in which every inner array having the length of k+1) and populating it with maximum values that could be achieved for various states of the Knapsack.
Each element of this array would represent the maximum total number of elements which can be adjusted to make them divisible by m for the various sizes of the Knapsack, i.e. number of items available from the list of differences, and different number of k (in the range from 0 to k inclusive).
The best way to understand how the algorithm works is to draw a table on a piece of paper and fill it with numbers manually (follow the comments in the code, some intermediate variables were introduced only for the purpose of making it easier to grasp, and also see the Wiki article linked above).
For instance, if the given array is [1, 8, 3, 9, 5], k=3 and m=3. We can see 2 elements divisible by m - 3 and 9. Numbers 1, 8, 5 would give the following list of differences [2, 1, 1]. Applying the logic of the Knapsack algorithm, we should get the following table:
[0, 0, 0, 0]
[0, 0, 1, 1]
[0, 1, 1, 2]
[0, 1, 2, 2]
We are interested in the value right most column of the last row, which is 2 plus 2 (number of elements divisible by 3) would give us 4.
Note: that code provided below can dial only with positive numbers. I don't want to shift the focus from the algorithm to such minor details. If OP or reader of the post are interested in making the code capable to work with negative number as well, I'm living the task of adjusting the code for them as an exercise. Hint: only a small change in the countMultiplesOfM() required for that purpose.
That how it might be implemented:
public static int countMultiplesOfM(int[] arr, int k, int m) {
List<Integer> differences = new ArrayList<>();
int count = 0;
long totalDiff = 0; // counter for the early kill - case when `k >= totalDiff`
for (int next : arr) {
if (next % m == 0)
count++; // number is already divisible by `m` we can increment the count and from that moment we are no longer interested in it
else if (m - next % m <= k) {
differences.add(m - next % m);
totalDiff += m - next % m;
}
}
if (totalDiff <= k) { // early kill - `k` is large enough to adjust all numbers in the `differences` list
return count + differences.size();
}
return count + getBestCount(differences, k); // fire the rest logic
}
// Knapsack Algorithm implementation
public static int getBestCount(List<Integer> differences, int knapsackSize) {
int[][] tab = new int[differences.size() + 1][knapsackSize + 1];
for (int numItemAvailable = 1; numItemAvailable < tab.length; numItemAvailable++) {
int next = differences.get(numItemAvailable - 1); // next available item which we're trying to place to knapsack to Maximize the current total
for (int size = 1; size < tab[numItemAvailable].length; size++) {
int prevColMax = tab[numItemAvailable][size - 1]; // maximum result for the current size - 1 in the current row of the table
int prevRowMax = tab[numItemAvailable - 1][size]; // previous maximum result for the current knapsack's size
if (next <= size) { // if it's possible to fit the next item in the knapsack
int prevRowMaxWithRoomForNewItem = tab[numItemAvailable - 1][size - next] + 1; // maximum result from the previous row for the size = `current size - next` (i.e. the closest knapsack size which guarantees that there would be a space for the new item)
tab[numItemAvailable][size] = Math.max(prevColMax, prevRowMaxWithRoomForNewItem);
} else {
tab[numItemAvailable][size] = Math.max(prevRowMax, prevColMax); // either a value in the previous row or a value in the previous column of the current row
}
}
}
return tab[differences.size()][knapsackSize];
}
main()
public static void main(String[] args) {
System.out.println(countMultiplesOfM(new int[]{17, 8, 9, 1, 4}, 3, 4));
System.out.println(countMultiplesOfM(new int[]{1, 2, 3, 4, 5}, 2, 2));
System.out.println(countMultiplesOfM(new int[]{1, 8, 3, 9, 5}, 3, 3));
}
Output:
3 // input array [17, 8, 9, 1, 4], m = 4, k = 3
4 // input array [1, 2, 3, 4, 5], m = 2, k = 2
4 // input array [1, 8, 3, 9, 5], m = 3, k = 3
A link to Online Demo
You must change 2 line in your code :
if(arr[i]%m==0)
{
count++; // add this line
break;
}
// add
arr[i]=arr[i]+1; // change j to 1
// check again
if(arr[i]%m==0)
{
count++;
break;
}
The first is because the number itself is divisible.
and The second is because you add a number to it each time.That is wrong.
for example chenge your arr to :
int[] arr ={17,8,10,2,4};
your output is :
Final Array : [20, 8, 16, 8, 4]
and That is wrong because 16-10=6 and is bigger than k=3.
I believe the problem is that you aren't processing the values in ascending order of the amount by which to adjust.
To solve this I started by using a stream to preprocess the array. This could be done using other methods.
map the values to the amount to make each one, when added, divisible by m.
filter out those that equal to m' (already divisible by m`)
sort in ascending order.
Once that is done. Intialize max to the difference between the original array length and the processed length. This is the number already divisible by m.
As the list is iterated
check to see if k > amount needed. If so, subtract from k and increment max
otherwise break out of the loop (because of the sort, no value remaining can be less than k)
public static int maxDivisors(int m, int k, int[] arr) {
int[] need = Arrays.stream(arr).map(v -> m - v % m)
.filter(v -> v != m).sorted().toArray();
int max = arr.length - need.length;
for (int val : need) {
if (k >= val) {
k -= val;
max++;
} else {
break;
}
}
return max;
}
int m = 4;
int k = 3;
int[] arr ={17,8,9,1,4};
int count = maxDivisors(m, k, arr);
System.out.println(count);
prints
3
consider the following problem :-
Given an array of integers nums and an integer k, return the number of contiguous subarrays where the product of all the elements in the subarray is strictly less than k.
Example:
Input: nums = [10,5,2,6], k = 100
Output: 8
Explanation: The 8 subarrays that have product less than 100 are:
[10], [5], [2], [6], [10, 5], [5, 2], [2, 6], [5, 2, 6]
Note that [10, 5, 2] is not included as the product of 100 is not strictly less than k.
The solution for this using the sliding window method is:-
public class Main {
public static void main(String[] args) {
int arr[] = {10, 5,2, 6};
int k = 100;
int prod = 1, ans = 0, left = 0;
for (int right = 0; right < arr.length; right++) {
prod *= arr[right];
while (prod >= k) prod /= arr[left++];
ans += right - left + 1;
}
System.out.println(ans);
}
}
Here is my quesstion:-
right - left -1 gives the total number of elements in the subarray extracted then how is this correctly calculating the total number of subarrays possible?
ie: for an array [10, 5, 2, 6]
right=0; left=0; (right-left+1)=1; list=[10]
right=1; left=0; (right-left+1)=2; list=[10, 5]
right=2; left=1; (right-left+1)=2; list=[5, 2]
right=3; left=1; (right-left+1)=3; list=[5, 2, 6]
What is the logic behind calculating the number of elements in the subarray while traversing the array will get the total number of subarrays possible with product less than K ?
You should first figure out what the invariant of the for loop is. In each iteration of the for loop, it fixes a right, and finds the leftmost left such that the range [left, right] contains elements that have a product closest but not exceeding 100, for that particular value of right.
For example, in the last iteration, the range found was [1, 3], and the subarray with that range was [5, 2, 6]. Notice that if we included one more element from the left, the product would be equal to, or exceed 100. You can think of this as finding the longest subarray ending at right that has a product less than 100.
Assuming the integers are all positive, then if we know the longest subarray ending at right that satisfy this condition, we also know that any shorter subarrays ending at right will also satisfy this condition. There are exactly (right - left) shorter subarrays than the longest that we found. Hence, every time we find a left, we count (right - left), plus the longest one we found. This will be the total number of subarrays that end at right that satisfy the condition.
Since the for loop checks every possible ending location (right) for a subarray, and we counted all the subarrays that satisfy the condition for each ending location, we have counted all the subarrays that satisfy the condition.
Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
Right now I'm trying to write a function that takes an array and an integer n, and gives a list of each size n combination (so a list of int arrays). I am able to write it using n nested loops, but this only works for a specific size of subset. I can't figure out how to generalize it to work for any size of combination. I think I need to use recursion?
This is the code for all combinations of 3 elements, and I need an algorithm for any number of elements.
import java.util.List;
import java.util.ArrayList;
public class combinatorics{
public static void main(String[] args) {
List<int[]> list = new ArrayList<int[]>();
int[] arr = {1,2,3,4,5};
combinations3(arr,list);
listToString(list);
}
static void combinations3(int[] arr, List<int[]> list){
for(int i = 0; i<arr.length-2; i++)
for(int j = i+1; j<arr.length-1; j++)
for(int k = j+1; k<arr.length; k++)
list.add(new int[]{arr[i],arr[j],arr[k]});
}
private static void listToString(List<int[]> list){
for(int i = 0; i<list.size(); i++){ //iterate through list
for(int j : list.get(i)){ //iterate through array
System.out.printf("%d ",j);
}
System.out.print("\n");
}
}
}
This is a well-studied problem of generating all k-subsets, or k-combinations, which can be easily done without recursion.
The idea is to have array of size k keeping sequence of indices of elements from the input array (which are numbers from 0 to n - 1) in increasing order. (Subset then can be created by taking items by these indices from the initial array.) So we need to generate all such index sequences.
First index sequence will be [0, 1, 2, ... , k - 1], on the second step it switches to [0, 1, 2,..., k], then to [0, 1, 2, ... k + 1] and so on. The last possible sequence will be [n - k, n - k + 1, ..., n - 1].
On each step, algorithm looks for the closest to the end item which can be incremented, increments it and fills up items right to that item.
To illustrate, consider n = 7 and k = 3. First index sequence is [0, 1, 2], then [0, 1, 3] and so on... At some point we have [0, 5, 6]:
[0, 5, 6] <-- scan from the end: "6" cannot be incremented, "5" also, but "0" can be
[1, ?, ?] <-- "0" -> "1"
[1, 2, 3] <-- fill up remaining elements
next iteration:
[1, 2, 3] <-- "3" can be incremented
[1, 2, 4] <-- "3" -> "4"
Thus, [0, 5, 6] is followed by [1, 2, 3], then goes [1, 2, 4] etc.
Code:
int[] input = {10, 20, 30, 40, 50}; // input array
int k = 3; // sequence length
List<int[]> subsets = new ArrayList<>();
int[] s = new int[k]; // here we'll keep indices
// pointing to elements in input array
if (k <= input.length) {
// first index sequence: 0, 1, 2, ...
for (int i = 0; (s[i] = i) < k - 1; i++);
subsets.add(getSubset(input, s));
for(;;) {
int i;
// find position of item that can be incremented
for (i = k - 1; i >= 0 && s[i] == input.length - k + i; i--);
if (i < 0) {
break;
}
s[i]++; // increment this item
for (++i; i < k; i++) { // fill up remaining items
s[i] = s[i - 1] + 1;
}
subsets.add(getSubset(input, s));
}
}
// generate actual subset by index sequence
int[] getSubset(int[] input, int[] subset) {
int[] result = new int[subset.length];
for (int i = 0; i < subset.length; i++)
result[i] = input[subset[i]];
return result;
}
If I understood your problem correctly, this article seems to point to what you're trying to do.
To quote from the article:
Method 1 (Fix Elements and Recur)
We create a temporary array ‘data[]’ which stores all outputs one by
one. The idea is to start from first index (index = 0) in data[], one
by one fix elements at this index and recur for remaining indexes. Let
the input array be {1, 2, 3, 4, 5} and r be 3. We first fix 1 at index
0 in data[], then recur for remaining indexes, then we fix 2 at index
0 and recur. Finally, we fix 3 and recur for remaining indexes. When
number of elements in data[] becomes equal to r (size of a
combination), we print data[].
Method 2 (Include and Exclude every element)
Like the above method, We create a temporary array data[]. The idea
here is similar to Subset Sum Problem. We one by one consider every
element of input array, and recur for two cases:
The element is included in current combination (We put the element in data[] and increment next available index in data[])
The element is excluded in current combination (We do not put the element and do not change index)
When number of elements in data[] become equal to r (size of a
combination), we print it.
If the number range is 0 - 10. I would like to generate the following numbers
e.g.
unique 5 numbers [3, 8, 5, 1, 9]
unique 3 numbers [2, 6, 5]
5 numbers of which 2 numbers occur twice [2, 3, 2, 6, 3]
7 numbers of which 2 numbers occur twice [2, 5, 9, 2, 7, 5, 3] (Here 2, 5 occur twice)
5 numbers of which 1 number occur thrice [2, 3, 8, 3, 3] (Here 3 occurs twice)
7 numbers of which 1 number occur thrice and 1 number occurs twice [1, 5, 9, 1, 1, 5, 3] (Here 1 occurs thrice, 5 occurs twice)
How would you implement a generic function which caters to the above requirements.
EDIT1:
This is what I have right now..
protected List<Integer> fetchOverlapIntegers(int min, int max,
int howMany, int overlap) {
// The following code fetches unique numbers within the range
List<Integer> numbers = this.fetchRandomIntegers(min, max, howMany);
if (overlap > 0) {
int size = numbers.size();
for (int i = 0; i < overlap; i++) {
numbers.set(size - 1 - i, numbers.get(i));
}
}
Collections.shuffle(numbers);
return numbers;
}
Just for grins I wrote this up for this very underspecified problem:
public static List<Integer> weirdFunction(List<Integer> criteria, List<Integer> candidateNumbers) {
List<Integer> results = new ArrayList<Integer>();
for (int occurrenceCount = 0; occurrenceCount < criteria.size(); occurrenceCount++) {
int thisMany = criteria.get(occurrenceCount);
for (int i=0; i < thisMany; i++) {
Integer theChoice = candidateNumbers.get(new Random().nextInt(candidateNumbers.size()));
for (int ct=0; ct < occurrenceCount; ct++) {
results.add(theChoice);
}
}
}
Collections.shuffle(results);
return results;
}
It takes two lists, one which is the criteria which is basically a list of how many times it'll place a randomly chosen number in the results (the 1st one gets chosen once, the 2nd twice, etc.), and a list of candidateNumbers from which they'll be chosen.
But, this way only allows you to specify one particular "count" for each criteria. e.g. you can't have a list where "two numbers appear twice" because "appear twice" only has one slot. You could make this a list of lists or some other data structure of course.
Also, it doesn't eliminate duplicates when it pulls them in. So if we had a criteria of {1, 1, 1} it'd try and pull one number once, one number twice, and one number thrice. But it doesn't check if any of those are the same number - so it could end up pulling the same number each time and effectively pulling just one number six times instead of 3 separate numbers once, twice, and thrice.
You can randomly pull out items from the list of contender values, for example:
List<int> numbers = new List<int>() { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Random r = new Random();
for (int i = 0; i < 4; i++)
{
int index = r.Next() % numbers.Count;
Console.WriteLine(numbers[index]);
numbers.RemoveAt(index);
}
If you want a value tripled, just triple the first removed. Then shuffle your results at the end.