How can I improve my algorithm? Degree of divisibility question - java

Hey guys I will explain the question below.
keys = list of integers
I have to find max degree of divisibility of elements in keys. But When I calculate the degree of divisibility I should just consider the keys element. for example:
keys = [2,4,8,2]
2 = [2,2] degree of divisibility is 2
4 = [2,4,2] degree of divisibility is 3
8 = [2,4,8,2] degree of divisibility is 4 so we choose 8 with 4 degrees of divisibility.
after that we have to calculate
if maxDegreeOfDivisibility(4 in our case) * 10^5 < validityPeriod* instructionCount then its
true.
and we return 1 and 4*10^5. I hope I explain the question if u guys have any questions about the question :D I can answer.
public static List<Integer> encryptionValidity(int instructionCount, int validityPeriod,
List<Integer> keys) {
List<Integer> result = Arrays.asList(0, 0);
Map<Integer, Integer> degreeOfDivisibilityCache = new HashMap<>();
for (int i: keys) {
Integer degreeOfDivisibility = degreeOfDivisibilityCache.getOrDefault(i, -1);
if (degreeOfDivisibility == -1) {
int count = 0;
for (int j : keys) {
if (j > 0 && i % j == 0) {
count++;
}
}
degreeOfDivisibilityCache.put(i, count);
}
}
int maxDegreeOfDivisibility = degreeOfDivisibilityCache.values().stream()
.max(Comparator.comparingInt(Integer::intValue)).orElse(0);
int s = maxDegreeOfDivisibility * 10000;
result.set(1, s);
BigInteger ic = BigInteger.valueOf(instructionCount);
BigInteger a = ic.multiply(BigInteger.valueOf(validityPeriod));
if (a.compareTo(BigInteger.valueOf(s)) > 0) {
result.set(0, 1);
}
return result;
}

Declare 32 buckets, each signifying an integer's most significant bit
Scan through the keys, record their occurrence count and put unique instances in their respective buckets (unsorted), and flag as candidate
Scan the buckets, in descending order of the most significant bit it represents. Compare only with contents of buckets of lower bits.
3.1 On a divisible match, aggregate current count with occurrence count in step 2 & clear that number's candidacy flag
3.2 If current count > current record holder, set record holder = this key & current count
Example: [19, 4, 12, 6, 4, 3, 8]
Occurrences: {19: 1, 4: 2, 12: 1, 6: 1, 3: 1, 8: 1}
Bucket #16: [19*]
Bucket #8: [12*, 8*]
Bucket #4: [4*, 6*]
Bucket #2: [3*]
Scan 19 => 1(self); set record: (19, 1)
Scan 12 => 1(self) + 2(from 4) + 1(from 6) + 1(from 3) = 5; clear 4, 6, 3; set record: (12, 5)
Scan 8 => 2
Skip 4, 6, 3
Time Complexity: if n is total integer count and m is number of unique integers, O(n) + O(m^2) in BigO notation, but actually O(n) + C*(m^2)/3 (for some constant C)
Analysis:
First scan in step 1: O(n)
Bucket scan: Worst case (full buckets) is inverted binary tree.
This is an infinite series that sums to 1/3 (See related Wikipedia articles: here and here)
Credits to Rick James for candidate flag idea.

Sort the numbers, largest first. Flag each one as being a candidate.
For each number that is still a candidate:
2.1. Count how many of the numbers after it divide into it. Mark each such number as no longer a candidate.
Search the list for the largest count from step 2.1. That count is the Degree of Divisibility of the original list.
Example:
[2,4,8,2,3,6] (before sorting)
[8*,6*,4*,3*,2*,2*] -- '*' means "is a candidate"
check 8: [8*,6*,4,3*,2,2] -- divisible by 4 (8,4,2,2)
check 6: [8*,6*,4,3,2,2] -- divisible by 4 (6,3,2,2)
the rest don't need checking
The answer is 4.

Related

Maximize the number of Elements in the Array divisible by M

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

Reduce the number of distinct elements in array

