Group the numbers C++ - c++

Here's the problem:
You have N (N represents the number of numbers that you have) numbers. Divide them in 2 groups in such way that the difference between sums of the numbers in the groups is minimal.
Examples:
5 // N
1, 9, 5, 3, 8 // The numbers
The difference is 0 if we put 1, 9 and 3 in Group A and 5 and 8 in Group B.
I think first I should calculate the sum of all numbers and divide it by 2. Then to check ever possible combination of numbers, whose sum is not higher than half of the sum of all numbers. After I do this I will choose the biggest number and print out the groups.
I have problem with going through all combinations, especialy when N is big numbers. How can I run through all combinations?
Also i think a little bit differently, I will group the numbers in descending order and i'll put the biggest number in Group A and lowest in Group B. Then I do the other way around. This works with some of the numbers, but sometimes it doesn't show the optimal grouping. For example:
If I use the previous example. Arrange the number in descending order.
9, 8, 5, 3, 1.
Put the biggest in Group A and lowest in Group B.
Group A: 9
Group B: 1
Other way around.
Group A: 9, 3
Group B: 1, 8
And so on. If in the end i have only one number I'll put it in the group with lower sum.
So I finally will get:
Group A: 9, 3
Group B: 1, 8, 5
This isn't the optimal grouping because the difference is 2, but with grouping in different way the difference can be 0, as I showed.
How can I get optimal grouping?
CODE:
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
int convertToBinary(int number) {
int remainder;
int binNumber = 0;
int i = 1;
while(number!=0)
{
remainder=number%2;
binNumber=binNumber + (i*remainder);
number=number/2;
i=i*10;
}
return binNumber;
}
int main()
{
int number, combinations, sum = 0;
double average;
cin >> number;
int numbers[number];
for(int i = 0; i<number; i++)
{
cin >> numbers[i];
sum += numbers[i];
}
if(sum%2 == 0)
{
average = sum/2;
}
else
{
average = sum/2 + 0.5;
}
combinations = pow(2,number-1);
double closest = average;
for(int i = 0; i<=combinations;i++)
{
int rem;
int temp_sum = 0;
int state = convertToBinary(i);
for(int j = 0; state!=0; j++)
{
int rem =state%10;
state = state/10;
if(rem == 1)
{
temp_sum = temp_sum + numbers[j];
}
}
if(abs(average-temp_sum)<closest)
{
closest = abs(average-temp_sum);
if(closest == 0)
{
break;
}
}
}
cout << closest*2;
return 0;
}

