Knapsack algorithm for large input - c

I have developed this knapsack algorithm based on pseudo-code found on wikipedia. It works fine for small number of items and capacity (n=6, v=2014), but it crashes for large numbers (n=5, v=123456789).
Additional problem is, that my program is tested by makefile with time limit set at 1 second.
What can i do to save time and memory?
v - Knapsack capacity
n - Number of items
weight[] - Weights
value[] - Values
int knapSack(int v, int weight[], int value[], int n){
int a, i, j;
int **ks;
ks = (int **)calloc(n+1, sizeof(int*));
for(a = 0; a < (n+1); a++) {
ks[a] = (int *)calloc(v+1, sizeof(int));
}
for (i = 1; i <= n; i++){
for (j = 0; j <= v; j++){
if (weight[i-1] <= j){
ks[i][j] = max(value[i-1] + ks[i-1][j-weight[i-1]], ks[i-1][j]);
} else {
ks[i][j] = ks[i-1][j];
}
}
}
int result = ks[n][v];
for(i = 0; i < (n+1); i++) {
free(ks[i]);
}
free(ks);
return result;
}

An array of 123456789 integer elements declared on the stack will crash many implementations of C. Sounds like this is your problem. Did you declare your arrays inside of a function (on the stack)?
// on heap
static int v[123456789]={0};
// on the stack (inside a function like main() )
int foo()
{
int v[123456789]={0};
}

Related

Trying to return an Array in C

