How to distribute integers topdown a set of persons? - java

I have a sorted set of persons, and I want to distribute a defined count topdown.
Example:
Scenario:
int i = 8;
personA = 0;
personB = 0;
personC = 0;
Desired result:
personA = 3;
personB = 3;
personC = 2;
Of course I could iterate with for/while loops, but if i is very large that might be inefficient.
Can the result also be optained by devision, without loops?
Pseudocode:
distributed = 0;
while (distributed < i) {
for (int p : persons) {
p++;
distributed++;
}
}
interation steps:
1-1-1 > start over > 2-2-2 > start over > 3-3-2

Yes, of course it's possible. Since you didn't give any particular implementation, I'll just put the numbers in an array. This is just a demonstration, of course. Assume NUM_OF_PERSONS is the number of persons in your array, and NUM_TO_DISTRIBUTE is the number you want to distribute. In your example, 8.
int persons[] = new int[NUM_OF_PERSONS];
int basicRation = NUM_TO_DISTRIBUTE / NUM_OF_PERSONS;
int peopleGettingExtra = NUM_TO_DISTRIBUTE % NUM_OF_PERSONS;
for ( int i = 0; i < NUM_OF_PERSONS; i ++ ) {
persons[i] = basicRation + ( i < peopleGettingExtra ? 1 : 0 );
}
Test case 1: 9 to give, 3 people. The base ration is 3. The number of people getting extra is zero. In the loop, since no i will be less than 0, everybody will get basicRation + 0 which means 3.
Test case 2: 8 to give, 3 people. The base ration is 2. The number of people getting extra is 2. In the loop, people with indexes 0 and 1 will get 2 + 1, and the last person gets 2 + 0.

distributed = 0;
int howManyForEach = (int)(i/persons.size())
for(int p : person){
p = howManyForEach ;
}
distrubuted = howManyForEach * persons.size();
while (distributed < i) {
for (int p : persons) {
p++;
distributed++;
}
}

The following code will run for i=8 5 times, i=10 4 times, as i increase , number of time loop executed is proportionately less
int i = 10;
int[] arr = new int[3] { 0, 0, 0 };
int BaseVal = i / (arr.Length);
int Remainder = i - (BaseVal * arr.Length);
for (int x = 0; x < arr.Length ;x++)
{
arr[x] = BaseVal;
}
for (int y = 0; y < Remainder; y++)
{
arr[y] = arr[y] + 1;
}

from the problem it is clear, that the maximal difference between any person is 1.
Just use #persons / i = base value for every person
and #persons % i = # top persons with +1 value
Example
i = 5 , persons = 3:
baseValue = 1
#topPersons = 2
=> 2 2 1

Related

Count the number of all possible distinct 3-digit numbers given a String array

