knapsack program, slow running time - knapsack-problem

i modified some code from internet to meet my requirements, but unfortunately this program seems to run a bit slow. im not sure whether its my computer or the program itself is slow.
int max(int a, int b)
{
return (a > b) ? a : b;
}
int knapSack(int W, int wt[], int val[], int n)
{
if (n == 0 || W == 0)
return 0;
if (wt[n - 1] > W)
return knapSack(W, wt, val, n - 1);
else
return max(val[n - 1] + knapSack(W - wt[n - 1], wt, val, n - 1),
knapSack(W, wt, val, n - 1));
}
int main()
{
char exeAgain='n';
do
{
cout << "Enter the number of items in a Knapsack : ";
int n, W;
cin >> n;
int val[n], wt[n];
for (int i = 0; i < n; i++)
{
val[i]=(rand()%100)+1;
wt[i]=(rand()%100)+1;
cout << "Item Number "<< i+1 << " value "<<val[i]<<" weight " << wt[i] << endl;
}
cout << "Enter the capacity of knapsack : ";
cin >> W;
cout << "The total profit is " << knapSack(W, wt, val, n)<< endl;
cout<<"Would you like to execute this program again? [Y/N] : ";
cin>>exeAgain;
}
while(exeAgain == 'y' || exeAgain == 'Y');
return 0;
}
is it normal for this program to run slow? (the input is 200 & 1500)
smaller input is fast, but when i use big number, the program slows down.

The Knapsack Problem is famously NP-Complete, which means in simple terms that as the size of your input increases, the time to solve it increases astronomically. So yes, it is completely normal for your program to run slowly at larger inputs.
If you research around, you'll find a lot of work done on the problem, including various strategies to run faster than a first attempt, or heuristics for a good-but-not-perfect solution, which will run faster than something looking for a perfect solution.

Related

Finding all possible unique combinations of numbers to reach a given sum

We have a list of numbers, let's say: [ 2, 3, 5 ]
and we have a targetSum, let's say: 8
Our goal, then, is to pick numbers from the list in such a way that the sum of the numbers would lead to targetSum
I'll explain my code first, I wrote a simple c++ code for the same, it uses recursion and backtracking ( without memoization ). It does the following:
We subdivide our original problem by reducing the targetSum by each number at each recursion
Visualizing this in the form of a tree is useful, we also keep track of what number's we have substracted so far, and we keep pushing and popping accordingly
Once we hit the base case of 0, meaning it's possible to create the sum, we make a note of the current numbers we have recursed
This process goes on until we have gone through all of the possibilities
Code:
#include<iostream>
#include<vector>
using namespace std;
bool bestSum( int targetSum, vector<int> &holder, vector<vector<int>> &combinations,
vector<int> &path )
{
if( targetSum == 0 )
{
combinations.push_back( path );
return true;
}
if( targetSum < 0 )
{
return false;
}
bool possible = false;
for( int i = 0; i < holder.size(); i++ )
{
int remainder = targetSum - holder[i];
path.push_back(holder[i]);
cout << "After pushing:";
for( int j = 0; j < path.size(); j++ )
{
cout << path[j] << " ";
}
cout << endl;
bool verdict = bestSum( remainder, holder, combinations, path );
if( verdict == true )
{
possible = true;
}
path.pop_back();
cout << "After popping:";
for( int j = 0; j < path.size(); j++ )
{
cout << path[j] << " ";
}
cout << endl;
}
return possible;
}
int main()
{
vector<int> holder = { 2, 3, 5 };
int targetSum = 8;
vector<vector<int>> combinations;
vector<int> path;
bool verdict = bestSum( targetSum, holder, combinations, path );
for( int i = 0; i < combinations.size(); i++ )
{
for( int j = 0; j < combinations[i].size();j++)
{
cout << combinations[i][j] << " ";
}
cout << endl;
}
return 0;
}
(ignoring the printing statements) Talking about time complexity, it should be: exponential, without memoization
And at most small degree polynomial, with memoization
Combing back to the original problem, currently my code produces all of the possible combinations, for example, with the numbers list and targetSum presented at the start of this article, we would get: 2,3,3 and 3,3,2 as two different combinations. But we know that they aren't unique
My question is, is it possible to find all unique combination of numbers whilst keeping the logic of my code consistent?

