Knapsack - save time and memory - c

According to Wikipedia and other sources I had went through, you need matrix m[n][W]; n - number of items and W - total capacity of knapsack. This matrix get really big, sometimes too big to handle it in C program. I know that dynamic programming is based on saving time for memory but still, is there any solution where can you save time and memory?
Pseudo-code for Knapsack problem:
// Input:
// Values (stored in array v)
// Weights (stored in array w)
// Number of distinct items (n)
// Knapsack capacity (W)
for j from 0 to W do
m[0, j] := 0
end for
for i from 1 to n do
for j from 0 to W do
if w[i] <= j then
m[i, j] := max(m[i-1, j], m[i-1, j-w[i]] + v[i])
else
m[i, j] := m[i-1, j]
end if
end for
end for
Lets say, that W = 123456789 and n = 100. In this case we get really big matrix m[100][123456789]. I was thinking how to implement this, but best I have in my mind is just to save which items was selected with one bit (0/1). Is this possible? Or is there any other approach for this problem?
int32 -> 32 * 123456789 * 100 bits
one_bit -> 1 * 123456789 * 100 bits
I hope this is not stupid question and thanks for your effort.
EDIT - working C code:
long int i, j;
long int *m[2];
m[0] = (long int *) malloc(sizeof(long int)*(W+1));
m[1] = (long int *) malloc(sizeof(long int)*(W+1));
for(i = 0; i <= W; i++){
m[0][i] = 0;
}
int read = 0;
int write = 1;
int tmp;
long int percent = (W+1)*(n)/100;
long int counter = 0;
for(i = 1; i <= n; i++){
for(j = 0; j <= W; j++){
if(w[i-1] <= j){
m[write][j] = max(m[read][j],(v[i-1]) + m[read][j-(w[i-1])]);
}else{
m[write][j] = m[read][j];
}
counter++;
if(counter == percent){
printf("."); //printing dot (.) for each percent
fflush(stdout);
counter = 0;
}
}
tmp = read;
read = write;
write = tmp;
}
printf("\n%ld\n", m[read][W]);
free(m[0]);
free(m[1]);

Knapsack problem can be solved using O(W) space.
At each step of the iteration you need only 2 rows - current state of the array m[i] and m[i + 1].
current = 1
int m[2][W]
set NONE for all elements of m # that means we are not able to process this state
m[0][0] = 0 # this is our start point, initially empty knapsack
FOR i in [1..n] do
next = 3 - current; /// just use 1 or 2 based on the current index
for j in [0...W] do
m[next][j] = m[current][j]
FOR j in [w[i]..W] do
if m[current][j - w[i]] is not NONE then # process only reachable positions
m[next][j] = max(m[next][j], m[current][j - w[i]] + v[i]);
current = next; /// swap current state and the produced one
Also it is possible to use only 1 array. Here is the pseudocode
FOR i in [1..n] do
FOR j in [w[i]..W] do
m[j] = max(m[j], m[j - w[i]] + v[i]);

You can decrease the space use from m[100][123456789] into m[2][123456789] by this observation:
Look at this part of the code, at any time, you only need to refer to two rows of the matrix i and i - 1
if w[i] <= j then
m[i, j] := max(m[i-1, j], m[i-1, j-w[i]] + v[i])
else
m[i, j] := m[i-1, j]
end if
You can use this trick:
int current = 1;
//.........
if w[i] <= j then
m[current, j] := max(m[1 - current, j], m[1 - current, j-w[i]] + v[i])
else
m[i, j] := m[1 - current, j]
end if
current = 1 - current;

Related

How to find all the palindromes in a large array?