Although this, as others have commented, is an NP-Complete problem, you have provided two fairly helpful bounds: you only want to split the group of numbers into two groups and you want to get the sums of the two groups as close as possible.
Your suggestion of working out the total sum of the numbers and dividing it by two is the right starting point - this means you know what the ideal sum of each group is. I also suspect that your best bet is to start by putting the largest number into, say, group A. (it has to go into one group, and it's the worst one to place later, so why not put it in there?)
This is when we get into heuristics which you cycle through until the groups are done:
N: Size of list of numbers.
t: sum of numbers divided by two (t is for target)
1. Is there a non-placed number which gets either group to within 0.5 of t? If so, put it in that group, put the remaining numbers in the other group and you're done.
2. If not, place the biggest remaining number in the group with the current lowest sum
3. go back to 1.
There will doubtless be cases that fail, but as a rough approach this should get close fairly often. To actually code the above, you will want to put the numbers in an ordered list so it is easy to work through them from largest to smallest. (Step 1 can then also be streamlined by checking (against both "groups so far") from largest remaining down until the "group so far" added to the number being checked are more then 1.0 below t - after that the condition cannot be met.)
Do let me know if this works!

Using the constraint of only two groups zour problem is already solved if you can find one grouping of numbers whichs sum is exactlz half of the total. thus I suggest you try to find this group and put the remainder into the other group obviously.
The assumption to put the biggest number in the first group is simple. Now the rest is more tricky.
This is simple in the binary system: Consider that for each number you have a bit. The bit beeing 1 signals that the number is in group A, otherwise it is in group B. The entire distribution can be described by a concatination of these bits. This can be considered a number. SO to check all combinations you have to go through all the numbers and calculate the combination.
code:
#include <iostream>
#include <memory>
using namespace std;
int partition(const std::unique_ptr<int[]>& numbers, int elements) {
int sum = 0;
for(int i=0; i<elements; ++i) {
sum += numbers[i];
}
double average = sum/2.0;
double closest = average+.5;
int beststate = 0;
for(int state=1; state< 1<<(elements-1);++state) {
int tempsum = 0;
for(int i=0; i<elements; ++i) {
if( state&(1<<i) ) {
tempsum += numbers[i];
}
}
double delta=abs(tempsum-average);
if(delta < 1) { //if delta is .5 it won't get better i.e. (3,5) (9) => average =8.5
cout << state;
return state;
}
if(delta<closest) {
closest = delta;
beststate = state;
}
}
return beststate;
}
void printPartition(int state, const std::unique_ptr<int[]>& numbers, int elements) {
cout << "(";
for(int i=0; i<elements; ++i) {
if(state&(1<<i)) {
cout << numbers[i]<< ",";
}
}
cout << ")" << endl;
}
int main()
{
int elements;
cout << "number of elements:";
cin >> elements;
std::unique_ptr<int[]> numbers(new int[elements]);
for(int i = 0; i<elements; i++)
{
cin >> numbers[i];
}
int groupA = partition(numbers, elements);
cout << "\n\nSolution:\n";
printPartition(groupA, numbers, elements);
printPartition(~groupA,numbers, elements);
return 0;
}
edit: For further (and better) solutions to generating all possibilities check this awnser. Here is a link to knuths book which I found here
edit2: To explain the concept of enumeration as requested:
suppose we have three elements, 1,23,5. all possible combinations disregarding permutations can be generated by filling out a table:
1 | 23 | 5 Concatination Decimal interpretation
-----------
0 | 0 | 0 000 0
0 | 0 | 1 001 1
0 | 1 | 0 010 2
0 | 1 | 1 011 3
1 | 0 | 0 100 4
1 | 0 | 1 101 5
1 | 1 | 0 110 6
1 | 1 | 1 111 7
If we now take for instant the number 4 this maps to 100 which says that the first number is in group A and the second and third number are not (which implies they are in group B). Thus A is 1 while B is 23,5.
Now to explain the trick why I only need to look at half: if we look at the decimal interpretation 3 (011 binary) we get for Group A 23,5 and for Group B 1. If we compare this to the example for 4 we notice that we have the same numbers grouped, just in the exactly opposite group names. Since this makes no difference for your problem we don't have to look at this.
edit3:
I added real code to try out, in the pseudocode i made the wrong assumption that i would always include the first element in the sum, which was wrong. As for your code that I started out on: you can't allocate arrays like that. Another solution instead of an Array would be a vector<int> which avoids the problems of having to pass the arraysize to the functions. Using this would be a great improvement. Furthermore this code is far from good. You will run into issues with int size (usually this should work for up to 32 elements). You can work arround this though (try this maybe How to handle arbitrarily large integers). Or you actually read up on knuth (see above) I am sure you will find some recursive approach.
THis code is also slow, since it always rebuilds the whole sum. One optimization would be to look into gray codes (I think Knuth describes them aswell). That way you only have to add/substract one number per permutation you test. That would be a performance boost in the order of n, since you replace n-1 additions with 1 addition/substraction.

If the average value of the individual group is same as the average of the complete set, then obviously the difference between the two group will be less. By using this we can bring up a algorithm for this problem.
Get the average of the complete set.
Take the largest value in the set and put it in one of the group.
Get the difference of the average of the individual group and the average of the entire set.
Place the next largest number in the group which is having the maximum difference.
Repeat the steps 3 and 4 until all the numbers are placed.
This will be the efficient approach to get the near optimal solution.

How about this:
Sort the list of numbers.
Put the largest number in group A. Remove that number from the list.
If the sum of all numbers in group A is less than the sum of all numbers in group B, goto 2.
Put the largest number in group B. Remove that number from the list.
If the sum of all numbers in group B is less than the sum of all numbers in group A, goto 4.
If more than zero numbers remain in the list, goto 2.

Related

Count amount of 3s in a series of numbers

(This isn't homework)
I'm working on a practice question (https://train.nzoi.org.nz/problems/1207) - "count the number of 3's when printing the numbers from 1 to N". I haven't found any solution online and I was wondering what a more efficient way to answer this question is.
my solution is:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int l;
cin >> l;
int c=0;
for (int i = 0; i < (l + 1); i++)
{
int j = i;
while (j>0)
{
int tmp = j%10;
if (tmp == 3) c++;
j /= 10;
}
}
cout << c << endl;
return 0;
}
although this takes a long time on long numbers.
what is a more efficient way to solve this problem?
EDIT:
For clarification, This is trying to find all instances of 3 while counting from 0 => N
E.G: 13 => 2 occurances of 3
Looks like a good use-case for recursion. Call your function f(n) (short name because I'm going to use math notation below). Then calculate f(n) by something like f(a) + f(b) + ... when all of the numbers a, b, ... are much smaller than n.
I am only going to give ideas by examples, not code. I hope this will be complete enough to write code, and not too much, so the task remains interesting.
First of all:
f(0) = 0
f(1) = 0
f(2) = 0
f(3) = 1
f(4) = 1
...
f(9) = 1
f(10) = 1
Now calculate f(n) for n which are powers of 10:
f(10) = 1
f(100) = 20
f(1000) = 300
...
f(10^(n+1)) = 10 * f(10^n) + 10^n (or something like that)
I hope I did it right. The idea is, for e.g. n = 1000, consider e.g. all 3-digit numbers with first digit 6. There are f(100) 3's in this list. The same for all other first digits, except for 3, where there are 100 more 3's.
Now consider an arbitrary n. Check its first digit; call it d. The list of all numbers smaller than n contains all possible numbers whose first digit is smaller than d, and some numbers whose first digit is exactly d. Now consider all these lists separately, and count 3's in them.
General advice: keep your "slow" code accessible at all times while you are writing your "fast" code. This way, it will be easy to test your code, and find unhandled cases, off-by-one bugs and such.

Reduce subsequence problem complexity from exponential to polynomial?

I am working on the following problem:
Given a set of non-negative distinct integers, and a value m, determine if there is a subset of the given set with sum divisible by m.
Input: The first line of input contains an integer T denoting the number of test cases. Then T test cases follow. The first line of each test case contains an integer N and M where N denotes the size of the array and M is the number for which we have to check the divisibility. The second line of each test case contains N space separated integers denoting elements of the array A[ ].
Output: If there is a subset which is divisible by M print '1' else print '0'.
I have tried a recursive solution:
#include <iostream>
#include<unordered_map>
using namespace std;
bool find_it(int a[],int &m,int n,int sum) {
if ((sum%m)==0 && sum>0)
return true;
if (n==0)
return false;
return find_it(a,m,n-1,sum) || find_it(a,m,n-1,sum-a[n-1]);
}
int main() {
int tc;
cin >> tc;
while (tc--) {
int n,m;
cin >> n >> m;
int a[n];
int sum = 0;
for (int i=0;i<n;i++) {
cin >> a[i];
sum += a[i];
}
bool answer = find_it(a,m,n,sum);
cout << answer << "\n";
}
return 0;
}
Which works fine and get accepted, but then I tried top-down approach, and am getting TLE ("Time Limit Exceeded"). What am I doing wrong in this memoization?
#include <iostream>
#include<unordered_map>
using namespace std;
bool find_it(
int a[], int &m, int n, int sum,
unordered_map<int,unordered_map<int,bool>> &value,
unordered_map<int,unordered_map<int,bool>> &visited){
if ((sum%m)==0 && sum>0)
return true;
if(n==0)
return false;
if(visited[n][sum]==true)
return value[n][sum];
bool first = false,second = false;
first = find_it(a,m,n-1,su1m,value,visited);
if(sum<a[n-1])
{
second=false;
}
else
second = find_it(a,m,n-1,sum-a[n-1],value,visited);
visited[n][sum] = true;
value[n][sum] = first || second;
return value[n][sum];
}
int main() {
int tc;
cin >> tc;
while (tc--) {
int n,m;
cin >> n >> m;
int a[n];
int sum = 0;
for (int i=0;i<n;i++) {
cin >> a[i];
sum+=a[i];
}
unordered_map<int,unordered_map<int,bool>> value;
unordered_map<int,unordered_map<int,bool>> visited;
cout << find_it(a,m,n,sum,value,visited) << "\n";
}
return 0;
}
Well, at first, you can reduce the problem to a modulo m problem, as properties of integers don't change when switching to modulo m field. It's easy to demonstrate that being divisible by m is the same as being identical to 0 mod m.
I would first convert all those numbers to their counterparts modulo m and eliminate repetitions by considering a_i, 2*a_i, 3*a_i,... until rep_a_i * a_i, all of them mod m. Finally you get a reduced set that has at most m elements. Then eliminate all the zeros there, as they don't contribute to the sum. This is important for two reasons:
It converts your problem from a Knapsack problem (NP-complete) on which complexity is O(a^n) into a O(K) problem, as its complexity doesn't depend on the number of elements of the set, but the number m.
You can still have a large set of numbers to compute. You can consider the reduced set a Knapsack problem and try to check (and further reduce it) for an easy-knapsack problem (the one in which the different values a_i follow a geometric sequence with K > 2)
The rest of the problem is a Knapsack problem (which is NP-complete) or one of it's P variants.
In case you don't get so far (cannot reduce it to an easy-knapsack problem) then you have to reduce the number of a_i's so the exponential time gets a minimum exponent :)
edit
(#mss asks for elaboration in a comment) Assume you have m = 8 and the list is 1 2 4 6 12 14 22. After reduction mod m the list remains as: 1 2 4 6 4 6 6 in which 6 is repeated three times. we must consider the three possible repetitions of 6, as they can contribute to get a sum, but not more (for the moment), let's consider 6*1 = 6, 6*2 = 12 and 6*3 = 18, the first is the original 6, the second makes a third repetition of 4 (so we'll need to consider 3 4s in the list), and the third converts into a 2. So now, we have 1 2 4 6 4 4 2 in the list. We make the same for the 4 repetitions (two 4 run into 8 which is 0mod m and don't contribute to sums, but we have to keep one such 0 because this means you got by repeated numbers the target m) getting into 1 2 4 6 0 4 2 => 1 2 4 6 0 0 2 =(reorder)=> 0 1 2 2 4 6 => 0 1 2 4 6. This should be the final list to consider. As it has a 0, you know a priori that there's one such sum (in this case you got is as including the two 4, for the original list's 4 and 12 numbers.
There is no need for value. Once you find a valid combination, i.e. if find_it ever returns true, you can just immediately return true in all recursive calls.
Some additional remarks:
You should use consistent indentation.
Variable sized arrays as in int a[n] are not standard C++ and will not work on all compilers.
There is no reason to pass m as int& instead of int.
A map taking boolean values is the same as a set where the element is assumed to map to true if it is in the set and false if it is not. Consider using unordered_set instead of unordered_map.
Composing two unordered_maps like this is expensive. You can just as easily put both keys into a std::pair and use that as key. This would avoid the overhead of maintaining the map.
bits/stdc++.h is also non-standard and you should specify the correct header files instead, e.g. #include <unordered_map> and #include <iostream>.
You should put spaces between the variable type and its name, even if the > from the template parameter allows it to parse correctly without. It makes code hard to read.

How do I solve this making it more efficient?

So, I am trying to solve the following question: https://www.codechef.com/TSTAM15/problems/ACM14AM3
The Mars Orbiter Mission probe lifted-off from the First Launch Pad at Satish Dhawan Space Centre (Sriharikota Range SHAR), Andhra
Pradesh, using a Polar Satellite Launch Vehicle (PSLV) rocket C25 at
09:08 UTC (14:38 IST) on 5 November 2013.
The secret behind this successful launch was the launch pad that ISRO
used. An important part of the launch pad is the launch tower. It is
the long vertical structure which supports the rocket.
ISRO now wants to build a better launch pad for their next mission.
For this, ISRO has acquired a long steel bar, and the launch tower can
be made by cutting a segment from the bar. As part of saving the cost,
the bar they have acquired is not homogeneous.
The bar is made up of several blocks, where the ith block has
durability S[i], which is a number between 0 and 9. A segment is
defined as any contiguous group of one or more blocks.
If they cut out a segment of the bar from ith block to jth block
(i<=j), then the durability of the resultant segment is given by (S[i]*10(j-i) + S[i+1]*10(j-i-1) + S[i+2]*10(j-i-2) + … + S[j] * 10(0)) % M. In other words, if W(i,j) is the base-10 number formed by
concatenating the digits S[i], S[i+1], S[i+2], …, S[j], then
the durability of the segment (i,j) is W(i,j) % M.
For technical reasons that ISRO will not disclose, the durability of
the segment used for building the launch tower should be exactly L.
Given S and M, find the number of ways ISRO can cut out a segment from
the steel bar whose durability is L. Input
The first line contains a string S. The ith character of this string
represents the durability of ith segment. The next line contains a
single integer Q, denoting the number of queries. Each of the next Q
lines contain two space separated integers, denoting M and L. Output
For each query, output the number of ways of cutting the bar on a
separate line. Constraints
1 ≤ |S| ≤ 2 * 10^4
Q ≤ 5
0 < M < 500
0 ≤ L < M
Example
Input:
23128765
3
7 2
9 3
15 5
Output:
9
4
5
Explanation
For M=9, L=3, the substrings whose remainder is 3 when divided by
9 are: 3, 31287, 12 and 876.
Now, what I did was, I initially generate all possible substrings of numbers of the given length, and tried to divide it by the given number to check if it is divisible and added it to the answer. Therefore, my code for the same was,
string s;
cin>>s;
int m,l,ans=0;
for ( i = 0; i < s.length(); i++ )
{
for ( j = i+1; j < s.length(); j++ )
{
string p = s.substr(i,j);
long long num = stoi(p);
if (num%m == l)
ans++;
}
}
cout<<ans<<"\n";
return 0;
But obviously since the input length is upto 10^4, this doesn't work in required time. How can I make it more optimal?
A little advice I can give you is to initialize a variable to s.length() to avoid calling the function each time for each for block.
Ok, here goes, with a working program at the bottom
Major optimization #1
Do not (ever) work with strings when it comes to integer arithmetic. You're converting string => integer over and over and over again (this is an O(n^2) problem), which is painstakingly slow. Besides, it also misses the point.
Solution: first convert your array-of-characters (string) to array-of-numbers. Integer arithmetic is fast.
Major optimization #2
Use a smart conversion from "substring" to number. After transforming the characters to actual integers, they become the factors in the the polynomial a_n * 10^n. To convert a substring of n segments into a number, it is enough to compute sum(a_i * 10^i) for 0 <= i < n.
And nicely enough, if the coefficients a_i are arranged the way they are in the problem's statement, you can use Horner's method (https://en.wikipedia.org/wiki/Horner%27s_method) to very quickly evaluate the numerical value of the substring.
In short: keep a running value of the current substring and growing it by one element is just * 10 + new element
Example: string "128472373".
First substring = "1", value = 1.
For the second substring we need to
add the digit "2" as follows: value = value * 10 + "2", thus: value = 1 * 10 + 2 = 12.
For 3rd substring need to add digit "8": value = value * 10 + "8", thus: value = 12 * 10 + 8 = 128.
Etcetera.
I had some issues with formatting the C++ code inline so I stuck it in IDEone: https://ideone.com/TbJiqK
The gist of the program:
In main loop, loop over all possible start points:
// For all startpoints in the segments array ...
for(int* f=segments; f<segments+n_segments; f++)
// add up the substrings that fullfill the question
n += count_segments(f, segments+n_segments, m, l);
// Output the answer for this question
cout << n << endl;
Implementation of the count_segments() function:
// Find all substrings that % m == l
// Use Horner's algorithm to quickly evaluate sum(a_n*10^n) where
// a_n are the segments' durabilities
int count_segments(int* first, int* last, int m, int l) {
int n = 0, number = 0;
while( first<last ) {
number = number * 10 + *first; // This is Horner's method
if( (number % m)==l ) {
n++;
// If you don't believe - enable this line of output and
// see the numbers matching the combinations of the
//cout << "[" << m << ", " << l << "]: " << number << endl;
}
first++;
}
return n;
}

Optimizing algorithm to find number of six digit numbers satisfying certain property

Problem: "An algorithm to find the number of six digit numbers where the sum of the first three digits is equal to the sum of the last three digits."
I came across this problem in an interview and want to know the best solution. This is what I have till now.
Approach 1: The Brute force solution is, of course, to check for each number (between 100,000 and 999,999) whether the sum of its first three and last three digits are equal. If yes, then increment certain counter which keeps count of all such numbers.
But this checks for all 900,000 numbers and so is inefficient.
Approach 2: Since we are asked "how many" such numbers and not "which numbers", we could do better. Divide the number into two parts: First three digits (these go from 100 to 999) and Last three digits (these go from 000 to 999). Thus, the sum of three digits in either part of a candidate number can range from 1 to 27.
* Maintain a std::map<int, int> for each part where key is the sum and value is number of numbers (3 digit) having that sum in the corresponding part.
* Now, for each number in the first part find out its sum and update the corresponding map.
* Similarly, we can get updated map for the second part.
* Now by multiplying the corresponding pairs (e.g. value in map 1 of key 4 and value in map 2 of key 4) and adding them up we get the answer.
In this approach, we end up checking 1K numbers.
My question is how could we further optimize? Is there a better solution?
For 0 <= s <= 18, there are exactly 10 - |s - 9| ways to obtain s as the sum of two digits.
So, for the first part
int first[28] = {0};
for(int s = 0; s <= 18; ++s) {
int c = 10 - (s < 9 ? (9 - s) : (s - 9));
for(int d = 1; d <= 9; ++d) {
first[s+d] += c;
}
}
That's 19*9 = 171 iterations, for the second half, do it similarly, with the inner loop starting at 0 instead of 1, that's 19*10 = 190 iterations. Then sum first[i]*second[i] for 1 <= i <= 27.
Generate all three-digit numbers; partition them into sets based on their sum of digits. (Actually, all you need to do is keep a vector that counts the size of the sets). For each set, the number of six-digit numbers that can be generated is the size of the set squared. Sum up the squares of the set sizes to get your answer.
int sumCounts[28]; // sums can go from 0 through 27
for (int i = 0; i < 1000; ++i) {
sumCounts[sumOfDigits(i)]++;
}
int total = 0;
for (int i = 0; i < 28; ++i) {
count = sumCounts[i];
total += count * count;
}
EDIT Variation to eliminate counting leading zeroes:
int sumCounts[28];
int sumCounts2[28];
for (int i = 0; i < 100; ++i) {
int s = sumOfDigits(i);
sumCounts[s]++;
sumCounts2[s]++;
}
for (int i = 100; i < 1000; ++i) {
sumCounts[sumOfDigits(i)]++;
}
int total = 0;
for (int i = 0; i < 28; ++i) {
count = sumCounts[i];
total += (count - sumCounts2[i]) * count;
}
Python Implementation
def equal_digit_sums():
dists = {}
for i in range(1000):
digits = [int(d) for d in str(i)]
dsum = sum(digits)
if dsum not in dists:
dists[dsum] = [0,0]
dists[dsum][0 if len(digits) == 3 else 1] += 1
def prod(dsum):
t = dists[dsum]
return (t[0]+t[1])*t[0]
return sum(prod(dsum) for dsum in dists)
print(equal_digit_sums())
Result: 50412
One idea: For each number from 0 to 27, count the number of three-digit numbers that have that digit sum. This should be doable efficiently with a DP-style approach.
Now you just sum the squares of the results, since for each answer, you can make a six-digit number with one of those on each side.
Assuming leading 0's aren't allowed, you want to calculate how many different ways are there to sum to n with 3 digits. To calculate that you can have a for loop inside a for loop. So:
firstHalf = 0
for i in xrange(max(1,n/3),min(9,n+1)): #first digit
for j in xrange((n-i)/2,min(9,n-i+1)): #second digit
firstHalf +=1 #Will only be one possible third digit
secondHalf = firstHalf + max(0,10-|n-9|)
If you are trying to sum to a number, then the last number is always uniquely determined. Thus in the case where the first number is 0 we are just calculating how many different values are possible for the second number. This will be n+1 if n is less than 10. If n is greater, up until 18 it will be 19-n. Over 18 there are no ways to form the sum.
If you loop over all n, 1 through 27, you will have your total sum.

Algorithm to determine coin combinations

I was recently faced with a prompt for a programming algorithm that I had no idea what to do for. I've never really written an algorithm before, so I'm kind of a newb at this.
The problem said to write a program to determine all of the possible coin combinations for a cashier to give back as change based on coin values and number of coins. For example, there could be a currency with 4 coins: a 2 cent, 6 cent, 10 cent and 15 cent coins. How many combinations of this that equal 50 cents are there?
The language I'm using is C++, although that doesn't really matter too much.
edit: This is a more specific programming question, but how would I analyze a string in C++ to get the coin values? They were given in a text document like
4 2 6 10 15 50
(where the numbers in this case correspond to the example I gave)
This problem is well known as coin change problem. Please check this and this for details. Also if you Google "coin change" or "dynamic programming coin change" then you will get many other useful resources.
Here's a recursive solution in Java:
// Usage: int[] denoms = new int[] { 1, 2, 5, 10, 20, 50, 100, 200 };
// System.out.println(ways(denoms, denoms.length, 200));
public static int ways(int denoms[], int index, int capacity) {
if (capacity == 0) return 1;
if (capacity < 0 || index <= 0 ) return 0;
int withoutItem = ways(denoms, index - 1, capacity);
int withItem = ways(denoms, index, capacity - denoms[index - 1]);
return withoutItem + withItem;
}
This seems somewhat like a Partition, except that you don't use all integers in 1:50. It also seems similar to bin packing problem with slight differences:
Wikipedia: Partition (Number Theory)
Wikipedia: Bin packing problem
Wolfram Mathworld: Partiton
Actually, after thinking about it, it's an ILP, and thus NP-hard.
I'd suggest some dynamic programming appyroach. Basically, you'd define a value "remainder" and set it to whatever your goal was (say, 50). Then, at every step, you'd do the following:
Figure out what the largest coin that can fit within the remainder
Consider what would happen if you (A) included that coin or (B) did not include that coin.
For each scenario, recurse.
So if remainder was 50 and the largest coins were worth 25 and 10, you'd branch into two scenarios:
1. Remainder = 25, Coinset = 1x25
2. Remainder = 50, Coinset = 0x25
The next step (for each branch) might look like:
1-1. Remainder = 0, Coinset = 2x25 <-- Note: Remainder=0 => Logged
1-2. Remainder = 25, Coinset = 1x25
2-1. Remainder = 40, Coinset = 0x25, 1x10
2-2. Remainder = 50, Coinset = 0x25, 0x10
Each branch would split into two branches unless:
the remainder was 0 (in which case you would log it)
the remainder was less than the smallest coin (in which case you would discard it)
there were no more coins left (in which case you would discard it since remainder != 0)
If you have 15, 10, 6 and 2 cents coins and you need to find how many distinct ways are there to arrive to 50 you can
count how many distinct ways you have to reach 50 using only 10, 6 and 2
count how many distinct ways you have to reach 50-15 using only 10, 6 and 2
count how many distinct ways you have to reach 50-15*2 using only 10, 6 and 2
count how many distinct ways you have to reach 50-15*3 using only 10, 6 and 2
Sum up all these results that are of course distinct (in the first I used no 15c coins, in the second I used one, in the third two and in the fourth three).
So you basically can split the problem in smaller problems (possibly smaller amount and fewer coins). When you have just one coin type the answer is of course trivial (either you cannot reach the prescribed amount exactly or you can in the only possible way).
Moreover you can also avoid repeating the same computation by using memoization, for example the number of ways of reach 20 using only [6, 2] doesn't depend if the already paid 30 have been reached using 15+15 or 10+10+10, so the result of the smaller problem (20, [6, 2]) can
be stored and reused.
In Python the implementation of this idea is the following
cache = {}
def howmany(amount, coins):
prob = tuple([amount] + coins) # Problem signature
if prob in cache:
return cache[prob] # We computed this before
if amount == 0:
return 1 # It's always possible to give an exact change of 0 cents
if len(coins) == 1:
if amount % coins[0] == 0:
return 1 # We can match prescribed amount with this coin
else:
return 0 # It's impossible
total = 0
n = 0
while n * coins[0] <= amount:
total += howmany(amount - n * coins[0], coins[1:])
n += 1
cache[prob] = total # Store in cache to avoid repeating this computation
return total
print howmany(50, [15, 10, 6, 2])
As for the second part of your question, suppose you have that string in the file coins.txt:
#include <fstream>
#include <vector>
#include <algorithm>
#include <iterator>
int main() {
std::ifstream coins_file("coins.txt");
std::vector<int> coins;
std::copy(std::istream_iterator<int>(coins_file),
std::istream_iterator<int>(),
std::back_inserter(coins));
}
Now the vector coins will contain the possible coin values.
For such a small number of coins you can write a simple brute force solution.
Something like this:
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
vector<int> v;
int solve(int total, int * coins, int lastI)
{
if (total == 50)
{
for (int i = 0; i < v.size(); i++)
{
cout << v.at(i) << ' ';
}
cout << "\n";
return 1;
}
if (total > 50) return 0;
int sum = 0;
for (int i = lastI; i < 6; i++)
{
v.push_back(coins[i]);
sum += solve(total + coins[i], coins, i);
v.pop_back();
}
return sum;
}
int main()
{
int coins[6] = {2, 4, 6, 10, 15, 50};
cout << solve(0, coins, 0) << endl;
}
A very dirty brute force solution that prints all possible combinations.
This is a very famous problem, so try reading about better solutions others have provided.
One rather dumb approach is the following. You build a mapping "coin with value X is used Y times" and then enumerate all possible combinations and only select those which total the desired sum. Obviously for each value X you have to check Y ranging from 0 up to the desired sum. This will be rather slow, but will solve your task.
It's very similar to the knapsack problem
You basically have to solve the following equation: 50 = a*4 + b*6 + c*10 + d*15, where the unknowns are a,b,c,d. You can compute for instance d = (50 - a*4 - b*6 - c*10)/15 and so on for each variable. Then, you start giving d all the possible values (you should start with the one that has the least possible values, here d): 0,1,2,3,4 and than start giving c all the possible values depending on the current value of d and so on.
Sort the List backwards: [15 10 6 4 2]
Now a solution for 50 ct can contain 15 ct or not.
So the number of solutions is the number of solutions for 50 ct using [10 6 4 2] (no longer considering 15 ct coins) plus the number of solutions for 35 ct (=50ct - 15ct) using [15 10 6 4 2]. Repeat the process for both sub-problems.
An algorithm is a procedure for solving a problem, it doesn't have to be in any particular language.
First work out the inputs:
typedef int CoinValue;
set<CoinValue> coinTypes;
int value;
and the outputs:
set< map<CoinValue, int> > results;
Solve for the simplest case you can think of first:
coinTypes = { 1 }; // only one type of coin worth 1 cent
value = 51;
the result should be:
results = { [1 : 51] }; // only one solution, 51 - 1 cent coins
How would you solve the above?
How about this:
coinTypes = { 2 };
value = 51;
results = { }; // there is no solution
what about this?
coinTypes = { 1, 2 };
value = { 4 };
results = { [2: 2], [2: 1, 1: 2], [1: 4] }; // the order I put the solutions in is a hint to how to do the algorithm.
Recursive solution based on algorithmist.com resource in Scala:
def countChange(money: Int, coins: List[Int]): Int = {
if (money < 0 || coins.isEmpty) 0
else if (money == 0) 1
else countChange(money, coins.tail) + countChange(money - coins.head, coins)
}
Another Python version:
def change(coins, money):
return (
change(coins[:-1], money) +
change(coins, money - coins[-1])
if money > 0 and coins
else money == 0
)