restricted subset sum into a specified range - java

i have an array which contains only 2 types of numbers(x and x-1) eg:- {5,5,4,4,5,5,5} and i am given a range like 12-14(inclusive). i already know the length of the array is constant 7 and i also know how many elements of each type there are in an array(count)
now i need to find if there is any combination of elements in the array whose sum falls into that range.
All i need is the number of elements in the subset whose sum falls in that range.
i have solved this problem by using brute force in the following way but it is very in efficient.
here count is the number of x-1's in the array
for(int i=0;i<=7-count;i++){
for(int j=0;j<=count;j++){
if(x*(i)+(x-1)*j>=min && x*(i)+(x-1)*j<=max){
output1=i+j;
}
}
}
could some one plz tell me if there is a better way of solving this
example:-
the array given is {5,5,4,4,5,5,5} and the range given is 12-14.
so i would pick {5,5,4} subset whose sum is 14 and so the answer to the number of elements in the subset will be 3.{5,4,4} can also be picked in this solution

You can improve your brute force by using some analysis.
with N being the array length and n being the result:
0 <= n <=N
0 <= j <= count
0 <= i <= N - count
n = i + j -> j <= n
sum = x * i + (x - 1) * j = x * n - j
min <= x * n - j <= max -> x * n - max <= j <= x * n - min
min <= x * n - j -> n >= (min + j) / x >= min / x
x * n - j <= max -> n <= (max + j) / x <= (max + count) / x
summing up you can use your cycle but with other range:
for (int n = min / x; n <= min (N, (max + count) / x); n++)
{
for (int j = max (0, x * n - max); j <= min (count, x * n - min, n); j++)
{
sum = x * n - j;
if (sum >= min && sum <= max)
{
output1 = n;
}
}
}
P.S.: here's some picture that may help to understand the idea
graph http://i.zlowiki.ru/110917_768e5221.jpg

say you want to find out the number of as and bs which add to n When testing a number of a you only need to use division to find the number of b.
i.e.
number of a * a + number of b * b = n
so
number of b = (n - number of a * a)/b;
EDIT: If this number is a whole number you have a solution.
To test if the division is a whole number you can do
(`n` - `number of a` * `a`) % `b` == 0
if you have a spread of the range which is smaller than b you can do
(`min` - `number of a` * `a`) % `b` <= `max` - `min`
if the spread is greater or equal to b you always have a number of solutions.
I am assuming b is positive.

Related

Maximising a divisor of an array of values and summing the quotients

Given an array of integer values P of length N, I am wanting to maximise a value M where all values of P are floor divided by M and summed to a variable K, but where K is greater than or equal to a value D.
The following constraints hold:
1 ≤ N ≤ 10,000
1 ≤ P[i] ≤ 1,000,000,000
1 ≤ D ≤ 10,000,000
1 ≤ M ≤ 10,000,000
For example,
D = 6, P = [10,11,14,15] and where M starts at 4:
sum([10,11,14,15] / 4) = sum([2,2,3,3]) = 10 # (D <= 10)
sum([10,11,14,15] / 5) = sum([2,2,2,3]) = 9 # (D <= 9)
sum([10,11,14,15] / 6) = sum([1,1,2,2]) = 6 # (D <= 6)
sum([10,11,14,15] / 7) = sum([1,1,2,2]) = 6 * # (D <= 6)
sum([10,11,14,15] / 8) = sum([1,1,1,1]) = 4 # (D > 4) -> break
*M is maximised and the sum K is kept above or equal to D.
I've done the following but want to find an approach that is not brute force:
long Split(long[] P,int D){
long K = Long.MAX_VALUE;
int M = P.length-1;
while(K >= min_amount) {
M++;
K = 0;
for (long value : P)
K += value/M;
}
return M-1;
}
}
A rather simple approach to improve runtime is to use some mathematical properties of your numbers to narrow down the interval in which you search.
Let S be the sum over all elements of the array P, let Mmax be the M we are looking for and Kopt the K calculated with Mmax. Then we can state that
S/Mmax >= Kopt >= D since the flooring can only reduce K. Therefore S/D >= Mmax.
Using this bound you can perform a binary search for the actual Mmax between 1 and S/D.