I need to find all the palindromes of π with 50 million digits 3.141592653589793238462643383279502884197169399375105820974944592307816406286... (goes on and on...)
I've stored all the digits of π in a char array. Now I need to search and count the number of 'palindromes' of length 2 to 15. For example, 535, 979, 33, 88, 14941, etc. are all valid results.
The final output I want is basically like the following.
Palindrome length Number of Palindromes of this length
-----------------------------------------------------------------
2 1234 (just an example)
3 1245
4 689
... ...
... ...
... ...
... ...
15 0
pseudocode of my logic so far - it works but takes forever
//store all digits in a char array
char *piArray = (char *)malloc(NUM_PI_DIGITS * sizeof(char));
int count = 0; //count for the number of palindromes
//because we only need to find palindroms that are 2 - 15 digits long
for(int i = 2; i <= 15; i++){
//loop through the piArray and find all the palindromes with i digits long
for(int j = 0; j < size_of_piArray; j++){
//check if the the sub array piArray[j:j+i] is parlindrom, if so, add a count
bool isPalindrome = true;
for (int k = 0; k < i / 2; k++)
{
if (piArray [j + k] != piArray [j + i - 1 - k])
{
isPalindrom = false;
break;
}
}
if(isPalindrome){
count++;
}
}
}
The problem I am facing now is that it takes too long to loop through the array of this large size (15-2)=13 times. Is there any better way to do this?
Here is a C version adapted from the approach proposed by Caius Jard:
void check_pi_palindromes(int NUM_PI_DIGITS, int max_length, int counts[]) {
// store all digits in a char array
int max_span = max_length / 2;
int start = max_span;
int end = NUM_PI_DIGITS + max_span;
char *pi = (char *)malloc(max_span + NUM_PI_DIGITS + max_span);
// read of generate the digits starting at position `max_span`
[...]
// clear an initial and trailing area to simplify boundary testing
memset(pi, ' ', start);
memset(pi + end, ' ', max_span);
// clear the result array
for (int i = 0; i <= max_length; i++) {
count[i] = 0;
}
// loop through the pi array and find all the palindromes
for (int i = start; i < end; i++) {
if (pi[i + 1] == pi[i - 1]) { //center of an odd length palindrome
count[3]++;
for (n = 2; n <= max_span && pi[i + n] == pi[i - n]; n++) {
count[n * 2 + 1]++;
}
}
if (pi[i] == pi[i - 1]) { //center of an even length palindrome
count[2]++;
for (n = 1; n <= max_span && pi[i + n] == pi[i - n]; n++) {
count[n * 2]++;
}
}
}
}
For each position in the array, it scans in both directions for palindromes of odd and even lengths with these advantages:
single pass through the array
good cache locality because all reads from the array are in a small span from the current position
fewer tests as larger palindromes are only tested as extensions of smaller ones.
A small working prefix and suffix is used to avoid the need to special case the beginning and end of the sequence.
I can't solve it for C, as I'm a C# dev but I expect the conversion will be trivial - I've tried to keep it as basic as possible
char[] pi = "3.141592653589793238462643383279502884197169399375105820974944592307816406286".ToCharArray(); //get a small piece as an array of char
int[] lenCounts = new int[16]; //make a new int array with slots 0-15
for(int i = 1; i < pi.Length-1; i++){
if(pi[i+1] == pi[i-1]){ //center of an odd length pal
int n = 2;
while(pi[i+n] == pi[i-n] && n <= 7) n++;
lenCounts[((n-1)*2+1)]++;
} else if(pi[i] == pi[i-1]){ //center of an even length pal
int n = 1;
while(pi[i+n] == pi[i-1-n] && n <= 7) n++;
lenCounts[n*2]++;
}
}
This demonstrates the "crawl the string looking for a palindrome center then crawl away from it in both directions looking for equal chars" technique..
..the only thing I'm not sure on, and it has occurred in the Pi posted, is what you want to do if palindromes overlap:
3.141592653589793238462643383279502884197169399375105820974944592307816406286
This contains 939 and overlapping with it, 3993. The algo above will find both, so if overlaps are not to be allowed then you might need to extend it to deal with eliminating earlier palindromes if they're overlapped by a longer one found later
You can play with the c# version at https://dotnetfiddle.net/tkQzBq - it has some debug print lines in too. Fiddles are limited to a 10 second execution time so I don't know if you'll be able to time the full 50 megabyte 😀 - you might have to run this algo locally for that one
Edit: fixed a bug in the answer but I haven't fixed it in the fiddle; I did have while(.. n<lenCounts.Length) i.e. allowing n to reach 15, but that would be an issue because it's in both directions.. nshould go to 7 to remain in range of the counts array. I've patched that by hard coding 7 but you might want to make it dependent on array length/2 etc
Well, I think it can't be done less than O(len*n), and that you are doing this O(len^2*n), where 2 <= len <= 15, is almost the same since the K coefficient doesn't change the O notation in this case, but if you want to avoid this extra loop, you can check these links, it shouldn't be hard to add a counter for each length since these codes are counting all of them, with maximum possible length:
source1, source2, source3.
NOTE: Mostly it's better to reach out GeekForGeeks when you are looking for algorithms or optimizations.
EDIT: one of the possible ways with O(n^2) time complexity and O(n)
Auxiliary Space. You can change unordered_map by array if you wish, anyway here the key will be the length and the value will be the count of palindromes with that length.
unordered_map<int, int> countPalindromes(string& s) {
unordered_map<int, int> m;
for (int i = 0; i < s.length(); i++) {
// check for odd length palindromes
for (int j = 0; j <= i; j++) {
if (!s[i + j])
break;
if (s[i - j] == s[i + j]) {
// check for palindromes of length
// greater than 1
if ((i + j + 1) - (i - j) > 1)
m[(i + j + 1) - (i - j)]++;
} else
break;
}
// check for even length palindromes
for (int j = 0; j <= i; j++) {
if (!s[i + j + 1])
break;
if (s[i - j] == s[i + j + 1]) {
// check for palindromes of length
// greater than 1
if ((i + j + 2) - (i - j) > 1)
m[(i + j + 2) - (i - j)]++;
} else
break;
}
}
return m;
}