I've been trying to return an array in C. I'm mostly acquainted with Java (where it's comparatively easier to return an array).
This is what I'm trying to do right now:
#include <stdio.h>
#include <stdlib.h>
int * duplicates(int[],int);
int main() {
int arr[] = {3,5,6,5,9,5,10,11,3};
int n = sizeof(arr)/sizeof(arr[0]);
int * tempArray = duplicates(arr,n);
for (int i = 0; i < n; i++) {
printf("%d \t",tempArray[i]);
}
}
int * duplicates(int arr[], int n) {
int tempArray[n];
int r = 0;
int flag = 0;
for (int i = 0; i < n; i++) {
for (int j = i+1; j < n; j++) {
if (arr[i] == arr[j]) {
for(int k = 0; k < r; k++) {
if(arr[i] == tempArray[k]) {
flag = 1;
}
}
if (flag == 0) {
tempArray[r++] = arr[i];
flag = 0;
} else {
break;
}
}
}
}
return tempArray;
}
And this, to no surprise - crashes my program. How I can return an array in C, because that feels like a bare-minimum, something I should know before I can move further into the language.
Yeah, just allocate the memory like here Returning an array using C
int * duplicates(int arr[], int n) {
int *tempArray;
...
tempArray = (int *)malloc(sizeof(int) * n);
if (tempArray == NULL)
return (NULL);
...
This will work. Google why you should 'dynamically allocate the memory' and 'pointer'.
the array you created inside function duplicate has automatic storage duration since it's on the stack of function unlike in java in which arrays are objects and allocated on the heap. anyway I just wanted to add some NOTE which is don't use VLA(variable length arrays) like int array[n] instead use literals like int array[50] since VLA generate a lot of assembly code(affect performance a tiny bit) and can have security problems like stack overflows or cause the program to crash. Also, not all compilers implement it(portability issues).

Merge Sort in C using Recursion

This is my code for merge sort in C. I'm not able to understand what the issue is here. My knowledge of pointers is not that much. The merge function takes in 2 arrays and merges them. the sort function is a recursive function which is supposed to sort the array.
int * merge(int *fir, int n, int *sec, int m){
int res[m+n];
int x=0, y=0;
for(int i = 0; i < m+n; i++){
if(*(fir+x)<=*(sec+y)){
res[i] = *(fir+x);
x++;
}else{
res[i] = *(sec+y);
y++;
}
}
return res;
}
int * sort(int A[], int n){
if(n == 1){
return A;
}
int mid = n/2;
int AL[mid], AR[n-mid];
for(int i = 0; i < mid; i++){
AL[i] = A[i];
}
for(int i = 0; i < n-mid; i++){
AR[i] = A[i+mid];
}
int *BL, *BR;
BL = sort(AL, mid);
BR = sort(AR, n-mid);
return(merge(BL, mid, BR, n-mid));
}
int main(){
int n;
scanf("%d", &n);
int A[n];
for(int i = 0; i < n; i++){
scanf("%d", &A[i]);
}
int *sortedArray;
sortedArray = sort(A, n);
for(int i = 0; i < n; i++){
printf("%d ", *(sortedArray+i));
}
return 0;
}
And this is the output
q8.c:16:9: warning: address of stack memory associated with local variable 'res' returned [-Wreturn-stack-address]
return res;
^~~
1 warning generated.
7
23 12 56 67 11 99 97
97 32766 539779418 32767 -2002825496 32767 6 %```
There are two issues here: First, you merge your partial arrays into a temporary local array, which yoes out of bounds after you return from merge. The pointer you return points to invlid memory. That's what the warning about.
Second, you don't check whether you are reading beyond the limits of the partial arrays when you merge: The condition x < n must be true when you access fir, likewise for y < m and sec.
You are also causing confusion by returning a pointer to the first element of the sorted or merged arrays. That suggests that you create new sorted and merged arrays (and that's what you try to do in merge). This is okay for garbage-collected morern languages, but C doesn't work that way.
In C, if you need new memory, you must allocate it and later free it explicitly. In a recursive function like yours, this is tedious, because you are interested only in the final sorted array, not in the intermediate results. Therefore, C sorting algorithms usually work "in place": The same memory is used thoughout the sorting and elements are swapped. The original order of the elements is lost unless you make a copy before sorting.
For merge sort, you need auxiliary memory. In your case, you use the temporary arrays AL and AR, which are copies of the contents of the original array, A. Now when you merge, you can merge AL and AR back into A.
So istead of creating a ephemeral local array, pass in A so that it can be filled with the sorted elements:
void sort(int A[], int n)
{
if (n > 1) {
int mid = n / 2;
int AL[mid], AR[n - mid];
for (int i = 0; i < mid; i++) AL[i] = A[i];
for (int i = 0; i < n - mid; i++) AR[i] = A[i + mid];
sort(AL, mid);
sort(AR, n - mid);
merge(A, AL, mid, AR, n - mid);
}
}
Your merge function is now very similar to the one you has before, only that you have the result array as parameter and that you must catch the out-of-bound cases before accessing elements with [].
void merge(int *res, const int *fir, int n, const int *sec, int m)
{
int x = 0, y = 0;
for(int i = 0; i < m + n; i++) {
if (x == n) res[i] = sec[y++];
else if (y == m) res[i] = fir[x++];
else if (fir[x] <= sec[y]) res[i] = fir[x++];
else res[i] = sec[y++];
}
}

(C programming) Segfault when combining 2 one-dimensional array into 1 multi-dimensional array

I am writing a program where I take 2 one dimensional arrays and generate a matrix in its most simplified form Ax=b.
This part of the function takes in the arrays A and b. A is A[n*n] and b is b[n]. In this section I tried to combine the two arrays so it looks like an actual matrix.
This code works, however, if n were to be greater than 1023 it would cause a segmentation fault when calling the main function. I was wondering if there is a better way in doing this. When I tried to use the GDB debugger, it stoped at the line Y[i][j] = A[k]; so I think this is the problem that requires fixing
int linsolve ( int n, double A[], double b[], double x []) {
double Y[n][n+1]; //Creating multidimensional matrix
int k = 0;
// Turns the two one dimensional array into one multidimensional
for (int i=0; i < n; i++){ //iterating row
for (int j=0; j < n; j++){ // per column
Y[i][j] = A[k]; // adding from array A to Y
k++;
}
Y[i][n] = b[i]; // adding from Array b to Y
}
I assume you are using a Unix/Linux type system. First find out the stack size by typing
ulimit -s
This is the stack size in kilobytes. On my system it is 8Mb. If you have a 1200x1200 matrix, that will require
1200x1201x8 appx 10Mb
This is why the program segvs. You are creating 10Mb array on an 8Mb stack. The question is, does A or b live on the stack too? You may be getting a segv because the item you are passing through was created on the stack and is larger than the allocated stack.
To solve it, create the array on the heap as #shirish has suggested. An alternative to #shirish's technique would be
int linsolve ( int n, double A[], double b[], double x []) {
double **Y = new double *[n];
double *Ybody = new double[n * (n + 1)];
for (int i = 0; i < n; i++) {
Y[i] = &Ybody[i * (n + 1)];
}
// Turns the two one dimensional array into one multidimensional
int k = 0
for (int i=0; i < n; i++){
for (int j=0; j < n; j++){
Y[i][j] = A[k++];
}
Y[i][n] = b[i];
}
// Do something
// Free up Y before returning
delete [] Y;
delete [] Ybody;
}
// Assuming A has n * n elements
int linsolve ( int n, double A[], double b[], double x []) {
double **Y = new double *[n];
for (int i = 0; i < n; i++) {
Y[i] = new double[n + 1];
}
int k = 0;
// Turns the two one dimensional array into one multidimensional
for (int i=0; i < n; i++){ //iterating row
for (int j=0; j < n; j++){ // per column
Y[i][j] = A[k++]; // adding from array A to Y
}
Y[i][n] = b[i]; // adding from Array b to Y
}
// Do something
// Free up Y before returning
for(int i = 0; i < n; i++) {
delete [] Y[i];
}
delete [] Y;
//Return int here
}

How to delete multiple elements from a array at the same time

I want to delete multiple elements from array using index array,this is my code:
// b is an index array, n is size of b,
// player is the array need to be delete elements,
// size is the size of player
void play_cards(int b[],int n,int player[],int *size){
int i;
for(i = 0; i < n; i++)
delete_cards(b[i],player,size);
}
void delete_cards(int n,int player[],int *size){
int i;
for(i = n; i < *size; i++)
player[i] = player[i+1];
*size -= 1;
}
int main(void){
int player[10] = {1,2,3,3,4,4,5,5,6,7};
int index[6] = {2,3,4,5,6,7};
int size = 10;
play_cards(index,6,player,&size);
for(int i = 0; i < size; i++)
printf("%d|",player[i]);
puts("");
return 0;
}
I expect print the player should be 1,2,6,7 instead of 1,2,3,4. How should I fix it?
First I would not call the function delete_cards as it suggests that it deletes multiple cards which it does not - just delete_card would make things more clear.
Anyway - when you change the player array before you have played all cards in the index array, you change the meaning of the indexes. This is why your current code doesn't work.
So you can do two things:
a) Play all cards first and then delete the cards played. This could be done by first marking played card with -1 and then have a loop where you removed all element being -1
or
b) Play a card, delete it and adjust the remaining elements in index by decrementing them by one. Note: This solution requires that index is sorted (lowest first).
Solution a) could look something like this:
void delete_played_cards(int player[],int *size)
{
int i;
int next_pos = 0;
int deleted = 0;
for(i = 0; i < *size; i++)
{
if (player[i] != -1)
{
player[next_pos] = player[i];
if (i != next_pos)
{
player[i] = -1;
}
++next_pos;
}
else
{
++deleted;
}
}
*size -= deleted;
}
void play_cards(int b[],int n,int player[],int *size)
{
int i;
for(i = 0; i < n; i++)
{
player[b[i]] = -1; // Mark card as played
}
delete_played_cards(player,size);
}
int main(void)
{
int player[10] = {1,2,3,3,4,4,5,5,6,7};
int index[6] = {2,3,4,5,6,7};
int size = 10;
play_cards(index,6,player,&size);
for(int i = 0; i < size; i++)
printf("%d|",player[i]);
puts("");
return 0;
}
Modify play_cards:
void play_cards(int b[], int n, int player[], int *size)
{
int i;
for(i = n-1; i >= 0; i--)
delete_cards(b[i],player,size);
}
This will start deleting from the end of array.
As BLUEPIXY mentioned.
here is a pseudocode that you can work with:
given a sorted list, 1..n
for i = 2 up to length of list:
if list[i] is equal to list[i-1]:
shift the sublist [2..] 1 position to the left
else
increment i by 1
If you want to delete easily and efficiently without using loop you can use memcpy
#include <stdio.h>
#include <string.h>
#define INDEX_MAX 6
int main ()
{
int size = 10;
int src[] = {1,2,3,3,4,4,5,5,6,7};
int index[] = {2,3,4,5,6,7};
int x;
size = size - INDEX_MAX;
memcpy(src+2, src+8, sizeof(int)*(size-2));// - 2 since index 1 and 2 remains in the array
for(x = 0; x < size; x++){
printf("%d",src[x]);
}
return(0);
}

Make a program run linear in C

So based in the following problem from cumulative sum query I created the solution. But is any other way to solve the problem in C with linear complexity O(N)?
Problem description:
William Macfarlane wants to look at an array.
You are given a list of N numbers and Q queries. Each query is
specified by two numbers i and j; the answer to each query is the sum
of every number between the range [i, j] (inclusive).
Note: the query ranges are specified using 0-based indexing.
Input
The first line contains N, the number of integers in our list (N <=
100,000). The next line holds N numbers that are guaranteed to fit
inside an integer. Following the list is a number Q (Q <= 10,000). The
next Q lines each contain two numbers i and j which specify a query
you must answer (0 <= i, j <= N-1). Output
Output
For each query, output the answer to that query on its own line in the
order the queries were made.
Here is the solution:
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
struct node {
int first;
int last;
};
int sum_array(int *array, int first, int last) {
int sum = 0;
for (int i = first; i <= last; i++) {
sum += array[i];
}
return sum;
}
int main() {
FILE* input = fopen("share.in","r");
int N = 0;
fscanf(input,"%d",&N);
int *array = (int*)malloc(N * sizeof(int));
for (int i = 0; i < N; i++) {
fscanf(input,"%d",&array[i]);
}
int Q = 0;
fscanf(input,"%d",&Q);
struct node query[Q];
for (int i=0; i < Q; i++) {
fscanf(input,"%d",&query[i].first);
fscanf(input,"%d",&query[i].last);
}
fclose(input);
int sum = 0;
for ( int i = 0; i < Q ; i++) {
int first = query[i].first;
int last = query[i].last;
sum = sum_array(array,first,last);
printf("Number of queries : %d , sum is %d\n",i ,sum);
}
free(array);
return 0;
}
Update:
The answer given is good. But for some reason I couldn't make it work.
So here is the code rewritten and if someone can explain me what I do wrong I will be happy! Keep in mind we want the range to be [first,last]
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
struct node {
int first;
int last;
};
int sum_array(int *array, int first, int last) {
int sum = 0;
for (int i = first; i <= last; i++) {
sum += array[i];
}
return sum;
}
int main() {
FILE* input = fopen("share.in","r");
int N = 0;
fscanf(input,"%d",&N);
int *array = (int*)malloc(N * sizeof(int));
int *integralArray = (int*)malloc(N * sizeof(int));
for (int i = 0; i < N; i++) {
fscanf(input,"%d",&array[i]);
integralArray[i] = array[i] + ((i > 0) ? array[i-1] : 0);
}
int Q = 0;
fscanf(input,"%d",&Q);
struct node query[Q];
for (int i=0; i < Q; i++) {
fscanf(input,"%d",&query[i].first);
fscanf(input,"%d",&query[i].last);
}
fclose(input);
int sum = 0;
for (int i = 0; i < Q ; i++) {
int first = query[i].first;
int last = query[i].last;
sum = integralArray[last] - integralArray[first - 1];
printf("Number of queries : %d , sum is %d\n",i ,sum);
}
free(array);
return 0;
}
You'd form the integral array. Modify to something like:
int *array = (int*)malloc(N * sizeof(int));
int *integralArray = (int*)malloc(N * sizeof(int));
for (int i = 0; i < N; i++) {
fscanf(input,"%d",&array[i]);
integralArray[i] = array[i] + ((i > 0) ? integralArray[i-1] : 0);
}
So the element at integralArray[i] is the sum of all elements in array from 0 to i.
Then, to get the sum from a to b, where a > b, integralArray[b] is the sum from 0 to b and integralArray[a] is the sum from 0 to a so you can just compute integralArray[b] - integralArray[a] to get the total from a to b. Intuitively, integralArray[b] includes the numbers you want but it also includes the numbers up to and including a. You don't want those so you take them off again.
Vary appropriately for inclusion or exclusion of the number at a and the number at b. That as given will include the number at b but not that at a. You could adjust your integralArray to be one earlier (so integralArray[b] is the sum from 0 to b-1) or adjust your indices.

Resources