Below is my code for counting the number of distinct 3 digit strings which works correctly, HOWEVER, I would like to optimize this code to where I can improve the time complexity. Can someone help me w/ this?
input: [1,2,1,4]
output: 12
Thanks.
static int countUnique(String [] arr)
{
Set<String> s = new TreeSet<>();
for (int i = 0; i<arr.length; i++)
{
for (int j = 0; j<arr.length; j++)
{
for (int k = 0; k<arr.length; k++)
{
if (i!=j && j != k && i!=k)
s.add((arr[i] +""+arr[j]+""+arr[k]));
}
}
}
return s.size();
}
Here's an O(n) solution:
Iterate over each distinct available digit in turn.
(A) Add 1 if there are three instances of it, accounting for one string of three of this digit.
(B) If there are two instances of it, add 3 times the number of digits already iterated over, accounting for 3 choose 2 ways to arrange two instances of this digit with one other digit already iterated over.
(C) Add the number of ways we can pick two of the digits seen so far, accounting for arranging just one instance of this digit with each of those.
(D) Finally, add to our record of the counts of ways to arrange two digits: if there are two instances of this digit, add 3 choose 2 = 3, accounting for just arranging two instances of this digit. Also add (2 * 3 choose 2 = 6) times the number of digits already iterated over, accounting for the number of ways to arrange this digit with another already seen.
For example:
1 2 1 4
1 -> D applies, add 3 to the two-digit-arrangements count
11x, 1x1, x11
2 -> C applies, add 3 to result
112, 121, 211
D applies, add 6 to the two-digit-arrangements count (total 9)
12x, 1x2, x12, 21x, 2x1, x21
4 -> C applies, add 9 to result
Result 12
JavaScript code with random tests, comparing with your brute force approach:
function f(A){
const counts = {};
for (let a of A)
counts[a] = counts[a] ? -~counts[a] : 1;
let result = 0;
let numTwoDigitArrangements = 0;
let numSeen = 0;
for (let d of Object.keys(counts)){
if (counts[d] > 2)
result += 1;
if (counts[d] > 1)
result += 3 * numSeen;
result += numTwoDigitArrangements;
if (counts[d] > 1)
numTwoDigitArrangements += 3;
numTwoDigitArrangements += 6 * numSeen;
numSeen = numSeen + 1;
}
return result;
}
function bruteForce(arr){
const s = new Set();
for (let i=0; i<arr.length; i++){
for (let j=0; j<arr.length; j++){
for (let k=0; k<arr.length; k++){
if (i != j && j != k && i != k)
s.add((arr[i] + "" + arr[j]+ "" + arr[k]));
}
}
}
return s.size;
}
// Random tests
var numTests = 500;
var maxLength = 25;
for (let i=0; i<numTests; i++){
const n = Math.ceil(Math.random() * maxLength);
const A = new Array(n);
for (let j=0; j<n; j++)
A[j] = Math.floor(Math.random() * 10);
const _f = f(A);
const _bruteForce = bruteForce(A);
if (_f != _bruteForce){
console.log('Mismatch found:');
console.log('' + A);
console.log(`f: ${ _f }`);
console.log(`brute force: ${ _bruteForce }`);
}
}
console.log('Done testing.');
Another way to solve this is by Backtracking algorithm. Any combination or permutation kind of problem can be solved using Backtracking.
Here is some information on Backtracking algorithm - https://en.wikipedia.org/wiki/Backtracking
Note: This is not most optimized solution nor O(n) solution. This solution is O(n! * n). But there are many opportunities to make it more optimized.
Java code using Backtracking:
int countUniqueOpt(String[] arr) {
//Set to avoid duplicates
Set<String> resultList = new HashSet<>();
backtracking(arr, 3, resultList, new ArrayList<>());
return resultList.size();
}
void backtracking(String[] arr, int k, Set<String> resultList, List<Integer> indexList) {
if (indexList.size() == k) {
String tempString = arr[indexList.get(0)] + arr[indexList.get(1)] + arr[indexList.get(2)];
resultList.add(tempString);
} else {
for (int i = 0; i < arr.length; i++) {
if (!indexList.contains(i)) {
indexList.add(i);
backtracking(arr, k, resultList, indexList);
indexList.remove(indexList.size() - 1);
}
}
}
}

Divide an Array in equal size, such that value of given function is minimum