Split C array into n equal parts

I am trying to split an array into n equal parts by calculating start and end indices. The address of the start and end elements will be passed into a function that will sort these arrays. For example, if arraySize = 1000, and n=2, the indices will be 0, 499, 999. So far I have the below code but for odd n, it is splitting it into more than n arrays. Another way I thought of doing this is by running through the loop n times, but I'm not sure where to start.
int chunkSize = arraySize / numThreads;
for (int start = 0; start < arraySize; start += chunkSize) {
int end = start + chunkSize - 1;
if (end > arraySize - 1) {
end = arraySize - 1;
}
InsertionSort(&array[start], end - start + 1);
}
EDIT: Here's something else I came up with. It seems to be working, but I need to do some more thorough testing. I've drawn this out multiple times and traced it by hand. Hopefully, there aren't any edge cases that will fail. I am already restricting n >= arraySize.
int chunkSize = arraySize / numThreads;
for (int i = 0; i < numThreads; i++) {
int start = i * chunkSize;
int end = start + chunkSize - 1;
if (i == numThreads - 1) {
end = arraySize - 1;
}
for (int i = start; i <= end; i++) {
printf("%d ", array[i]);
}
printf("\n");
}
Calculate the minimum chunk size with the truncating division. Then calculate the remainder. Distribute this remainder by adding 1 to some chunks:
Pseudo-code:
chunk_size = array_size / N
bonus = array_size - chunk_size * N // i.e. remainder
for (start = 0, end = chunk_size;
start < array_size;
start = end, end = start + chunk_size)
{
if (bonus) {
end++;
bonus--;
}
/* do something with array slice over [start, end) interval */
}
For instance if array_size is 11 and N == 4, 11/N yields 2. The remainder ("bonus") is 3: 11 - 2*3. Thus the first three iterations of the loop will add 1 to the size: 3 3 3. The bonus then hits zero and the last chunk size will just be 2.
What we are doing here is nothing more than distributing an error term in a discrete quantization, in a way that is satisfactory somehow. This is exactly what happens when a line segment is drawn on a raster display with the Bresenham algorithm, or when an image is reduced to a smaller number of colors using Floyd-Steinberg dithering, et cetera.
You need to calculate your chunk size so that it is "rounded up", not down. You could do it using % operator and a more complex formula, but just using simple if is probably easier to understand:
int chunkSize = arraySize / numThreads;
if (chunkSize * numThreads < arraySize) {
// In case arraySize is not exactly divisible by numThreads,
// we now end up with one extra smaller chunk at the end.
// Fix this by increseing chunkSize by one byte,
// so we'll end up with numThread chunks and smaller last chunk.
++chunkSize;
}
I hope this would help:
int chunkSize = arraySize / numThreads;
for (int i = 0; i < numThreads-1; i++) {
start = i* chunkSize;
end = start + chunkSize - 1;
InsertionSort(&array[start], end + 1);
}
//Last chunk with all the remaining content
start = end + 1;
end = arraySize - 1;
InsertionSort(&array[start], end + 1);