I have an array of numbers and another number K.
My task is to reduce the number of distinct elements in the array. For that, I can update the array several times. For updating the array, I have to follow these steps:
Select an element at index i and add that element by K, and reduce all other remaining elements by K.
For updating an array I can select the same index several times.
Example:
K = 1
Array: [3,1,3]
Answer: 3
I am picking index = 1, as [3-1, 1+1, 3-1] = [2,2,2] so we have number 2 that appears 3 times so this element occurs maximum number of times. So answer is 3.
Another example:
K = 1
Array: [1,2,2]
Answer: 2
It's not possible to make all elements same, so we have number 2 that appears 2 times, so answer is 2.
Array size can be [1, 1000], and the value of K and elements in array is in range [0, 1000]
Here is my code that I tried, my my approach is not correct.
public static int process(int K, int[] A) {
Map<Integer, Integer> map = new TreeMap<>();
for (int key : A) {
map.put(key, map.getOrDefault(key, 0) + 1);
}
int result = 0;
boolean flag = false;
int last = -1, cur = -1;
for (int key : map.keySet()) {
if (flag == false) {
flag = true;
last = key;
continue;
}
cur = key;
int a = map.get(last), b = map.get(cur);
if (Math.abs(last - cur) > K) {
result += a + b;
} else {
result += Math.max(a, b);
}
}
last = cur;
return result;
}
When looking at the examples with K = 1, it is clear that the answer
depends on the parity of the elements. Only elements with same parity can be set to the same level,
and all elements with same parity can be joined.
For example:
[2 4 6] -> [1 5 5] -> [2 4 4] -> [3 3 3]
[1 2 2] -> [2 1 1] ... no progress
With K = 1, we have to consider value modulo 2, i.e. modulo 2*K.
When K is different of one, for example K = 2, two numbers can be joined only there are separated by a distance multiple of 4, i.e. of 2*K.
[2 6 6] -> [4 4 4]
For K different from 1, instead of creating buckets for numbers with same parity,
we just create buckets according to value modulo 2K.
We just have to pay attention to use the modulo and not the remainder, the values are different for negative values.
Then the answer if simply the highest size of a bucket.
Output:
K = 1 Array : 3 1 3 -> 3
K = 1 Array : 1 2 2 -> 2
K = 1 Array : 2 3 4 7 4 9 11 -> 4
K = 1 Array : -3 -1 2 3 -> 3
K = 3 Array : -7 -1 0 1 2 4 5 -> 3
Here is a simple code in C++ to illustrate the algorithm.
In this code, the value val_modulo modulo 2K of each element is calculated.
Then, the orresponding counter is increased
Bucket[val_modulo] = Bucket[val_modulo] + 1
At the end, the highest value corresponds to the number of repetitions of the most repeated final value.
We may note that the number of non empty bucket corrresponds to the number of different
final values (not used in this code).
#include <iostream>
#include <vector>
#include <string>
#include <map>
void print (const std::vector<int> &A, const std::string &after = "\n", const std::string &before = "") {
std::cout << before;
for (int x: A) {
std::cout << x << " ";
}
std::cout << after;
}
int Modulo (int n, int mod) {
int ans = n % mod;
if (ans < 0) ans += mod;
return ans;
}
int max_equal(int K, std::vector<int> A) {
K = std::abs(K); // useful befoe taking the modulo
std::map<int, int> Buckets;
int nmax = 0;
int mod = 2*K;
for (int x: A) {
int val_modulo = Modulo (x, mod); // and not x*mod, as x can be negative
Buckets[val_modulo]++;
}
for (auto x: Buckets) {
if (x.second > nmax) {
nmax = x.second;
}
}
return nmax;
}
int main() {
std::vector<std::vector<int>> examples = {
{3, 1, 3},
{1, 2, 2},
{2, 3, 4, 7, 4, 9, 11},
{-3, -1, 2, 3},
{-7, -1, 0, 1, 2, 4, 5}
};
std::vector<int> tab_K = {1, 1, 1, 1, 3};
for (int i = 0; i < examples.size(); ++i) {
std::cout << "K = " << tab_K[i] << " Array : ";
print (examples[i], " -> ");
auto ans = max_equal (tab_K[i], examples[i]);
std::cout << ans << "\n";
}
return 0;
}
The problem is conceptual, and translating it in somewhat computing code.
Let's look:
We pick a number (by index, which is irrelevant), and for all the occurrences we add K. All others we subtract K And then the number of same occurrences must be maximal.
The same occurrences can only grow when the picked number + K is equal to another number - K.
The data structure:
A map with the array numbers as key, and mapped to frequency (how often the number occurs in the array).
So:
pickedNumber.value + K = otherNumber.value - K
=> otherNumber.value = pickedNumber.value + 2*K
Note that as there is only one single otherNumber, derived from the pickedNumber.
(It might occur more than once.)
And we want maximal:
pickedNumber.frequency + otherNumber.frequency maximal.
Though map is not really needed, a sorted array would do too, let's do a map:
The algorithm:
Kept simple.
int[] numbers = {3, 1, 3};
int index = pickedIndexOfBestSolution(numbers, 1);
System.out.println("Index: " + index);
int pickedIndexOfBestSolution(int[] numbers, int k) {
Map<Integer, Long> frequencyTable = IntStream.of(numbers)
.boxed()
.collect(Collectors.groupingBy(Function.identity(),
Collectors.counting()));
int bestNumber = frequencyTable.entrySet().stream()
.sorted(Comparator.comparingLong(e -> -e.getValue()
- frequencyTable.getOrDefault(e.getKey() + 2*k, 0L)))
.findFirst()
.map(e -> e.getKey())
.orElseThrow();
int index = -1;
while (numbers[++index] != bestNumber) {
}
return index;
}
The frequency table I filled using an IntStream and groupingBy (just as SQL).
As counting is done with long, I just kept that.
To find the max I counted the new occurrence count trying to add the "other" number's frequency too; 0 when no other number.
Instead of using .reverse() to reverse the comparison (largest, max, first), I took the negative value, which to me seems more calculatory.
Notice that a Stream with findFirst to find the max is probably optimal too: no need that the stream creates an intermediate list.
For the index I used brute force (while loop), a kind of indexOf.
Notice if there is no other number, it returns the index of a number with the most occurrences. Which is fine.
In short:
You see the different approach. Actually simpler, and more solid. In fact applying
some (minor) intelligence first. Trying to nail down the problem first.