I've came across the following problem statement.
You have a list of natural numbers of size N and you must distribute the values in two lists A and B of size N/2, so that the squared sum of A elements is the nearest possible to the multiplication of the B elements.
Example:
Consider the list 7 11 1 9 10 3 5 13 9 12.
The optimized distribution is:
List A: 5 9 9 12 13
List B: 1 3 7 10 11
which leads to the difference abs( (5+9+9+12+13)^2 - (1*3*7*10*11) ) = 6
Your program should therefore output 6, which is the minimum difference that can be achieved.
What I've tried:
I've tried Greedy approach in order to solve this problem. I took two variables sum and mul. Now I started taking elements from the given set one by one and tried adding it in both the variables and calculated current
square of sum and multiplication. Now finalize the element in one of the two sets, such that the combination gives minimum possible value.
But this approach is not working in the given example itselt. I can't figure out what approach could be used here.
I'm not asking for exact code for the solution. Any possible approach and the reason why it is working, would be fine.
EDIT:
Source: CodinGame, Community puzzle
Try out this:
import java.util.Arrays;
public class Test {
public static void main(String [] args){
int [] arr = {7, 11, 1, 9, 10, 3, 5, 13, 9, 12};
int [][] res = combinations(5, arr);
int N = Arrays.stream(arr).reduce(1, (a, b) -> a * b);
int min = Integer.MAX_VALUE;
int [] opt = new int [5];
for (int [] i : res){
int k = (int) Math.abs( Math.pow(Arrays.stream(i).sum(), 2) - N/(Arrays.stream(i).reduce(1, (a, b) -> a * b)));
if(k < min){
min = k;
opt = i;
}
}
Arrays.sort(opt);
System.out.println("minimum difference is "+ min + " with the subset containing this elements " + Arrays.toString(opt));
}
// returns all k-sized subsets of a n-sized set
public static int[][] combinations(int k, int[] set) {
int c = (int) binomial(set.length, k);
int[][] res = new int[c][Math.max(0, k)];
int[] ind = k < 0 ? null : new int[k];
for (int i = 0; i < k; ++i) {
ind[i] = i;
}
for (int i = 0; i < c; ++i) {
for (int j = 0; j < k; ++j) {
res[i][j] = set[ind[j]];
}
int x = ind.length - 1;
boolean loop;
do {
loop = false;
ind[x] = ind[x] + 1;
if (ind[x] > set.length - (k - x)) {
--x;
loop = x >= 0;
} else {
for (int x1 = x + 1; x1 < ind.length; ++x1) {
ind[x1] = ind[x1 - 1] + 1;
}
}
} while (loop);
}
return res;
}
// returns n choose k;
// there are n choose k combinations without repetition and without observance of the sequence
//
private static long binomial(int n, int k) {
if (k < 0 || k > n) return 0;
if (k > n - k) {
k = n - k;
}
long c = 1;
for (int i = 1; i < k+1; ++i) {
c = c * (n - (k - i));
c = c / i;
}
return c;
}
}
Code taken from this stackoverflow answer, also take a look at this wikipedia article about Combinations.
I am not sure if there is any exact solution in polynomial time. But you could try a simulated annealing based approach.
My approach would be:
Initialize listA and listB to a random state
With probability p run greedy step, otherwise run a random step
Keep track of the state and corresponding error (with a HashMap)
Greedy step: Find one element you can move between the list that optimizes the error.
Random Step: Pick a random element from either of these two sets and calculate the error. If the error is better, keep it. Otherwise with probability of q keep it.
At either of these two steps make sure that the new state is not already explored (or at least discourage it).
Set p to a small value (<0.1) and q could depend on the error difference.

how can i amend this code to have a time complexity of o(log n) or o(n) instead of o(n^2)