Largest Slice Sum from Two Different Arrays

Original Problem: Problem 1 (INOI 2015)
There are two arrays A[1..N] and B[1..N]
An operation SSum is defined on them as
SSum[i,j] = A[i] + A[j] + B[t (where t = i+1, i+2, ..., j-1)] when i < j
SSum[i,j] = A[i] + A[j] + B[t (where t = 1, 2, ..., j-1, i+1, i+2, ..., N)] when i > j
SSum[i,i] = A[i]
The challenge is to find the largest possible value of SSum.
I had an O(n^2) solution based on computing the Prefix Sums of B
#include <iostream>
#include <utility>
int main(){
int N;
std::cin >> N;
int *a = new int[N+1];
long long int *bPrefixSums = new long long int[N+1];
for (int iii=1; iii<=N; iii++) //1-based arrays to prevent confusion
std::cin >> a[iii];
bPrefixSums[0] = 0;
for (int b,iii=1; iii<=N; iii++){
std::cin >> b;
bPrefixSums[iii] = bPrefixSums[iii-1] + b;
}
long long int SSum, SSumMax=-(1<<10);
for (int i=1; i <= N; i++)
for (int j=1; j <= N; j++){
if (i<j)
SSum = a[i] + a[j] + (bPrefixSums[j-1] - bPrefixSums[i]);
else if (i==j)
SSum = a[i];
else
SSum = a[i] + a[j] + ((bPrefixSums[N] - bPrefixSums[i]) + bPrefixSums[j-1]);
SSumMax = std::max(SSum, SSumMax);
}
std::cout << SSumMax;
return 0;
}
For larger values of N around 10^6, the program fails to complete the task in 3 seconds.
Since I didn't get enough rep to add a comment, I shall just write the ideas here in this answer.
This problem is really nice, and I was actually inspired by this link. Thanks to #superty.
We may consider this problem separately, in other words, into three conditions: i == j, i < j, i > j. And we only need to find the maximum result.
Consider i == j: The maximum result should be a[i], and it's easy to find the answer in O(n) time complexity.
Consider i < j: It's quite similar to the classical maximum sum problem, and for each j we only need to find the i in the left which manages to make the result maximum.
Think about the classical problem first, if we are asked to get the maximum partial sum for array a, we calculate the prefix-sum of a in order to get an O(n) complexity. Now in this problem, it is almost the same.
You can see that here(i < j), we have SSum[i,j] = A[i] + A[j] + B[t (where t = i+1, i+2, ..., j-1)] = (B[1] + B[2] + ... + B[j - 1] + A[j]) - (B[1] + B[2] + ... B[i] - A[i]), and the first term stays the same when j stays the same while the second term stays the same when i stays the same. So the solution now is quite clear, you get two 'prefix-sum' and find the smallest prefix_sum_2[i] for each prefix_sum_1[j].
Consider i > j: It's quite similar with this discussion on SO(but this discussion doesn't help much).
Similarly, we get SSum[i,j] = A[i] + A[j] + B[t (where t = 1, 2, ..., j-1, i+1, i+2, ..., N)] = (B[1] + B[2] + ... + B[j - 1] + A[j]) + (A[i] + B[i + 1] + ... + B[n - 1] + B[n]). Now you need to get both the prefix-sum and the suffix-sum of the array (we need prefix_sum[i] = a[i] + prefix_sum[i - 1] - a[i - 1] and suffix similarly), and get another two arrays, say ans_left[i] as the maximum value of the first term for all j <= i and ans_right[j] as the maximum value of the second term for i >= j, so the answer in this condition is the maximum value among all (ans_left[i] + ans_right[i + 1])
Finally, the maximum result required for the original problem is the maximum of the answers for these three sub-cases.
It's clear to see that the total complexity is O(n).

kth smallest element in two sorted array - O(log n) Solution

