C++ Part of brute-force knapsack - c++

reader,
Well, I think I just got brainfucked a bit.
I'm implementing knapsack, and I thought about I implemented brute-force algorithm like 1 or 2 times ever. So I decided to make another one.
And here's what I chocked in.
Let us decide W is maximum weight, and w(min) is minimal-weighted element we can put in knapsack like k=W/w(min) times. I'm explaining this because you, reader, are better know why I need to ask my question.
Now. If we imagine that we have like 3 types of things we can put in knapsack, and our knapsack can store like 15 units of mass, let's count each unit weight as its number respectively. so we can put like 15 things of 1st type, or 7 things of 2nd type and 1 thing of 1st type. but, combinations like 22222221[7ed] and 12222222[7ed] will mean the same for us. and counting them is a waste of any type of resources we pay for decision. (it's a joke, 'cause bf is a waste if we have a cheaper algorithm, but I'm very interested)
As I guess the type of selections we need to go through all possible combinations is called "Combinations with repetitions". The number of C'(n,k) counts as (n+k-1)!/(n-1)!k!.
(while I typing my message I just spotted a hole in my theory. we will probably need to add an empty, zero-weighted-zero-priced item to hold free space it's probably just increases n by 1)
so, what's the matter.
https://rosettacode.org/wiki/Combinations_with_repetitions
as this problem is well-described up here^ I don't really want to use stack this way, I want to generate variations in single cycle, which is going from i=0 to i<C'(n,k).
so, If I can make it, how it works?
we have
int prices[n]; //appear mystically
int weights[n]; // same as previous and I guess we place (0,0) in both of them.
int W, k; // W initialized by our lord and savior
k = W/min(weights);
int road[k], finalroad[k]; //all 0
int curP = curW = maxP = maxW = 0;
for (int i = 0; i < rCombNumber(n, k); i ++) {
/*guys please help me to know how to generate this mask which is consists of indices from 0 to n (meaning of each element) and k is size of mask.*/
curW = 0;
for (int j = 0; j < k; j ++)
curW += weights[road[j]];
if (curW < W) {
curP = 0;
for (int l = 0; l < k; l ++)
curP += prices[road[l]];
if (curP > maxP) {
maxP = curP;
maxW = curW;
finalroad = road;
}
}
}
mask, road -- is an array of indices, each can be equal from 0 to n; and have to be generated as C'(n,k) (link about it above) from { 0, 1, 2, ... , n } by k elements in each selection (combination with repetitions where order is unimportant)
that's it. prove me wrong or help me. Much thanks in advance _
and yes, of course algorithm will take the hell much time, but it looks like it should work. and I'm very interesting in it.
UPDATE:
what do I miss?
http://pastexen.com/code.php?file=EMcn3F9ceC.txt

The answer was provided by Minoru here https://gist.github.com/Minoru/745a7c19c7fa77702332cf4bd3f80f9e ,
it's enough to increment only the first element, then we count all of the carries, set where we did a carry and count reset value as the maximum of elements to reset and reset with it.
here's my code:
#include <iostream>
using namespace std;
static long FactNaive(int n)
{
long r = 1;
for (int i = 2; i <= n; ++i)
r *= i;
return r;
}
static long long CrNK (long n, long k)
{
long long u, l;
u = FactNaive(n+k-1);
l = FactNaive(k)*FactNaive(n-1);
return u/l;
}
int main()
{
int numberOFchoices=7,kountOfElementsInCombination=4;
int arrayOfSingleCombination[kountOfElementsInCombination] = {0,0,0,0};
int leftmostResetPos = kountOfElementsInCombination;
int resetValue=1;
for (long long iterationCounter = 0; iterationCounter<CrNK(numberOFchoices,kountOfElementsInCombination); iterationCounter++)
{
leftmostResetPos = kountOfElementsInCombination;
if (iterationCounter!=0)
{
arrayOfSingleCombination[kountOfElementsInCombination-1]++;
for (int anotherIterationCounter=kountOfElementsInCombination-1; anotherIterationCounter>0; anotherIterationCounter--)
{
if(arrayOfSingleCombination[anotherIterationCounter]==numberOFchoices)
{
leftmostResetPos = anotherIterationCounter;
arrayOfSingleCombination[anotherIterationCounter-1]++;
}
}
}
if (leftmostResetPos != kountOfElementsInCombination)
{
resetValue = 1;
for (int j = 0; j < leftmostResetPos; j++)
{
if (arrayOfSingleCombination[j] > resetValue)
{
resetValue = arrayOfSingleCombination[j];
}
}
for (int j = leftmostResetPos; j != kountOfElementsInCombination; j++)
{
arrayOfSingleCombination[j] = resetValue;
}
}
for (int j = 0; j<kountOfElementsInCombination; j++)
{
cout<<arrayOfSingleCombination[j]<<" ";
}
cout<<"\n";
}
return 0;
}
thanks a lot, Minoru

Related

Almost same code running much slower

I am trying to solve this problem:
Given a string array words, find the maximum value of length(word[i]) * length(word[j]) where the two words do not share common letters. You may assume that each word will contain only lower case letters. If no such two words exist, return 0.
https://leetcode.com/problems/maximum-product-of-word-lengths/
You can create a bitmap of char for each word to check if they share chars in common and then calc the max product.
I have two method almost equal but the first pass checks, while the second is too slow, can you understand why?
class Solution {
public:
int maxProduct2(vector<string>& words) {
int len = words.size();
int *num = new int[len];
// compute the bit O(n)
for (int i = 0; i < len; i ++) {
int k = 0;
for (int j = 0; j < words[i].length(); j ++) {
k = k | (1 <<(char)(words[i].at(j)));
}
num[i] = k;
}
int c = 0;
// O(n^2)
for (int i = 0; i < len - 1; i ++) {
for (int j = i + 1; j < len; j ++) {
if ((num[i] & num[j]) == 0) { // if no common letters
int x = words[i].length() * words[j].length();
if (x > c) {
c = x;
}
}
}
}
delete []num;
return c;
}
int maxProduct(vector<string>& words) {
vector<int> bitmap(words.size());
for(int i=0;i<words.size();++i) {
int k = 0;
for(int j=0;j<words[i].length();++j) {
k |= 1 << (char)(words[i][j]);
}
bitmap[i] = k;
}
int maxProd = 0;
for(int i=0;i<words.size()-1;++i) {
for(int j=i+1;j<words.size();++j) {
if ( !(bitmap[i] & bitmap[j])) {
int x = words[i].length() * words[j].length();
if ( x > maxProd )
maxProd = x;
}
}
}
return maxProd;
}
};
Why the second function (maxProduct) is too slow for leetcode?
Solution
The second method does repetitive call to words.size(). If you save that in a var than it working fine
Since my comment turned out to be correct I'll turn my comment into an answer and try to explain what I think is happening.
I wrote some simple code to benchmark on my own machine with two solutions of two loops each. The only difference is the call to words.size() is inside the loop versus outside the loop. The first solution is approximately 13.87 seconds versus 16.65 seconds for the second solution. This isn't huge, but it's about 20% slower.
Even though vector.size() is a constant time operation that doesn't mean it's as fast as just checking against a variable that's already in a register. Constant time can still have large variances. When inside nested loops that adds up.
The other thing that could be happening (someone much smarter than me will probably chime in and let us know) is that you're hurting your CPU optimizations like branching and pipelining. Every time it gets to the end of the the loop it has to stop, wait for the call to size() to return, and then check the loop variable against that return value. If the cpu can look ahead and guess that j is still going to be less than len because it hasn't seen len change (len isn't even inside the loop!) it can make a good branch prediction each time and not have to wait.

Merge 5 Vectors To Matrix C++

I'm using C++.
I have 5 vectors.
int currDim = 5;
int a[currDim] = {1,6,11,16,21};
int b[currDim] = {2,7,12,17,22};
int c[currDim] = {3,8,13,18,23};
int d[currDim] = {4,9,14,19,24};
int e[currDim] = {5,10,15,20,25};
I want to merge them to one int matrix[currDim][5].
The matrix should be:
{1,2,3,4,5}
{6,7,8,9,10}
{11,12,13,14,15}
{16,17,18,19,20}
{21,22,23,24,25}
What i'm did:
int j=k=0;
for(int i = 0; i<currDim ; i++)
{
matrix[i][k++] = a[j];
matrix[i][k++] = b[j];
matrix[i][k++] = c[j];
matrix[i][k++] = d[j];
matrix[i][k++] = e[j];
k = 0;
j++;
}
The code is works but i looking for better way to increased efficiency , any suggestion?
First of all, I may have to apologize for my answer may not help, since I'm not quite sure what you mean by asking for "better ways": It could mean better coding style, increased efficiency, or generality.
Since it is really a simple task that only takes 25 assignment operations you are dealing with, efficiency is not such a big issue here.
However, for the elegance's sake, you can consider using an array of pointers to store a,b,c,d,e and replace five excessive assignment statements with a simple, elegant loop, just like the following code shows:
typedef int *pInt;
//Each element of arr is a int-type pointer
pInt arr[5] = {a, b, c, d, e};
int matrix[5][5] = {0};
for(int i = 0; i < 5; ++i){
for(int j = 0 ; j < 5; ++j){
matrix[i][j] = arr[j][i];
}
}
Let me know if my approach is helpful to you.
P.S. I usually leave left bracket in the end rather than starting a new line, and I tend to use ++i instead of i++, it's more of a personal habit and you don't need to exactly follow my style.

Positive rows in a matrix

I have to do an exercise for University that asks me to check ( for k times ) if a matrix has positive rows ( i mean a row with all positive elements ) , i think there's something wrong with the indices of for loops but i cannot find the mistakes.
i tried to debug with a cout statement apply to the counter an it gives me "101" , so it seems like compiler assign "1" to the positive rows and "0" to the negative
This is the code i wrote:
#include <iostream>
using namespace std;
const int N = 3;
bool positive(int a[N][N], int row[N], int x) {
bool condition = false;
for(int i = 0; i < N; i++) {
row[i] = a[x][i];
}
int j = 0;
while(j < N) {
if(row[j] >= 0) {
condition = true;
} else {
condition = false;
}
j++;
}
return condition;
}
bool function (int a[N][N], int z, int j, int k) {
int b[N];
int c[N];
int count = 0;
if(positive(a, b, z) && positive(a, c, j)) {
count++;
}
if(count == k) {
return true;
} else {
return false;
}
}
int main() {
int a[N][N] = {
{
2, 8, 6
}, {
-1, - 3, - 5
}, {
6, 10, 9
}
};
int k = 2;
for(int i = 0; i < N; i++) {
for(int j = 0; j < N; j++) {
if(function (a, i, j, k)) {
cout << "OK";
} else {
cout << "NO";
}
}
}
return 0;
}
You should probably take another look at this problem and restart with a different solution. The objective is pretty easy but your code is surprisingly complex and some of it doesn't really make sense.
For example, if you had a matrix like this:
1 2 4 --> matrix A
-1 8 -6
3 9 2
You have N=3 rows and columns. The only thing you have to to based on what you said is take the matrix, cycle through the N rows, and for each row, check it's N columns to see if anything is < 0.
Doing this K times, as you put it, makes no sense. The matrix will be the same every time you compare it since you're not changing it, why would you do it more than once? I think you should reread the assignment brief there.
As for the logic of finding which rows are positive or negative, just do something simple like this.
//Create array so we have one marker for each row to hold results.
bool rowPositiveFlags[N] = { true, true, true };
//Cycle through each row.
for (int row = 0; row < N; ++row)
//Cycle through every column in the current row.
for (int col = 0; col < N; ++col)
//If the column is negative, set the result for this row to false and break
//the column for loop as we don't need to check any more columns for this row.
if (A[row][col] < 0) {
rowPositiveFlags[row] = false;
break;
}
You should always name things so that you can read your code like a book. Your i's, j's, and k's just make something simple confusing. As for the problem, just plan out your solution.
Solve the problem by hand on paper, write the steps in comments in your code, and then write code below the comments so what you do definitely makes sense and isn't overkill.
And this is a great site, but next time, post a smaller piece of code that shows your problem. People shouldn't ever give you a full solution here for homework so don't look for one. Just find the spot where your indices are broken and paste that set of 5 lines or whatever else is wrong. People appreciate that and you'll get faster, better answers for showing the effort :)

How to derive the execution time growth rate(Big O)?

So I have the following code and I need to derive the execution time growth rate, however I have no idea where to start. My question is, how do I go about doing this? Any help would be appreciated.
Thank you.
// function to merge two sorted arrays
int merge (int smax, char sArray[], int tmax, char tArray[], char target[])
{
int m, s, t;
for (m = s = t = 0; s < smax && t < tmax; m++)
{
if (sArray[s] <= tArray[t])
{
target[m] = sArray[s];
s++;
}
else
{
target[m] = tArray[t];
t++;
}
}
int compCount = m;
for (; s < smax; m++)
{
target[m] = sArray[s++];
}
for (; t < tmax; m++)
{
target[m] = tArray[t++];
}
return compCount;
}
It's actually very simple.
Look, the first for loop increases either s or t at each iteration, so it's O(smax + tmax). The second loop is obviously O(smax), the third is O(tmax). Altogether we get O(smax + tmax).
(There exist some cleverer ways to prove, but I've intentionally left them out.)
All loops are bounded in number of iterations by (smax + tmax). So you could say the algorithm is O( max(smax,tmax) ) or O( smax +tmax).

All possible combination. Faster way

I have a vector of numbers between 1 and 100(this is not important) which can take sizes between 3 and 1.000.000 values.
If anyone can help me getting 3 value unique* combinations from that vector.
*Unique
Example: I have in the array the following values: 1[0] 5[1] 7[2] 8[3] 7[4] (the [x] is the index)
In this case 1[0] 5[1] 7[2] and 1[3] 5[1] 7[4] are different, but 1[0] 5[1] 7[2] and 7[2] 1[0] 5[1] are the same(duplicate)
My algorithm is a little slow when i work with a lot of values(example 1.000.000). So what i want is a faster way to do it.
for(unsigned int x = 0;x<vect.size()-2;x++){
for(unsigned int y = x+1;y<vect.size()-1;y++){
for(unsigned int z = y+1;z<vect.size();z++)
{
// do thing with vect[x],vect[y],vect[z]
}
}
}
In fact it is very very important that your values are between 1 and 100! Because with a vector of size 1,000,000 you have a lot of numbers that are equal and you don't need to inspect all of them! What you can do is the following:
Note: the following code is just an outline! It may lack sufficient error checking and is just here to give you the idea, not for copy paste!
Note2: When I wrote the answer, I assumed the numbers to be in the range [0, 99]. Then I read that they are actually in [1, 100]. Obviously this is not a problem and you can either -1 all the numbers or even better, change all the 100s to 101s.
bool exists[100] = {0}; // exists[i] means whether i exists in your vector
for (unsigned int i = 0, size = vect.size(); i < size; ++i)
exists[vect[i]] = true;
Then, you do similar to what you did before:
for(unsigned int x = 0; x < 98; x++)
if (exists[x])
for(unsigned int y = x+1; y < 99; y++)
if (exists[y])
for(unsigned int z = y+1; z < 100; z++)
if (exists[z])
{
// {x, y, z} is an answer
}
Another thing you can do is spend more time in preparation to have less time generating the pairs. For example:
int nums[100]; // from 0 to count are the numbers you have
int count = 0;
for (unsigned int i = 0, size = vect.size(); i < size; ++i)
{
bool exists = false;
for (int j = 0; j < count; ++j)
if (vect[i] == nums[j])
{
exists = true;
break;
}
if (!exists)
nums[count++] = vect[i];
}
Then
for(unsigned int x = 0; x < count-2; x++)
for(unsigned int y = x+1; y < count-1; y++)
for(unsigned int z = y+1; z < count; z++)
{
// {nums[x], nums[y], nums[z]} is an answer
}
Let us consider 100 to be a variable, so let's call it k, and the actual numbers present in the array as m (which is smaller than or equal to k).
With the first method, you have O(n) preparation and O(m^2*k) operations to search for the value which is quite fast.
In the second method, you have O(nm) preparation and O(m^3) for generation of the values. Given your values for n and m, the preparation takes too long.
You could actually merge the two methods to get the best of both worlds, so something like this:
int nums[100]; // from 0 to count are the numbers you have
int count = 0;
bool exists[100] = {0}; // exists[i] means whether i exists in your vector
for (unsigned int i = 0, size = vect.size(); i < size; ++i)
{
if (!exists[vect[i]])
nums[count++] = vect[i];
exists[vect[i]] = true;
}
Then:
for(unsigned int x = 0; x < count-2; x++)
for(unsigned int y = x+1; y < count-1; y++)
for(unsigned int z = y+1; z < count; z++)
{
// {nums[x], nums[y], nums[z]} is an answer
}
This method has O(n) preparation and O(m^3) cost to find the unique triplets.
Edit: It turned out that for the OP, the same number in different locations are considered different values. If that is really the case, then I'm sorry, there is no faster solution. The reason is that all the possible combinations themselves are C(n, m) (That's a combination) that although you are generating each one of them in O(1), it is still too big for you.
There's really nothing that can be done to speed up the loop body you have there. Consider that with 1M vector size, you are making one trillion loop iterations.
Producing all combinations like that is an exponential problem, which means that you won't be able to practically solve it when the input size becomes large enough. Your only option would be to leverage specific knowledge of your application (what you need the results for, and how exactly they will be used) to "work around" the issue if possible.
Possibly you can sort your input, make it unique, and pick x[a], x[b] and x[c] when a < b < c. The sort will be O(n log n) and picking the combination will be O(n³). Still you will have less triplets to iterate over:
std::vector<int> x = original_vector;
std::sort(x.begin(), x.end());
std::erase(std::unique(x.begin(), x.end()), x.end());
for(a = 0; a < x.size() - 2; ++a)
for(b=a+1; b < x.size() - 1; ++b)
for(c=b+1; c< x.size(); ++c
issue triplet(x[a],x[b],x[c]);
Depending on your actual data, you may be able to speed it up significantly by first making a vector that has at most three entries with each value and iterate over that instead.
As r15habh pointed out, I think the fact that the values in the array are between 1-100 is in fact important.
Here's what you can do: make one pass through the array, reading values into a unique set. This by itself is O(n) time complexity. The set will have no more than 100 elements, which means O(1) space complexity.
Now since you need to generate all 3-item permutations, you'll still need 3 nested loops, but instead of operating on the potentially huge array, you'll be operating on a set that has at most 100 elements.
Overall time complexity depends on your original data set. For a small data set, time complexity will be O(n^3). For a large data set, it will approach O(n).
If understand your application correctly then you can use a tuple instead, and store in either a set or hash table depending on your requirements. If the normal of the tri matters, then make sure that you shift the tri so that lets say the largest element is first, if normal shouldn't matter, then just sort the tuple. A version using boost & integers:
#include <set>
#include <algorithm>
#include "boost/tuple/tuple.hpp"
#include "boost/tuple/tuple_comparison.hpp"
int main()
{
typedef boost::tuple< int, int, int > Tri;
typedef std::set< Tri > TriSet;
TriSet storage;
// 1 duplicate
int exampleData[4][3] = { { 1, 2, 3 }, { 2, 3, 6 }, { 5, 3, 2 }, { 2, 1, 3 } };
for( unsigned int i = 0; i < sizeof( exampleData ) / sizeof( exampleData[0] ); ++i )
{
std::sort( exampleData[i], exampleData[i] + ( sizeof( exampleData[i] ) / sizeof( exampleData[i][0] ) ) );
if( !storage.insert( boost::make_tuple( exampleData[i][0], exampleData[i][1], exampleData[i][2] ) ).second )
std::cout << "Duplicate!" << std::endl;
else
std::cout << "Not duplicate!" << std::endl;
}
}