How can I solve this in o(n) or o(logn)?
After the lessons n groups of schoolchildren went outside and decided to visit Polycarpus to celebrate his birthday. We know that the i-th group consists of si friends (1 ≤ si ≤ 4), and they want to go to Polycarpus together. They decided to get there by taxi. Each car can carry at most four passengers. What minimum number of cars will the children need if all members of each group should ride in the same taxi (but one taxi can take more than one group)?
Following is my approach but in o(n^2)
import java.util.*;
public class taxi {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while (sc.hasNext()) {
int cars = 0;
int groups = sc.nextInt();
int[] a = new int[groups];
for (int i = 0; i < a.length; i++) {
a[i] = sc.nextInt();
}
Arrays.sort(a);
for (int i = a.length - 1; i > 0; i--) {
if (a[i] == 4) {
cars = cars + 1;
} else {
if (a[i] == 0) {
break;
} else {
int y = 4 - a[i];
for (int j = i - 1; j >= 0; j--) {
if (y - a[j] == 0) {
a[j] = 0;
break;
}
if (y - a[j] > 0) {
y = y - a[j];
a[j] = 0;
}
}
cars = cars + 1;
Arrays.sort(a);
}
}
}
if (a[0] != 0) {
cars++;
}
System.out.println(cars);
cars = 0;
}
}
}
You'll never achieve O(log N) since you have to examine every group.
You can do it in a single traversal of the set of groups and a cache: so O(N).
For each group, count the size.
If it's 4, then add a taxi.
If it's 3, then attempt to pair with a cached 1, if not, cache that group.
If it's 1, then attempt to pair with a cached 3, if not, cache that group.
If it's 2, then attempt to pair with a cached 2, if not, cache that group.
Examine your cache. Pair any group of 2 with one or more group of 1. Then pair any remaining groups of 1.
A similar solution to what Bathsheba suggested, but based on the number of groups of each size instead of caching :
Iterate over the list of group once and count how many there are of each size. This required O(n) time and gives you the following counters :
int size1 = ...
int size2 = ...
int size3 = ...
int size4 = ...
Now calculate the number of cars based on these counters (this calculation takes O(1)) :
int carsCount = size4; // add full cars (groups of 4)
carsCount += size3; // each group of 3 requires a separate car
carsCount += size2/2; // pairs of groups of 2
size1 -= size3; // group as many groups of 1 with groups of 3 as possible
boolean odd2s = (size2 % 2) == 1;
if (odd2s) {
carsCount++; // add a car for the odd group of 2
}
if (size1 > 0) {
if (odd2s) {
size1 -= 2; // two groups of 1 can be paired with the odd group of 2
}
}
if (size1 > 0) {
carsCount += (size1 + 3) / 4; // any remaining groups of 1 are grouped in groups of 4
}
There are only 4 group sizes: 1, 2, 3 or 4.
Initialize an array of counts for each size, pass through your array of group sizes, incrementing the appropriate array element for the group size.
// Count the number of groups of each size.
int[] counts = new int[5]; // 1 more, just to use 1-based indexing.
for (int e : a) {
++counts[e];
}
which is O(n).
Then, pass through like this:
int numTaxis = counts[4]; // 1 taxi for each 4-sized group.
+ counts[3]; // 1 taxi for each 3-sized group.
+ counts[2] / 2; // 1 taxi for each pair of 2-sized groups.
// But you can put a 1-sized group in each 3-sized group's taxi.
int unmatched1s = Math.max(0, counts[1] - counts[3]);
// You require ceil(unmatched1s / 4) taxis for the 1-sized groups -
// - but increase by 1 to handle the possibility of unpaired 2-sized group.
numTaxis += (unmatched1s + counts[2] % 2 + 3) / 4;
which is O(1), meaning O(n) overall.

in Java, design linear algorithm that finds contiguous subsequence with highest sum