The above is one of the interview question. There is an article about 0(log n) algorithm explaining the invariant (i + j = k – 1). I'm having much difficulty in understanding this algorithm. Could anyone explain this algorithm in simple way and also why do they calculate i as (int)((double)m / (m+n) * (k-1)). I appreciate your help. Thanks.
protected static int kthSmallestEasy(int[] A, int aLow, int aLength, int[] B, int bLow, int bLength, int k)
{
//Error Handling
assert(aLow >= 0); assert(bLow >= 0);
assert(aLength >= 0); assert(bLength >= 0); assert(aLength + bLength >= k);
int i = (int)((double)((k - 1) * aLength / (aLength + bLength)));
int j = k - 1 - i;
int Ai_1 = aLow + i == 0 ? Int32.MinValue : A[aLow + i - 1];
int Ai = aLow + i == A.Length ? Int32.MaxValue : A[aLow + i];
int Bj_1 = bLow + j == 0 ? Int32.MinValue : B[bLow + j - 1];
int Bj = bLow + j == B.Length ? Int32.MaxValue : B[bLow + j];
if (Bj_1 < Ai && Ai < Bj)
return Ai;
else if (Ai_1 < Bj && Bj < Ai)
return Bj;
assert(Ai < Bj - 1 || Bj < Ai_1);
if (Ai < Bj_1) // exclude A[aLow .. i] and A[j..bHigh], k was replaced by k - i - 1
return kthSmallestEasy(A, aLow + i + 1, aLength - i - 1, B, bLow, j, k - i - 1);
else // exclude A[i, aHigh] and B[bLow .. j], k was replaced by k - j - 1
return kthSmallestEasy(A, aLow, i, B, bLow + j + 1, bLength - j - 1, k - j - 1);
Could anyone explain this algorithm in simple way.
Yes, it is essentially a bisection algorithm.
In successive passes, it moves probes on one array index upward and the other index array downward, seeking equal values while keeping the sum of the two indices equal to k.
and also why do they calculate i as (int)((double)m / (m+n) * (k-1)).
This gives an estimate of the new half-way point assuming an equidistribution of values between the known points.

complexity for a nested loop with varying internal loop

Very similar complexity examples. I am trying to understand as to how these questions vary. Exam coming up tomorrow :( Any shortcuts for find the complexities here.
CASE 1:
void doit(int N) {
while (N) {
for (int j = 0; j < N; j += 1) {}
N = N / 2;
}
}
CASE 2:
void doit(int N) {
while (N) {
for (int j = 0; j < N; j *= 4) {}
N = N / 2;
}
}
CASE 3:
void doit(int N) {
while (N) {
for (int j = 0; j < N; j *= 2) {}
N = N / 2;
}
}
Thank you so much!
void doit(int N) {
while (N) {
for (int j = 0; j < N; j += 1) {}
N = N / 2;
}
}
To find the O() of this, notice that we are dividing N by 2 each iteration. So, (not to insult your intelligence, but for completeness) the final non-zero iteration through the loop we will have N=1. The time before that we will have N=a(2), then before that N=a(4)... where 0< a < N (note those are non-inclusive bounds). So, this loop will execute a total of log(N) times, meaning the first iteration we see that N=a2^(floor(log(N))).
Why do we care about that? Well, it's a geometric series which has a nice closed form:
Sum = \sum_{k=0}^{\log(N)} a2^k = a*\frac{1-2^{\log N +1}}{1-2} = 2aN-a = O(N).
If someone can figure out how to get that latexy notation to display correctly for me I would really appreciate it.
You already have the answer to number 1 - O(n), as given by #NickO, here is an alternative explanation.
Denote the number of outer repeats of inner loop by T(N), and let the number of outer loops be h. Note that h = log_2(N)
T(N) = N + N/2 + ... + N / (2^i) + ... + 2 + 1
< 2N (sum of geometric series)
in O(N)
Number 3: is O((logN)^2)
Denote the number of outer repeats of inner loop by T(N), and let the number of outer loops be h. Note that h = log_2(N)
T(N) = log(N) + log(N/2) + log(N/4) + ... + log(1) (because log(a*b) = log(a) + log(b)
= log(N * (N/2) * (N/4) * ... * 1)
= log(N^h * (1 * 1/2 * 1/4 * .... * 1/N))
= log(N^h) + log(1 * 1/2 * 1/4 * .... * 1/N) (because log(a*b) = log(a) + log(b))
< log(N^h) + log(1)
= log(N^h) (log(1) = 0)
= h * log(N) (log(a^b) = b*log(a))
= (log(N))^2 (because h=log_2(N))
Number 2 is almost identical to number 3.
(In 2,3: assuming j starts from 1, not from 0, if this is not the case #WhozCraig giving the reason why it never breaks)

Resources