Why Dijkstra + priority_queue performs worst compared to Dijkstra + queue?

I solved this problem using Dijkstra with a normal queue instead of a priority queue because interestingly I was getting a TLE when I used the priority queue. My understanding is that Dijkstra with priority queue should perform better. I tried analyzing as to why is this the case?
I came up with a thought that it is so because the question uses test cases with graphs having edges from 1 to n in increasing order of their weights so it is a waste of time to maintain a priority queue and hence the TLE. I would like to know if this is exactly the case an I'm not missing anything. Here is my solution to the problem:
#include <bits/stdc++.h>
using namespace std;
#define INF 100000000
queue<pair<long, int>> pi;
vector<long> dist;
map<int, vector<pair<int, long>>> mp;
int main() {
int m = 0, n = 0;
cin >> m >> n;
while (n--) {
int l = 0, k = 0, j = 0;
cin >> l >> k >> j;
mp[l].push_back(make_pair(k, j));
mp[k].push_back(make_pair(l, j)); // for undirected edge
}
dist.assign(m + 1, INF);
dist[1] = 0;
pi.push(make_pair(0, 1));
while (!pi.empty()) {
pair<int, int> p = pi.front(); pi.pop();
int u = p.second; long w = p.first;
if (w > dist[u]) continue;
for (auto e : mp[u]) {
if (max(dist[u], e.second) < dist[e.first]) {
dist[e.first] = max(dist[u], e.second);
if (e.first != m)
pi.push(make_pair(dist[e.first], e.first));
}
}
}
if (dist[m] == INF) cout << "NO PATH EXISTS" << endl;
else cout << dist[m] << endl;
return 0;
}
Did you change a default comparator of priority queue?
If not, then, of course, it would be slower
How can I create Min stl priority_queue?

Minimum number of operations to get from source to target.