this is the question, and yes it is homework, so I don't necessarily want anyone to "do it" for me; I just need suggestions: Maximum sum: Design a linear algorithm that finds a contiguous subsequence of at most M in a sequence of N long integers that has the highest sum among all such subsequences. Implement your algorithm, and confirm that the order of growth of its running time is linear.
I think that the best way to design this program would be to use nested for loops, but because the algorithm must be linear, I cannot do that. So, I decided to approach the problem by making separate for loops (instead of nested ones).
However, I'm really not sure where to start. The values will range from -99 to 99 (as per the range of my random number generating program).
This is what I have so far (not much):
public class MaxSum {
public static void main(String[] args){
int M = Integer.parseInt(args[0]);
int N = StdIn.readInt();
long[] a = new long[N];
for (int i = 0; i < N; i++) {
a[i] = StdIn.readLong();}}}
if M were a constant, this wouldn't be so difficult. For example, if M==3:
public class MaxSum2 {
public static void main(String[] args){
int N = StdIn.readInt(); //read size for array
long[] a = new long[N]; //create array of size N
for (int i = 0; i < N; i++) { //go through values of array
a[i] = StdIn.readLong();} //read in values and assign them to
//array indices
long p = a[0] + a[1] + a[2]; //start off with first 3 indices
for (int i =0; i<N-4; i++)
{if ((a[i]+a[i+1]+a[1+2])>=p) {p=(a[i]+a[i+1]+a[1+2]);}}
//if sum of values is greater than p, p becomes that sum
for (int i =0; i<N-4; i++) //prints the subsequence that equals p
{if ((a[i]+a[i+1]+a[1+2])==p) {StdOut.println((a[i]+a[i+1]+a[1+2]));}}}}
If I must, I think MaxSum2 will be acceptable for my lab report (sadly, they don't expect much). However, I'd really like to make a general program, one that takes into consideration the possibility that, say, there could be only one positive value for the array, meaning that adding the others to it would only reduce it's value; Or if M were to equal 5, but the highest sum is a subsequence of the length 3, then I would want it to print that smaller subsequence that has the actual maximum sum.
I also think as a novice programmer, this is something I Should learn to do. Oh and although it will probably be acceptable, I don't think I'm supposed to use stacks or queues because we haven't actually covered that in class yet.
Here is my version, adapted from Petar Minchev's code and with an important addition that allows this program to work for an array of numbers with all negative values.
public class MaxSum4 {
public static void main(String[] args)
{Stopwatch banana = new Stopwatch(); //stopwatch object for runtime data.
long sum = 0;
int currentStart = 0;
long bestSum = 0;
int bestStart = 0;
int bestEnd = 0;
int M = Integer.parseInt(args[0]); // read in highest possible length of
//subsequence from command line argument.
int N = StdIn.readInt(); //read in length of array
long[] a = new long[N];
for (int i = 0; i < N; i++) {//read in values from standard input
a[i] = StdIn.readLong();}//and assign those values to array
long negBuff = a[0];
for (int i = 0; i < N; i++) { //go through values of array to find
//largest sum (bestSum)
sum += a[i]; //and updates values. note bestSum, bestStart,
// and bestEnd updated
if (sum > bestSum) { //only when sum>bestSum
bestSum = sum;
bestStart = currentStart;
bestEnd = i; }
if (sum < 0) { //in case sum<0, skip to next iteration, reseting sum=0
sum = 0; //and update currentStart
currentStart = i + 1;
continue; }
if (i - currentStart + 1 == M) { //checks if sequence length becomes equal
//to M.
do { //updates sum and currentStart
sum -= a[currentStart];
currentStart++;
} while ((sum < 0 || a[currentStart] < 0) && (currentStart <= i));
//if sum or a[currentStart]
} //is less than 0 and currentStart<=i,
} //update sum and currentStart again
if(bestSum==0){ //checks to see if bestSum==0, which is the case if
//all values are negative
for (int i=0;i<N;i++){ //goes through values of array
//to find largest value
if (a[i] >= negBuff) {negBuff=a[i];
bestSum=negBuff; bestStart=i; bestEnd=i;}}}
//updates bestSum, bestStart, and bestEnd
StdOut.print("best subsequence is from
a[" + bestStart + "] to a[" + bestEnd + "]: ");
for (int i = bestStart; i<=bestEnd; i++)
{
StdOut.print(a[i]+ " "); //prints sequence
}
StdOut.println();
StdOut.println(banana.elapsedTime());}}//prints elapsed time
also, did this little trace for Petar's code:
trace for a small array
M=2
array: length 5
index value
0 -2
1 2
2 3
3 10
4 1
for the for-loop central to program:
i = 0 sum = 0 + -2 = -2
sum>bestSum? no
sum<0? yes so sum=0, currentStart = 0(i)+1 = 1,
and continue loop with next value of i
i = 1 sum = 0 + 2 = 2
sum>bestSum? yes so bestSum=2 and bestStart=currentStart=1 and bestEnd=1=1
sum<0? no
1(i)-1(currentStart)+1==M? 1-1+1=1 so no
i = 2 sum = 2+3 = 5
sum>bestSum? yes so bestSum=5, bestStart=currentStart=1, and bestEnd=2
sum<0? no
2(i)-1(currentStart)+1=M? 2-1+1=2 so yes:
sum = sum-a[1(curentstart)] =5-2=3. currentStart++=2.
(sum<0 || a[currentStart]<0)? no
i = 3 sum=3+10=13
sum>bestSum? yes so bestSum=13 and bestStart=currentStart=2 and bestEnd=3
sum<0? no
3(i)-2(currentStart)+1=M? 3-2+1=2 so yes:
sum = sum-a[1(curentstart)] =13-3=10. currentStart++=3.
(sum<0 || a[currentStart]<0)? no
i = 4 sum=10+1=11
sum>bestSum? no
sum<0? no
4(i)-3(currentStart)+1==M? yes but changes to sum and currentStart now are
irrelevent as loop terminates
Thanks again! Just wanted to post a final answer and I was slightly proud for catching the all negative thing.
Each element is looked at most twice (one time in the outer loop, and one time in the while loop).
O(2N) = O(N)
Explanation: each element is added to the current sum. When the sum goes below zero, it is reset to zero. When we hit M length sequence, we try to remove elements from the beginning, until the sum is > 0 and there are no negative elements in the beginning of it.
By the way, when all elements are < 0 inside the array, you should take only the largest negative number. This is a special edge case which I haven't written below.
Beware of bugs in the below code - it only illustrates the idea. I haven't run it.
int sum = 0;
int currentStart = 0;
int bestSum = 0;
int bestStart = 0;
int bestEnd = 0;
for (int i = 0; i < N; i++) {
sum += a[i];
if (sum > bestSum) {
bestSum = sum;
bestStart = currentStart;
bestEnd = i;
}
if (sum < 0) {
sum = 0;
currentStart = i + 1;
continue;
}
//Our sequence length has become equal to M
if (i - currentStart + 1 == M) {
do {
sum -= a[currentStart];
currentStart++;
} while ((sum < 0 || a[currentStart] < 0) && (currentStart <= i));
}
}
I think what you are looking for is discussed in detail here
Find the subsequence with largest sum of elements in an array
I have explained 2 different solutions to resolve this problem with O(N) - linear time.

Finding the longest down sequence in a Java array

Given this array
int [] myArray = {5,-11,2,3,14,5,-14,2};
I must be able to return 3 because the longest down sequence is 14,5,-14.
What's the fastest way to do this?
PS: Down sequence is a series of non-increasing numbers.
another implementation in python:
def longest_down_sequence(seq):
max = 0
current_count = 0
last = None
for x in seq:
if x <= last: current_count += 1
else: current_count = 1
if current_count > max: max = current_count
last = x
return max
Just make one pass through the list of numbers. Pseudocode:
bestIndex = 0
bestLength = 0
curIndex = 0
curLength = 1
for index = 1..length-1
if a[index] is less than or equal to a[index-1]
curLength++
else
//restart at this index since it's a new possible starting point
curLength = 1
curIndex = index
if curLength is better than bestLength
bestIndex = curIndex
bestLength = curLength
next
Note: You can ditch any line containing bestIndex or curIndex if you don't care about knowing where that subsequence occurs, as seen in Gary's implementation.
In java:
int [] myArray = {5,-11,2,3,14,5,-14,2};
int downSequence = 1;
int longestDownSequence = 1;
for(int i = 1; i < myArray.length; i++) {
if(myArray[i] <= myArray[i-1]) downSequence++;
else {
if(downSequence > longestDownSequence)
longestDownSequence = downSequence;
downSequence = 1;
}
}
if(downSequence > longestDownSequence)
longestDownSequence = downSequence;
System.out.println(longestDownSequence);
Since you're asking for fastest or better performance, only check for the longest down sequence just before you reset the counter. Never on each iteration. However, you have to check again after the loop in case the longest sequence is at the end of the array.
Another solution in Java:
static int[] longestDownSequenceList(int[] array) {
if (array.length <= 1) {
return array;
}
int maxSize = 1;
int maxEnd = 0;
int curSize = 1;
for (int i = 1; i < array.length; i++) {
if (array[i] < array[i-1]) {
curSize++;
if (curSize > maxSize) {
maxSize = curSize;
maxEnd = i;
}
}
else {
curSize = 1;
}
}
return Arrays.copyOfRange(array, maxEnd-maxSize+1, maxEnd+1);
}
As Bill above said, this is essentially longest increasing subsequence. See the wikipedia entry for the optimal solution. This is quoted from there with small changes to work for the nondecreasing case
L = 0
for i = 1, 2, ... n:
binary search for the largest positive j ≤ L such that X[M[j]] >= X[i] (or set j = 0 if no such value exists)
P[i] = M[j]
if j == L or X[i] >= X[M[j+1]]:
M[j+1] = i
L = max(L, j+1)
See counterexample to other proposed solution in my comment above.
The fastest way might depend on the environment: computer and problemsize.
For a very large List (or array) it might be useful to parallelize the job, which could be implemented:
Split and split and split the List to simple elements.
Glue together elements which are down sequences (or non increasing) to chunks, and glue together chunks, if possible.
Search for the longest chunk.

Categories

Resources