Explain the implementation of Euler's Totient Implementation

I have seen this code in a coding platform to efficiently calculate the euler's totient for different values.
I am not being able to understand this implementation. I really want to learn this. Could anyone please help me explain this?
for(int i = 1; i < Maxn; i++) { // phi[1....n] in n * log(n)
phi[i] += i;
for(int j = 2 * i; j < Maxn; j += i) {
phi[j] -= phi[i];
}
}
First, lets note that for prime values p, phi(p) = p - 1. This should be fairly intuitive, because all numbers less than a prime must be coprime to said prime. So then we start into our outer for loop:
for(int i = 1; i < Maxn; i++) { // phi[1....n] in n * log(n)
phi[i] += i;
Here we add the value of i to phi(i). For the prime case, this means we need phi(i) to equal -1 beforehand, and all other phi(i) must be adjusted further to account for the number of coprime integers. Focusing on the prime case, lets convince ourselves that these do equal -1.
If we step through the loop, at case i=1, we'll end up iterating over all other elements in our inner loop, subtracting 1.
for(int j = 2 * i; j < Maxn; j += i) {
phi[j] -= phi[i];
}
For any other values to be subtracted j must equal the prime p. But that would require j = 2 * i + i * k to equal p, for some iteration k. That cannot be, because 2 * i + i * k == i * (2 + k) implying that p can be divided evenly by i, which it cannot (since its prime). Thus, all phi(p) = p - 1.
For non-prime i, we need to subtract out the number of coprime integers. We do this in the inner for loop. Reusing the formula from before, if i divides j, we get j / i = (2 + k). So every value less than i can be multiplied by (2 + k) to be less than j, yet have a common factor of (2 + k) with j (thus, not coprime).
However, if we subtracted out (i - 1) multiples containing (2 + k) factors, we'd count the same factors multiple times. Instead, we only count those which are coprime to i, or in other words phi(i). Thus, we are left with phi(x) = x - phi(factor_a) - phi(factor_b) ... to account for all the (2 + k_factor) multiples of coprimes less than said factor, which now share a factor of (2 + k_factor) with x.
Putting this into code gives us exactly what you have above:
for(int i = 1; i < Maxn; i++) { // phi[1....n] in n * log(n)
phi[i] += i;
for(int j = 2 * i; j < Maxn; j += i) {
phi[j] -= phi[i];
}
}
By the way, just out of interest, there's also an O(n) algorithm to achieve the same. We know Euler's product formula for the totient is
phi(n) = n * product(
(p - 1) / p)
where p is a distinct prime that divide n
For example,
phi(18) = 18 * (
(2-1)/2 * (3-1)/3)
= 18 * 2/6
= 18 * 1/3
= 6
Now consider a number m = n * p for some prime p.
phi(n) = n * product(
(p' - 1) / p')
where p' is a distinct prime that divide n
If p divides n, since p already appears in the calculation for phi(n), we do not need to add it to the product section, rather we just add it to the initial multiplier
phi(m) = phi(p * n) = p * n * product(
(p' - 1) / p')
= p * phi(n)
Otherwise, if p does not divide n, we need to use the new prime,
phi(m) = phi(p * n) = p * n * product(
(p' - 1) / p') * (p - 1) / p
= p * (p - 1) / p * n * product(
(p' - 1) / p')
= (p - 1) * phi(n)
Either way, we can calculate the totient of a number multiplied by a prime only from the prime and the number's own totient, which can be aggregated in O(n) by repeatedly multiplying the numbers we've generated so far by the next prime we find until we reach Maxn. We find the next prime by incrementing an index to the successor we haven't recorded a totient for (prime generation here is a benefit).

How to jump to 0 index of array if it will be exceeded in java?

Let's say that i have an array of int's in range 65-90. I'll randomly pick one of elements and add 10 to it. Is it possible that value, if cross range of 90 return to 65? For example - i take a 85 and add 10 to it. So it should be 95, but i want a 70.
You can do it by placing your value in the interval [0, high - low] by removing low to your value, then add the number you want to it, take the modulo of the sum, and finally add low back to get back in the range [low, high]
public static void main(String[] args) {
int low = 65, high = 90;
System.out.println(addWithinInterval(85, 10, low, high));
}
private static int addWithinInterval(int value, int add, int low, int high) {
return (value - low + add) % (high - low + 1) + low;
}
I'll randomly pick one of elements and add 10 to it. Is it possible that value, if cross range of 90 return to 65?
Sure, the remainder operator will do that for you:
n = (n - 65) % (90 - 65) + 65;
Example (live copy):
int n = 85;
for (int x = 0; x < 10; ++x) {
n += 10;
n = (n - 65) % (90 - 65) + 65;
System.out.println(n);
}
Or here on site: Java and JavaScript are different, but their % operators work the same, so:
let n = 85;
for (let x = 0; x < 10; ++x) {
n += 10;
n = (n - 65) % (90 - 65) + 65;
console.log(n);
}

Counting efficiently all the pairs with given sum

I came across the following question.
Given a list where each item represents the duration expressed in seconds of a song, return the total number of pairs of songs such their durations sum to minutes (e.g., 1m0s, 2m0s,..)
Example:
Input: [10,50,20,110,40]
Output: 3 (considering pairs at indexes (0,1),(0,3),(2,4))
I can only think to a brute force approach where I consider all pairs of songs. The time complexity of this approach is O(n^2).
Is there any better way of doing it?
The given problem can be reduced to the fact that we need to discover pairs (a,b) such that (a + b) mod 60 == 0 from the given list A.
Observation #1: For any integer x, (x mod 60) lies from o to 59.
Initialise an array of length 60 with default value set to 0 the index i of which will store the number of elements in the list A, such that x mod 60 = i for all x belonging to A
int freq[60] = {0};
for(int i = 0; i < A.size(); i++)
freq[(A[i] % 60)]++;
Now iterate over the array A again and for for every x, we need the count for the index 60 - (x mod 60) from our cumulative frequency map, which will corresponds to the number of elements it can form a pair with. The case where (x mod 60) == 30 would be a tricky one, which will require us to subtract 1 from the frequency count.
int ans = 0;
for(int i = 0; i < A.size(); i++) {
ans += freq[60 - (A[i] % 60)];
if(A[i] % 60 == 30) ans--;
}
The overall complexity of the solution is O(n).
Think on the lines of hashing, creating buckets and modulo division. All the possible minutes will go into one of 60 possible buckets. Then think how many possible combinations can there be when you choose two of second values from any two buckets. Then use nC2 to count. Here's my solution in Java.
public int numPairsDivisibleBy60(int[] time) {
int k = 60;
int[] mods = new int[k];
for (int i = 0; i < time.length; i++)
mods[time[i] % k]++;
// n(n-1)/2 pairs for multiples of k and numbers which leave remainder as half multiple of k
int count = ((mods[0] * (mods[0] - 1)) / 2) +
((mods[k / 2] * (mods[k / 2] - 1)) / 2);
for (int i = 1; i < k / 2; i++)
count += mods[i] * mods[k - i];
return count;
}

Identify number of iterations of while loop

I have this code from my computer science class:
int input=15;
while (input < n ) { input = input *3;}
This code has the ceiling of log3(n/15) loops. How can we obtain this result?
I think he is talking about the analytical solution to complexity. I think it's something like this (long time ago i did logaritms):
15 * 3^x = n // x would be the number of iterations until value reaches n
ln(15*(3^x)) = ln(n)
ln(15) + ln(3^x) = ln(n)
ln(15) + x*ln(3) = ln(n)
x = (ln(n) - ln(15)) / ln(3)
x = ln(n/15) / ln(3)
x = log3(n/15) / log3(3)
x = log3(n/15)
For what values of n does the code loop k times?
It must be that 15 < n, and 15*3 < n and 15*3*3 < n and .... and 15*3^(k-1) < n. Also, it must be that 15*3^k >= n (otherwise the code would do at least one more loop).
That is, 3^k >= n/15 > 3^(k-1), and taking logs (base 3), k >= log3(n/15) > k-1.
Thus k is the smallest integer greater than or equal to log3(n/15), or equivalently: k = ceil(log3(n/15)) as required.

Categories

Resources