I came across this question during an interview -
Convert a number source to target in the minimum number of operations.
Allowed Operations
Multiplied by 2.
Addition by 1.
subtraction by 1.
0 < source, target <= 1000.
I tried going the naive recursive route(O(3^n)) ie. subtract 1, add 1 and multiply by 2 at each level to try and find a solution that I could extend to Dynamic Programming but couldnt because of an infinite loop.
//Naive approach Via Recursion
int minMoves(int source, int target){
if(source <1 || source > target){
return -1;
}
int moves =0;
// Potential infinite loop - consider 3,6-> 2,6- >1,6->(0,6)x (2,6)->1,6->(0,6)x (1,6)->(0,6)x (2,6)->1,6..
int movesLeft = minMoves(source -1, target) ==-1? Integer.MAX_VALUE:minMoves(source -1, target);
int movesRight = minMoves(source +1, target) ==-1? Integer.MAX_VALUE:minMoves(source +1, target);
int moves2X = minMoves(2*source, target) ==-1? Integer.MAX_VALUE:minMoves(2*source, target);
moves = 1+ Math.min(Math.min(movesRight,movesLeft), moves2X);
return moves;
}
Any ideas on how I can tweak my solution? Or possibly a better way to solve it?
If you think about your solution like a graph traversal, where each node is an intermediate value you can produce, your recursive solution is like a depth first search (DFS). You'll have to fully expand until you've tried all solutions from that "branch" of the search space before you can proceed anywhere else. If you have an infinite loop, this means it will never terminate even if a shorter path exists, and even if you don't have an infinite loop, you still have to search the rest of the solution space to make sure its optimal.
Instead, consider an approach similar to breadth first search (BFS). You expand outward uniformly, and will never search a path longer than the optimal solution. Just use FIFO queue to schedule which node to access next. This is the approach I've taken with my solver.
from queue import Queue
def solve(source, target):
queue = Queue()
path = [source]
queue.put(path)
while source != target:
queue.put(path + [source * 2])
queue.put(path + [source + 1])
queue.put(path + [source - 1])
path = queue.get()
source = path[-1]
return path
if __name__ == "__main__":
print(solve(4,79))
One way in which you can speed up(and possibly fix) this code, while maintaining the recursive implementation, is to use memoization.
The issue here is that you are recalculating the same value many times. Instead you can use a map to store the results that you already calculated, and reuse them when you need it again.
This problem can be solved constructively. First, the easy cases. If s=t, the answer is 0. If s > t, the answer is s-t because subtraction by 1 is the only operation that lowers s, and the other two can only increase the number of subtractions required.
Now let's assume s < t. Since s>0 is given, doubling will always be the fastest way to increase (if s is 1, it's tied with incrementing). So if the challenge was to make s >= t, the answer would always be the number of doublings required to do that. This procedure may overshoot t, but the first doubling greater than t and the last doubling not greater than t must be within a factor of 2 of t.
Let's look at the effect of when we do an addition or subtraction. First, look only at addition:
(((s*2) * 2) * 2) + 1 = 8s + 1
vs:
((((s+1)*2) * 2) * 2) = 8s + 8
Putting an addition before n doublings makes the final result 2^n bigger. So consider if s is 3 and t is 8. The last double not bigger than 8 is 6. This is 2 off, so if we put an addition 1 double before the last double, we get what we want: (3+1) * 2. Alternatively we could try overshooting to the first double greater than 8, which is 12. This is 4 off, so we need to put a subtraction two doublings before the last : (3-1)*2*2 = 8
In general if we are x below the target, we need to put a +1 at n doublings before the last if the binary representation of x has a 1 at the nth place.
Similarly, if we are x above the target, we do likewise with -1's.
This procedure won't help for the 1's in x's binary representation that are at a position more than the number of doublings there are. For example, if s = 100, t=207, there is only 1 doubling to do, but x is 7, which is 111. We can knock out the middle one by doing an addition first, the rest we have to do one by one (s+1)*2 + 1 + 1 + 1 + 1 + 1.
Here is an implementation that has a debug flag that also outputs the list of operations when the flag is defined. The run time is O(log(t)):
#include <iostream>
#include <string>
#include <sstream>
#define DEBUG_INFO
int MinMoves(int s, int t)
{
int ans = 0;
if (t <= s)
{
return s - t; //Only subtraction will help
}
int firstDoubleGreater = s;
int lastDoubleNotGreater = s;
int nDouble = 0;
while(firstDoubleGreater <= t)
{
nDouble++;
lastDoubleNotGreater = firstDoubleGreater;
firstDoubleGreater *= 2;
}
int d1 = t - lastDoubleNotGreater;
int d2 = firstDoubleGreater - t;
if (d1 == 0)
return nDouble -1;
int strat1 = nDouble -1; //Double and increment
int strat2 = nDouble; //Double and decrement
#ifdef DEBUG_INFO
std::cout << "nDouble: " << nDouble << "\n";
std::stringstream s1Ops;
std::stringstream s2Ops;
int s1Tmp = s;
int s2Tmp = s;
#endif
int mask = 1<<strat1;
for(int pos = 0; pos < nDouble-1; pos++)
{
#ifdef DEBUG_INFO
if (d1 & mask)
{
s1Ops << s1Tmp << "+1=" << s1Tmp+1 << "\n" << s1Tmp+1 << "*2= " << (s1Tmp+1)*2 << "\n";
s1Tmp = (s1Tmp + 1) * 2;
}
else
{
s1Ops << s1Tmp << "*2= " << s1Tmp*2 << "\n";
s1Tmp = s1Tmp*2;
}
#endif
if(d1 & mask)
strat1++;
d1 = d1 & ~mask;
mask = mask >> 1;
}
strat1 += d1;
#ifdef DEBUG_INFO
if (d1 != 0)
s1Ops << s1Tmp << " +1 " << d1 << " times = " << s1Tmp + d1 << "\n";
#endif
mask = 1<<strat2;
for(int pos = 0; pos < nDouble; pos++)
{
#ifdef DEBUG_INFO
if (d2 & mask)
{
s2Ops << s2Tmp << "-1=" << s2Tmp-1 << "\n" << s2Tmp-1 << "*2= " << (s2Tmp-1)*2 << "\n";
s2Tmp = (s2Tmp-1)*2;
}
else
{
s2Ops << s2Tmp << "*2= " << s2Tmp*2 << "\n";
s2Tmp = s2Tmp*2;
}
#endif
if(d2 & mask)
strat2++;
d2 = d2 & ~mask;
mask = mask >> 1;
}
strat2 += d2;
#ifdef DEBUG_INFO
if (d2 != 0)
s2Ops << s2Tmp << " -1 " << d2 << " times = " << s2Tmp - d2 << "\n";
std::cout << "Strat1: " << strat1 << "\n";
std::cout << s1Ops.str() << "\n";
std::cout << "\n\nStrat2: " << strat2 << "\n";
std::cout << s2Ops.str() << "\n";
#endif
if (strat1 < strat2)
{
return strat1;
}
else
{
std::cout << "Strat2\n";
return strat2;
}
}
int main()
{
int s = 25;
int t = 193;
std::cout << "s = " << s << " t = " << t << "\n";
std::cout << MinMoves(s, t) << std::endl;
}
Short BFS algorithm. It finds the shortest path in graph where every vertex x is connected to x + 1, x - 1 and x * 2; O(n)
#include <bits/stdc++.h>
using namespace std;
const int _MAX_DIS = 2020;
const int _MIN_DIS = 0;
int minMoves(int begin, int end){
queue<int> Q;
int dis[_MAX_DIS];
fill(dis, dis + _MAX_DIS, -1);
dis[begin] = 0;
Q.push(begin);
while(!Q.empty()){
int v = Q.front(); Q.pop();
int tab[] = {v + 1, v - 1, v * 2};
for(int i = 0; i < 3; i++){
int w = tab[i];
if(_MIN_DIS <= w && w <= _MAX_DIS && dis[w] == -1){
Q.push(w);
dis[w] = dis[v] + 1;
}
}
}
return dis[end];
}
int main(){
ios_base::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cout << minMoves(1, 1000);
return 0;
}