What is the Time and Space Complexity of LeetCode 241. Different Ways to Add Parentheses?

I am trying to understand what is time complexity of leetcode 241. Different ways to add parentheses. I have used memoization technique. My friend was asked in Google coding round, he couldn't give correct time and space complexity. I found same problem in Leetcode.
Problem:
Given a string of numbers and operators, return all possible results from computing all the different possible ways to group numbers and operators. The valid operators are +, - and *.
Example 1:
Input: "2-1-1"
Output: [0, 2]
Explanation:
((2-1)-1) = 0
(2-(1-1)) = 2
Example 2:
Input: "2 * 3 - 4 * 5"
Output: [-34, -14, -10, -10, 10]
Explanation:
(2*(3-(4*5))) = -34
((23)-(45)) = -14
((2*(3-4))*5) = -10
(2*((3-4)*5)) = -10
(((2*3)-4)*5) = 10
Code:
import java.util.*;
class Solution {
Map<String, List<Integer>> map = new HashMap<>();
public List<Integer> diffWaysToCompute(String input) {
if(map.containsKey(input)) {
return map.get(input);
}
List<Integer> result = new ArrayList<>();
int length = input.length();
for(int i = 0; i< length; i++) {
char character = input.charAt(i);
if(isOperator(character)) {
String part1 = input.substring(0, i);
String part2 = input.substring(i + 1);
List<Integer> part1Result = diffWaysToCompute(part1);
List<Integer> part2Result = diffWaysToCompute(part2);
computeAndStoreResult(input, result, i, part1Result, part2Result);
}
}
//store in map...
map.put(input, result);
//this is when only one input is present.
// input 3 ==> nothing is added in result so add 3 ...
if(result.size() == 0) {
result.add(Integer.valueOf(input));
}
return result;
}
private boolean isOperator(char character) {
return character == '-' || character == '*' || character == '+';
}
private void computeAndStoreResult(String input, List<Integer> result, int i, List<Integer> part1Result, List<Integer> part2Result) {
for(Integer p1 : part1Result) {
for(Integer p2 : part2Result) {
int c= 0;
switch (input.charAt(i)) {
case '+':
c = p1+p2;
break;
case '-':
c = p1-p2;
break;
case '*':
c = p1*p2;
break;
}
result.add(c);
}
}
}
}
I have research on many sites could not find good explanation
This is how recursive tree looks like: Techinque used is divide and conquer with memoization.
Some useful links that I found.
https://www.cnblogs.com/yrbbest/p/5006196.html
https://just4once.gitbooks.io/leetcode-notes/content/leetcode/divide-and-conquer/241-different-ways-to-add-parentheses.html
Observation
If an expression consists of a single number, then there's 1 way to place parentheses.
If there's only 1 math operator in an expression, then there's only 1 way to place parentheses to obtain a relevant result. For example, for 3 + 4 it's (3 + 4).
If there are 2 math operators in an expression, then there are 2 ways to place parentheses. For example, for 3 + 4 - 2, the method will split the expression by + into 3 and 4 - 2, and then by - into 3 + 4 and 2. So, it's 2 ways.
If there are 3 math operators in an expression, then it's 5 results. For example, 1 + 2 - 3 * 4 can be split into 1 and 2 - 3 * 4, 1 + 2 and 3 * 4, 1 + 2 - 3 and 4. As we've learned from 2) and 3), the number of ways is 2 + 1 + 2 = 5` ways.
If there are 4 math operators in an expression, then it's 14 results. For example, 1 + 2 - 3 * 4 + 5 can be split into 1 and 2 - 3 * 4 + 5, 1 + 2 and 3 * 4 + 5, 1 + 2 - 3 and 4 + 5, 1 + 2 - 3 * 4 and 5. As we've learned from 2), 3) and 4) the number of ways is 5 + 2 + 2 + 5 = 14 ways correspondingly.
If the sequence is continued then it will be 42, 132, 429, 1430, 4862, 16796, 58786, 208012, 742900, .... As you can see, it grows exponentially.
Such sequence of numbers has a name and is called Catalan numbers.
Application for your algorithm
As you may have already realised, your algorithm will become exponential even after you obtain results for your left and right subexpressions split by the first math operator.
So, if there are 10 math operators, then after computing the results for the first configuration (expression is split by the first math operator) their number will be 4862.
So, not only is your time complexity exponential, but also your space complexity since you save all the results in a list.

Get consecutive subarray size with largest size

I have an array of numbers, now I want to find all the consecutive subarray sums whose value matches with less than or equal to k. I want to return the largest subarray size.
This is my program:
public static int process(List<Integer> arr, int k) {
int size = 0;
for (int i = 0; i < arr.size(); i++) {
int sum = 0;
for (int j = i; j < arr.size(); j++) {
sum += arr.get(j);
if (sum <= k) {
size = Math.max(size, j - i + 1);
}
}
}
return size;
}
constraints:
arr size is between 1 to 100000
array elements are between 1 to 100
k is between 1 to 1000000
Explanation:
Arrays.asList(2, 3, 5, 1, 1, 2, 1), 7
arr = 2, 3, 5, 1, 1, 2, 1
k= 7
possible sub arrays:
[2], [3], [5], [1], [1], [2], [1]
[2,3], [5,1], [1,1], [1,2], [2,1]
[5,1,1], [1,1,2], [1,2,1]
[1,1,2,1]
Maximum sub array is [1,1,2,1] = its length is 4. So program should return 4.
I got this task in a competitive exam last week, when I used this code it has passed only 50% test cases. It failed for some hidden test cases saying wrong output, and some test cases saying time out.
What is the issue with my code?
Update:
Modified my code little bit. And added a sample example.
Nested loops means that performance is O(n2). You need to re-think the algorithm.
Below is an O(n) solution, which I will show by example. Writing the code is still your challenge.
What we need is a way to know the sum of values before a particular index. With that we can calculate sumRange(i, j) = sumBefore(j) - sumBefore(i).
Since we're looking for sumRange(i, j) = k, we need to check if we have a sumBefore(i) = sumBefore(j) - k. If we do, we have a candidate range with size = j - i.
So, iterate the values and calculate the accumulated sum. Build a Map of accSum to indexAfter the value leading to that accumulated sum.
Say the array is [1, 6, 5, 3, 2, 8, 4, 2, 7, 1] and k = 13:
Index: 0 1 2 3 4 5 6 7 8 9
Value: 1 6 5 3 2 8 4 2 7 1
accSum: 0 1 7 12 15 17 25 29 31 38 39 sumBefore
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
indexAfter: 0 1 2 3 4 5 6 7 8 9 10 index
Adding the dummy 0 → 0 mapping just simplifies the logic of your code.
As you iterate, you have the accumulated sum up to now, inclusive, i.e. sumBefore(i + 1), so look to see if we have a range, i.e. sumBefore(x) = sumBefore(i + 1) - k = accSum - k, so look in the map for accSum - k, and if found, the value is x, meaning we have a candidate range of x, i+1.
I hope that all made sense.
UPDATE
The question was changed to look for sums <= k, not just sums exactly equal to k.
This is easily done with the logic above by changing the Map to a TreeMap, or more specifically a NavigableMap, and replace the get() call with a ceilingEntry() call:
Returns a key-value mapping associated with the least key greater than or equal to the given key, or null if there is no such key.
If the returned key (sum) is greater than the parameter, the result is a subarray sum that is less than k.

Algorithm to partition a list into groups

I have a list of names.
I want to partition this list into groups of a specified size. All groups should be equal to or less than the specified size, with an equal as possible group size across the groups, and as close to the specified size as possible.
What algorithm (Java-esque pseudo code if possible please!) determines the most appropriate group sizes?
For example:
List contains 13 names - max team size 3.
Output (group sizes): 3, 3, 3, 2, 2
List contains 13 names - max team size 4.
Output: 4, 3, 3, 3
List contains 31 names - max team size 5.
Output: 5, 5, 5, 4, 4, 4, 4
List contains 31 names - max team size 6.
Output: 6, 5, 5, 5, 5, 5
List contains 31 names - max team size 10.
Output: 8, 8, 8, 7
It's pretty straightforward. You calculate the result of N div M and add 1 to have the correct number of arrays (N is the list length and M the max team size), then you iterate on all arrays to add an item until you run out of items
example : 43 names, max team number 4 => 43 mod 4 + 1 = 11, remains 3. so 11 arrays (10 with 4 and 1 with 3)
I'm not going to write code for this, but
If the list size is a multiple of the max team size, divide by team size to get the number of groups, all of max size, and stop.
Integer-divide the list size by the max team size, then add one. That's the number of groups.
Subtract the list size from that next higher multiple; that's the number of teams that are one less than the max size.
This obviously only works for inputs that are close to working out; it fails if the max team size is large compared to the size of the list.
If the number of items in the list is N and the max number of items in sublist is K, the solution of your problem comes from solving a Linear Diophantine Equation
K*x + (K-1)*y = N
with additional constraints
x > 0
y >= 0
Where x is the number of sublists of the exact size K, and y is the number of sublists of length K-1.
EDIT : Because of the coefficients to the equation are always off by one from each other, the solution is rather straightforward:
int m = (N+K-1)/K;
int x = N - (K-1)*m; // Number of "full" lists
int y = K*M - N; // Number of off-by-one lists
Example 1:
N = 31, K = 5
m = (31+5-1)/5 = 7
x = 31 - (5-1)*7 = 3 // 3 lists of 5 items
y = 5*7 - 31 = 4 // 4 lists of 4 items
Example 2:
N = 32, K = 4
m = (32+4-1)/4 = 8
x = 32 - (4-1)*8 = 8 // 8 lists of 4 items
y = 4*8 - 32 = 0 // no lists of 3 items
Look up an algorithm for solving linear Diophantine equations on the net - it is not that complicated, if you understand Euclidean algorithm well. The number of solutions of the equation without constraints is infinite, but once you add the constraints, you should get a single solution.
public class Q {
public static void q(int size, int maxTeamSize) {
int numOfTeams = size / maxTeamSize;
int mod = size % maxTeamSize;
numOfTeams += (mod > 0) ? 1 : 0;
System.out.print("\n(" + size + ":" + maxTeamSize + ")");
for (int i = 0; i < numOfTeams; i++) {
System.out.print(" " + (size / numOfTeams + ((i < mod) ? 1 : 0)));
}
}
public static void main(String[] args) {
q(13, 3);
q(12, 4);
q(31, 5);
q(31, 6);
}
}

Categories