Dynamic Programming - Number of combinations to reach a given target

This is a basic dynamic programming problem - Number of score combinations. I am aware of the bottom up approach for this problem which works well.
However, I am unable to work towards finding a top-down solution approach for the problem. Caching the recursive part gives us more than necessary combinations(where ordering/sequence of scores is also a factor, so, to avoid it we need to provide a constraint to make the sequence increase monotonically. Here is recursive approach for the same. Dynamic Programming - Number of distinct combinations to reach a given score
Here is my current code:
#include <iostream>
#include <vector>
using namespace std;
int helper(int target, vector<int>& coins, vector<int>& cache, int min) {
if(target < 0) return 0;
if(target == 0) return 1;
if(cache[target] != 0) return cache[target];
for(auto& c : coins) {
if(target >= c && min <= c) {
//cout << min << " " << c << " " << target << endl;
cache[target] += helper(target-c, coins, cache, c) ;
//cout << cache[target] << endl;
}
}
return cache[target];
}
int main() {
vector<int> coins{2, 3};
int target = 7;
vector<int> cache(target+1, 0);
cache[0] = 1;
cache [7] = helper(target, coins, cache, 1);
for (auto& x : cache) cout << x << endl;
return 0;
}
Here is run-able ideone link.

Having trouble with a for loop for school

I'm currently taking a C/C++ programming class at my school. I am tasked with writing a piece of code that will ask the user how many numbers they would like averaged, then averages them. The program has to contain a for loop. The problem that I am having is that after the user has entered the "n" variable, if they type a character such as "a", the program will immediately spit out an answer as my average. I would like to find a way to prevent the user from entering characters so that my for loop can finish running and average the numbers properly. Here is my code:
{
int n, i = 1, x = 1;
double sum = 0, average, value;
cout << "\nHow many numbers do you want to average?: ";
cin >> n;
while (n < 1)
{
cout << "\nYou have entered an invalid number.\n";
cout << "\nHow many numbers do you want to average?: ";
cin.clear();
while (cin.get() != '\n');
cin >> n;
}
for (n; i <= n; i++)
{
cout << "\nEnter value: ";
cin >> value;
sum = sum + value;
}
average = sum / n;
cout << "\nThe average is: " << average << endl;
system("pause");
return 0;
}

Resources