reshape 2D array into 3D array C - c

Hi I am trying to convert matlab code in C . While doing that I have to reshape a 2d array into 3d array. I tried to write a function which is given below. I took help from here.
#include <stdio.h>
#include <stdlib.h>
#define ZALLOC(item, n, type) if ((item = (type *)calloc((n), sizeof(type))) == NULL) \
fatalx("Unable to allocate %d unit(s) for item\n", n)
int i,j,k,x,y;
static void fatalx(const char *str, size_t n)
{
fprintf(stderr, "%s: %zu\n", str, n);
exit(1);
}
static int ***alloc_3d(int ar[][12],int rows, int cols,int levels)
{
int count = 0;
int ***array_3d;
ZALLOC(array_3d, levels, int **);
for (i = 0; i < levels; i++)
{
int **data;
ZALLOC(data, rows, int *);
array_3d[i] = data;
for (j = 0; j < rows; j++)
{
int *entries;
ZALLOC(entries, cols, int);
array_3d[i][j] = entries;
for (k = 0; k < cols; k++)
{
array_3d[i][j][k] = ar[i][j];
}
}
}
return array_3d;
}
static void print_3d(int ***a3d, int rows, int cols,int levels)
{
for (i = 0; i < levels; i++)
{
printf("%d:\n", i);
for (j = 0; j < rows; j++)
{
printf(" %d: ", j);
for (k = 0; k < cols; k++)
printf(" %3d", a3d[i][j][k]);
putchar('\n');
}
}
}
static void free_3d(int ***a3d, int levels, int rows)
{
for (i = 0; i < levels; i++)
{
for (j = 0; j < rows; j++)
free(a3d[i][j]);
free(a3d[i]);
}
free(a3d);
}
int main(void)
{
int ar[2][12]={
{1,2,3,4,5,6,7,8,9,10,11,12},
{13,14,15,16,17,18,19,20,21,22,23,24}
};
int d1 = 2;
int d2 = 3;
int d3 = 4;
int ***a3d = alloc_3d(ar,d1, d2, d3);
print_3d(a3d, d1, d2, d3);
free_3d(a3d, d3, d2);
return(0);
}
This not only giving me wrong values but also garbage values. Where matlab output for first slice is:
a3d(:,:,1) =
1 2 3
13 14 15
mine one is totally different with
0:
0: 1 1 1
1: 2 2 2
1:
0: 13 13 13
1: 14 14 14
2:
0: 1991011277 1991011277 1991011277
1: 4 4 4
3:
0: 1 1 1
1: 6630248 6630248 6630248
As you can see there is garbage value too. So my indexing is also wrong. Any idea how to properly do that?
Thanks in advance.

Your example does not, in fact, perform a reshaping, inasmuch as it creates a composite object of a completely different type from that of the original array.
A C multidimensional array is an array of arrays (of arrays ...). Among other significant characteristics, all of the elements of such an array are contiguous in memory. You can also construct an array of pointers, and initialize each pointer to point to an array of its own, etc.. Although these kinds of objects are superficially similar in that you can apply the indexing operator to to both in about the same way, it is important to understand that:
The array of pointers requires additional space for the pointers, above and beyond the space to which they point.
Although the pointers in an array of pointers are contiguous in memory, the arrays they point to very well might not be. That often leads to
wasted space if the system's page size does not evenly divide the pointed-to arrays' sizes, and
poorer performance for accessing array elements as a result of poorer locality of reference.
The array of pointers is messier and slower to allocate, because multiple separate calls are required to memory allocation functions.
The array of pointers is messier and slower to free, because all the pointed-to arrays must also be freed, separately.
On the other hand, the array of pointers accommodates "ragged" multi-dimensional pseudo-arrays, were the (pointed to) member arrays are not all the same length.
Here's how your program might look if written using standard C multidimensional arrays (== arrays of arrays):
#include <stdlib.h>
#include <stdio.h>
#define ALLOC(p, n) do { \
if (!((p) = calloc((n), sizeof(*(p))))) { \
fprintf(stderr, "Memory allocation failure\n"); \
exit(1); \
} \
} while (0)
void *reshape_2d_3d(size_t id1, size_t id2, int iar[][id2],
size_t od1, size_t od2, size_t od3) {
// oar is a pointer to a multidimensional array; in this case, it will
// point to the first element of an array of arrays (of arrays).
int (*oar)[od2][od3];
size_t size1 = id1 * id2;
size_t size2 = od1 * od2 * od3;
size_t min_size = (size1 <= size2) ? size1 : size2;
ALLOC(oar, od1);
// A loop nest could be used here, too, but I find this simpler for
// tracking the correspondence of array elements. It also better
// accommodates the case where the reshaped result has different overall
// size from the original.
for (size_t i = 0; i < min_size; i++) {
oar[i / (od2 * od3)][(i / od3) % od2][i % od3] = iar[i / id2][i % id2];
}
return oar;
}
void print_3d(size_t levels, size_t rows, size_t cols, int ar[][rows][cols]) {
for (int i = 0; i < levels; i++) {
printf("%d:\n", i);
for (int j = 0; j < rows; j++) {
printf(" %d: ", j);
for (int k = 0; k < cols; k++) {
printf(" %3d", ar[i][j][k]);
}
putchar('\n');
}
}
}
int main(void) {
int ar[2][12] = {
{1,2,3,4,5,6,7,8,9,10,11,12},
{13,14,15,16,17,18,19,20,21,22,23,24}
};
int d1 = 2, d2 = 3, d3 = 4;
int (*a3d)[d2][d3] = reshape_2d_3d(2, 12, ar, d1, d2, d3);
print_3d(d1, d2, d3, a3d);
// A single, simple free() is all that's needed
free(a3d);
}
Output:
0:
0: 1 2 3 4
1: 5 6 7 8
2: 9 10 11 12
1:
0: 13 14 15 16
1: 17 18 19 20
2: 21 22 23 24
Note that that uses variable-length arrays, but without the usual concern about stack allocation. It therefore requires a conforming C99 compiler, or a conforming C2011 compiler that implements the VLA optional (in that version) feature.

Your way of assigning the numbers to the newly allocated memory is wrong.
static int ***alloc_3d(int ar[][12],int rows, int cols,int levels,int colsize)
{
int count = 0;
int ***array_3d;
ZALLOC(array_3d, levels, int **);
int i1=0,j1=0;
for (i = 0; i < levels; i++)
{ ...
...
for (k = 0; k < cols; k++)
{
array_3d[i][j][k] = ar[i1][j1++];
if( j1 == colsize) i1++,j1=0;
}
}
}
return array_3d;
}
Call like this
int colsize = 12;
int ***a3d = alloc_3d(ar,d1, d2, d3,colsize);
This prints:
0:
0: 1 2 3
1: 4 5 6
1:
0: 7 8 9
1: 10 11 12
2:
0: 13 14 15
1: 16 17 18
3:
0: 19 20 21
1: 22 23 24
A small note - earlier your code had undefined behavior accessing array index out of the bound.

Related

Converting a 2D array into an array of same size, but shaped as a 1D?

I am trying to convert the multidimensional array table into a monodimensional one without creating 2 monodimensional arrays. How can i do it?
#include <stdio.h>
#include <stdbool.h>
#define ROWS 100
#define COLUMNS 100
int matrice[ROWS][COLUMNS];
int i = 0;
int j = 0;
int rows = 0;
int columns = 0;
void table_creation(void);
void insert_data(void);
int main() {
table_creation();
i = 0;
j = 0;
while (i < rows) {
j = 0;
while (j < columns) {
printf("\t%d ", table[i][j]);
j++;
}
printf("\n");
i++;
}
i = 0;
j = 0;
while (i < rows) {
j = 0;
while (j < columns) {
printf("\t%d ", table[i][j]);
j++;
}
printf("\n");
i++;
}
}
void table_creation(){
i = 0;
j = 0;
do{
printf("How many rows do you want?\n");
scanf("%d", &rows);
} while (rows > ROWS || rows < 0);
do{
printf("How many columns do you want?\n");
scanf("%d", &columns);
} while (columns > COLUMNS || columns < 0);
insert_data();
}
void insert_data(){
i = 0;
j = 0;
printf("\nInsert numbers\n");
while (i < rows) {
j = 0;
printf("\n");
while (j < columns) {
printf("Insert the element in %d row and %d column\n", i ,j);
scanf("%d", &table[i][j]);
j++;
}
i++;
printf("\n");
}
}
The output of this function are the number that are in the table. Now i want the same output but with a monodimensional array
Addressing only the title question:
"convert the multidimensional array table into a monodimensional one without creating 2 monodimensional arrays. How can i do it?"
Creating a 1D to match size of a 2D is easily doable, and updating the new array with contents of 2D is just as easy. both can be done in a variety of ways. Following shows some of them:
Given the following, creating a 1D can be done in multiple ways:
#define ROWS 5 (using smaller sizes for illustration)
#define COLS 3
int main(void)
{
//simplest:
int matrix1[ROWS*COLS] = {1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
//less simple, using variable length array:
int matrice[ROWS][COLS];
size_t elements = sizeof(matrice)/sizeof(int);//give number of int memory locations in 2D matrix
//use count of int memory locations to create matrix of same size, but of different shape
int newMatrix[elements];//note VLA cannot be initialized
memset(newMatrix, 0, sizeof(newMatrix));//...its done in another statement.
return 0;
}
It is noteworthy that the memory layout for a 2D matrix such as int matrice[ROWS][COLS]; is laid out in memory identically to that of a 1D matrix of the same size, eg int matrice[ROWS*COLS];. Both exist in memory as 1 contiguous and sequential block. So matrix1, matrice and newMatrix all conceptually look like this:
| | | | | | | | | | | | | | | |
Rows 0 1 2 3 4 (each with 3 columns)
Also good to about pointer offset notation i.e. that the expression *(newMatrix + 0) = val; is equivalent to normal array notation: newMatrix[0] = val;
And using this arrangement, and pointer offset notation, a 2D can be easily copied into a 1D using this, which copies 2D matrix1 to 1D newMatrix:
for(int i=0;i<elements;i++)
{
*(newMatrix + i) = *(matrix1 +i);
}
But even easier is to use memcpy();. The following copies 2D matrix1 to 1D newMatrix
memcpy(newMatrix, matrix1, elements*sizeof(int));
Each of these methods copy the contents of memory with exactly the same result, i.e. in the same order, row 0 appended by row 1, ..., appended by row 4
Note, VLAs are available in compilers compliant with C99, and optional other others since C99. More on variable length array

Why is this function not returning the right number of common elements between the arrays?

I was solving an exercise which goes as follows:
Create a function with the following prototype int commons(int a[], int na, int b[], int nb) which returns how many numbers the array a (of size na) has in common with array b (of size nb). The arrays are not ordered.
The exercise does not say what is supposed to happen when there is more than one number which is the same but for I want to suppose that if there is an array a = { 1, 2, 2 } and array b = { 2, 0, 5} they only have one element in common which is 2 (occurring once).
In order to solve this, I decided to loop through each number of the first array and check if it belong to the second, if it does, I'm gonna get the position and shift the second array in order to "remove" it from the array. However I had to make some adjustments, as once I shift it, the last item will be repeated so I chose to decrement the len variable. However I am getting unexpected results. The website which I'm testing at
int index_occur(int x, int a[], int N) {
int i;
for (i = 0; i < N && a[i] != x; i++);
return (i == N ? -1 : i);
}
int common(int a[], int na, int b[], int nb) {
int i, c = 0, j, index;
int len = nb;
for (i = 0; i < na; i++) {
if ((index = index_occur(a[i], b, len)) != -1) {
for (j = index; j < nb - 1; j++)
b[j] = b[j + 1];
len--;
c++;
}
}
return c;
}
When I test on the website with the following arrays, here is what I get:
Inputs: [ 1 12 13 14 5 16 17 18 19 110 111 12 113 114 115 116 117 18 119 12 ]
[ 2 3 4 1 20 21 22 23 5 126 127 129 132 132 132 132 132 132 132 132 ]
Output: expected 2 obtained 7
I don't understand what is wrong with my code, for it not to produce the desired results.
There must be something wrong in your testing code. I ran your functions with the posted arrays and I did get the expected output 2.
Your code seems to work, but are some remarks:
your inner loop should stop at len - 1 instead of nb - 1.
you modify the b array and effectively destroy its contents. You might want to move the found elements to the end so the array is just shuffled but still contains the same numbers.
If you are going to modify the arrays, you might as well sort them and use a linear scan to count the elements in common.
Here is an alternative that does not modify the arrays:
int count_occurrences(int x, const int a[], int n) {
int count = 0, i;
for (i = 0; i < n; i++) {
if (a[i] == x)
count++;
}
return count;
}
int common(const int a[], int na, const int b[], int nb) {
int i, count = 0;
for (i = 0; i < na; i++) {
int n = count_occurrences(a[i], b, nb);
if (n > 0 && n > count_occurrences(a[i], a, i - 1))
count++;
}
return count;
}

Reordering the rows in a matrix in a specific order

I am successfully storing the calculated subsets in a 2-D array matrix in C language.Now I want to print the subsets in an order desired.
For eg.
2-D array matrix is
10 7 3 2 1
10 7 5 1
7 6 5 3 2
10 6 5 2
10 7 6
Desired Output
10 7 6
10 7 5 1
10 7 3 2 1
10 6 5 2
7 6 5 3 2
How quick sort can be applied to sort/order these rows?
As #chqrlie noted, this can be easily solved with qsort.
Depending on the way the matrix is declared (is it an array of pointers to arrays of ints? do all arrays have the same length? is it a global array of fixed size?) the code will have to do slightly different things.
So, assuming the array is a global variable and all rows have same length (padded with 0s):
MWE:
#include <stdio.h>
#include <stdlib.h>
/*
Compare 2 integers
returns:
-1 if *i1 < *i2
+1 if *i1 > *i2
0 if *i1 == *i2
*/
int intcmp(const int *i1, const int *i2)
{
return (*i2 < *i1) - (*i1 < *i2);
}
#define ROWS 5
#define COLS 5
/*
Assumes rows already sorted in descending order
NOTE: qsort calls the comparison function with pointers to elements
so this function has to be tweaked in case the matrix is an array of
pointers. In that case the function's declaration would be:
int rowcmp(int **pr1, int **pr2)
{
const int *r1 = *pr1;
const int *r2 = *pr2;
// the rest is the same
}
*/
int rowcmp(const int *r1, const int *r2)
{
int i = 0, cmp;
do {
cmp = intcmp(&r1[i], &r2[i]);
i++;
} while (i < COLS && cmp == 0);
return -cmp; /* return -cmp to sort in descending order */
}
int data[5][5] = {
{10,7,3,2,1},
{10,7,5,1,0},
{ 7,6,5,3,2},
{10,6,5,2,0},
{10,7,6,0,0}
};
void printmatrix()
{
int i, j;
for (i = 0; i < ROWS; i++) {
for (j = 0; j < COLS; j++) {
printf("%d ", data[i][j]); /* leaves a trailing space in each row */
}
printf("\n");
}
}
int main()
{
printmatrix();
qsort(data, 5, sizeof(data[0]), (int (*)(const void *, const void *))rowcmp);
printf("\n");
printmatrix();
return 0;
}
For the most flexible solution, I would define
struct row {
size_t len;
int *elems;
};
struct matrix {
struct row *rows;
size_t nrows;
};
and change the code accordingly.
NOTE: code not thoroughly tested, use with caution ;)
First of all, are you sure that the 1 on row 3,col 5 should be there and not on the last line?
Anyway, an efficient way to achieve what you want is:
compute the frequency array
declare a new matrix
go from the highest element (10 in your case) from frequency array and put in your matrix using your desired format.
It is time-efficient because you don't use any sorting algorithm, thus you don't waste time there.
It is NOT space-efficient because you use 2 matrices and 1 array, instead of only 1 matrix as suggested in other posts, but this should not be a problem, unless you use matrices of millions of rows and columns
C code for frequency array:
int freq[11] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
for(int i=0; i<NO_ROWS; i++) {
for(int j=0; j<NO_COLS; j++) {
if(MATRIX[i][j]!=null && MATRIX[i][j]>0 && MATRIX[i][j]<11) {
freq[MATRIX[i][j]]++;
}
}
}
C code for computing the new matrix dimensions
(assuming you want to keep the number of rows)
OUTPUT_MATRIX[100][100] /*I declared it statically, but I would advise to make it dinamically */
/* first, compute the number columns.
To do so, we need the number of elements
(we get them by simply summing up frequency array's elements) */
int s=0;
for(int i=0; i<11; i++) {
s+=frequency[i];
}
int addOne = 0 /* boolean value to check if we will have to add one extra column for safety */
if(s % NO_ROWS) {
addOne = 1; /* division is not even, so we will have to add extra column */
}
NO_COLS = s/NO_ROWS + addOne;
Now, final part, assigning the values from frequency array to the OUTPUT_MATRIX
int k=0;
int currentNumber = 10; /* assigning starts from 10 */
for(int i=0; i<NO_ROWS; i++) {
for(int j=0; j<NO_COLS; j++) {
if(currentNumber>0) {
if(frequency[currentNumber]==0 || k>=frequency[currentNumber]) {
currentNumber--;
k=0;
}
OUTPUT_MATRIX[i][j] = frequency[currentNumber];
k++;
} else {/*here, you can assign the rest of the value with whatever you want
I will just put 0's */
OUTPUTMATRIX[i][j] = 0;
}
}
}
Hope this helps!
This is what I do in C++ to reorder a matrix:
// b is the matrix and p is an array of integer containing the desired order of rows
for(i=0; i<n; i++){
if( p[i]==i )
continue;
b[i].swap(b[p[i]]);
j = p[i]; // New row i position
// Update row i position to new one
for(int k=i+1; k<n; k++){
if( p[k] == i )
p[k] = j;
}
printRow( b[i] );
}
You need to define an array of pointers of the data type you use and then you can reorder your matrix.
for example your matrix is: arr[5][10], and you want to print line 4 before line 3:
int *[5] arr2;
arr2[0] = &arr[0][0];
arr2[1] = &arr[1][0];
arr2[2] = &arr[2][0];
arr2[3] = &arr[4][0];
arr2[4] = &arr[3][0];
in regard to how will the ordering algorithm work, i would suggest placing a header in the start of each array in the matrix which will tell you how many elements it has(basically the first element of each array can be a counter of the total elements) afterwards you can order the strings by comparing the header, and if it is equal comparing the first element and so on. this can be done in a loop that iterates as many times as there are elements in the array, when the elements are not equal, break out of the loop.
hope this helps.

Rearranging an array with respect to another array

I have 2 arrays, in parallel:
defenders = {1,5,7,9,12,18};
attackers = {3,10,14,15,17,18};
Both are sorted, what I am trying to do is rearrange the defending array's values so that they win more games (defender[i] > attacker[i]) but I am having issues on how to swap the values in the defenders array. So in reality we are only working with the defenders array with respect to the attackers.
I have this but if anything it isn't shifting much and Im pretty sure I'm not doing it right. Its suppose to be a brute force method.
void rearrange(int* attackers, int* defenders, int size){
int i, c, j;
int temp;
for(i = 0; i<size; i++){
c = 0;
j = 0;
if(defenders[c]<attackers[j]){
temp = defenders[c+1];
defenders[c+1] = defenders[c];
defenders[c] = temp;
c++;
j++;
}
else
c++;
j++;
}
}
Edit: I did ask this question before, but I feel as if I worded it terribly, and didn't know how to "bump" the older post.
To be honest, I didn't look at your code, since I have to wake up in less than 2.30 hours to go to work, hope you won't have hard feelings for me.. :)
I implemented the algorithm proposed by Eugene Sh. Some links you may want to read first, before digging into the code:
qsort in C
qsort and structs
shortcircuiting
My approach:
Create merged array by scanning both att and def.
Sort merged array.
Refill def with values that satisfy the ad pattern.
Complete refilling def with the remaining values (that are
defeats)*.
*Steps 3 and 4 require two passes in my approach, maybe it can get better.
#include <stdio.h>
#include <stdlib.h>
typedef struct {
char c; // a for att and d for def
int v;
} pair;
void print(pair* array, int N);
void print_int_array(int* array, int N);
// function to be used by qsort()
int compar(const void* a, const void* b) {
pair *pair_a = (pair *)a;
pair *pair_b = (pair *)b;
if(pair_a->v == pair_b->v)
return pair_b->c - pair_a->c; // d has highest priority
return pair_a->v - pair_b->v;
}
int main(void) {
const int N = 6;
int def[] = {1, 5, 7, 9, 12, 18};
int att[] = {3, 10, 14, 15, 17, 18};
int i, j = 0;
// let's construct the merged array
pair merged_ar[2*N];
// scan the def array
for(i = 0; i < N; ++i) {
merged_ar[i].c = 'd';
merged_ar[i].v = def[i];
}
// scan the att array
for(i = N; i < 2 * N; ++i) {
merged_ar[i].c = 'a';
merged_ar[i].v = att[j++]; // watch out for the pointers
// 'merged_ar' is bigger than 'att'
}
// sort the merged array
qsort(merged_ar, 2 * N, sizeof(pair), compar);
print(merged_ar, 2 * N);
// scan the merged array
// to collect the patterns
j = 0;
// first pass to collect the patterns ad
for(i = 0; i < 2 * N; ++i) {
// if pattern found
if(merged_ar[i].c == 'a' && // first letter of pattern
i < 2 * N - 1 && // check that I am not the last element
merged_ar[i + 1].c == 'd') { // second letter of the pattern
def[j++] = merged_ar[i + 1].v; // fill-in `def` array
merged_ar[i + 1].c = 'u'; // mark that value as used
}
}
// second pass to collect the cases were 'def' loses
for(i = 0; i < 2 * N; ++i) {
// 'a' is for the 'att' and 'u' is already in 'def'
if(merged_ar[i].c == 'd') {
def[j++] = merged_ar[i].v;
}
}
print_int_array(def, N);
return 0;
}
void print_int_array(int* array, int N) {
int i;
for(i = 0; i < N; ++i) {
printf("%d ", array[i]);
}
printf("\n");
}
void print(pair* array, int N) {
int i;
for(i = 0; i < N; ++i) {
printf("%c %d\n", array[i].c, array[i].v);
}
}
Output:
gsamaras#gsamaras:~$ gcc -Wall px.c
gsamaras#gsamaras:~$ ./a.out
d 1
a 3
d 5
d 7
d 9
a 10
d 12
a 14
a 15
a 17
d 18
a 18
5 12 18 1 7 9
The problem is that you are resetting c and j to zero on each iteration of the loop. Consequently, you are only ever comparing the first value in each array.
Another problem is that you will read one past the end of the defenders array in the case that the last value of defenders array is less than last value of attackers array.
Another problem or maybe just oddity is that you are incrementing both c and j in both branches of the if-statement. If this is what you actually want, then c and j are useless and you can just use i.
I would offer you some updated code, but there is not a good enough description of what you are trying to achieve; I can only point out the problems that are apparent.

Getting incorrect output when I implement merge sort with threads, can't figure out what's wrong

I've been at this problem for like 3 days and I've combed my entire code to try to figure out why I'm getting incorrect output. The purpose of this program is to do a merge sort using threads. The first part is simply sorting the elements in parallel into however many segments a user inputs. The only inputs tested will be 2, 5, and 10. And the array to be sorted will always be 50 int array of randomly generated numbers.My code works fine when the segments entered (denoted by the variable 'segments' at the top of main) is 2. However, when I change segments to 5 or 10, I don't get a sorted array at the end. I've tried debugging by using print statements (which I've commented out but you can still see) and there seems to be a problem during the first two merge iterations. For some reason the resulting of those merge iterations are not in order, and they contain duplicate numbers that don't exist in duplicate in the original array passed to it. My sorting method and merging methods work fine when I just pass arrays to them, and don't use threads but when I do use threads I get behavior that I can't explain. Below is my program in its entirety, to merge an array of 50 it should do the following:
split the array into 10 segments of 5, and sort each segment.
pass the segments in pairs, in rounds. So round one should pas segment 0-5 in one segment and 5-10 in another, 10-15 and 15-20, 20-25 and 25-30, and so on until it reaches 40-45 and 45-50.
then it will go into round two which does same thing as round one but it passes the segments in pairs of 10. So 0-10 and 10-20, 20-30 and 30-40, then it leaves the last part of 10 untouched
round three passes the segments to merge in pairs of 20: 0-20 and 20-40, then stops.
Finally it should merge the segments 0-40 with 40-50.
My program: (you should mainly focus on my main function, sort is fine, and merge seems fine too, but i've included them anyways just in case)
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <pthread.h>
/**
* Struct to contain an array partition as well as the size of the partition.
* Use struct to pass multiple parameters to pthread_create
*/
struct array_struct{
int *partition;
int size;
};
/**
* Struct that contains two arrays (should be sorted) to merge
* Use struct to pass multiple parameters to pthread_create
*/
struct arrays_to_merge{
int *array1;
int *array2;
int size1;
int size2;
};
//comparison function to use with qsort, sorts in ascending order
int cmpfunc (const void * a, const void * b)
{
return ( *(int*)a - *(int*)b );
}
/**
* Method that takes a struct containing a pointer to the first int in an array
* partition, as well as the partition size. Object must be type void, used with pthread_create
* #param pointer to the partition object. Type void
*/
void *sort(void *object){
struct array_struct *structure;
structure = (struct array_struct *) object;
int *array = structure->partition;
int size = structure->size;
int *i, j = 0;
qsort(array, size, sizeof(int), cmpfunc);
printf("Sorted %d elements.\n", size);
}
void *merge(void * object){
struct arrays_to_merge *arrays_struct;
arrays_struct = (struct arrays_to_merge *) object;
int *array1 = arrays_struct->array1;
int *array2 = arrays_struct->array2;
int size1 = arrays_struct->size1;
int size2 = arrays_struct->size2;
int tempArray[size1 + size2];
int i = 0, j = 0, k = 0, duplicates = 0;
while (i < size1 && j < size2) {
// printf("Merge number : %d Comparing %d and %d\n", mergenumber, array1[i], array2[j]);
if (array1[i] <= array2[j]) {
// printf("Picking %d\n", array1[i]);
tempArray[k] = array1[i];
if (array1[i] == array2[j])
{
duplicates++;
}
i++;
k++;
}else {
// printf("Merge number : %d Picking %d\n", mergenumber, array2[j]);
tempArray[k] = array2[j];
k++;
j++;
}
}
while (i < size1) {
// printf("Merge number : %d left over Picking %d\n", mergenumber, array1[i]);
tempArray[k] = array1[i];
i++;
k++;
}
while (j < size2) {
// printf("Merge number : %d left over Picking %d\n", mergenumber, array2[j]);
tempArray[k] = array2[j];
k++;
j++;
}
array1 = arrays_struct->array1;
for(i = 0; i < size1 + size2; i++){
array1[i] = tempArray[i];
}
printf("Merged %d and %d elements with %d duplicates\n", size1, size2, duplicates);
}
//return an array of size 50 with randomly generated integers
int *randomArray(){
srand(time(NULL));
static int array[50];
int i;
for (i = 0; i < 50; ++i){
array[i] = rand() % 51;
}
return array;
}
int main(int argc, char const *argv[])
{
int segments = 10;//make equal to argv input after testing
pthread_t threads[segments];
int i, *numbers; //iterator i, and pointer to int array 'numbers'
numbers = randomArray(); //return an array of random ints and store in 'numbers'
struct array_struct array[segments];
for(i = 0; i < segments; i++){
int *partition = numbers + (i * (50/segments));//obtain the first index of partition
array[i].partition = partition;
array[i].size = 50/segments;
pthread_create(&threads[i], NULL, sort, (void *) &array[i]);
}
for(i = 0; i < segments; i++){
pthread_join(threads[i], NULL);
}
int count = segments;
struct arrays_to_merge arrays[segments];
int j;
int size = 50/ segments;
while(count > 1){
for(i = 0, j = 0; i < count-1; j++, i += 2){
int *partition = numbers + (i * (size));
int *partition2 = numbers + (i+1 * (size));
arrays[j].array1 = partition;
arrays[j].array2 = partition2;
arrays[j].size1 = size;
arrays[j].size2 = size;
pthread_create(&threads[j], NULL, merge, (void *) &arrays[j]);
}
for(i = 0; i < j; i++){
pthread_join(threads[i], NULL);
}
size = size * 2;
count = count/2;
}
if(segments != 2){//for segments = 2, no need for his
int *partition = numbers;
int *partition2 = numbers + (size);
arrays[0].array1 = partition;
arrays[0].array2 = partition2;
arrays[0].size1 = size;
arrays[0].size2 = 50 - size;
pthread_create(&threads[0], NULL, merge, (void *) &arrays[0]);
pthread_join(threads[0], NULL);
}
for(i = 0; i < 50; i++){
printf("%d\n", numbers[i]);
}
pthread_exit(NULL);
return 0;
}
this is my output:
Sorted 5 elements.
Sorted 5 elements.
Sorted 5 elements.
Sorted 5 elements.
Sorted 5 elements.
Sorted 5 elements.
Sorted 5 elements.
Sorted 5 elements.
Sorted 5 elements.
Sorted 5 elements.
Merged 5 and 5 elements with 0 duplicates
Merged 5 and 5 elements with 0 duplicates
Merged 5 and 5 elements with 0 duplicates
Merged 5 and 5 elements with 0 duplicates
Merged 5 and 5 elements with 0 duplicates
Merged 10 and 10 elements with 3 duplicates
Merged 10 and 10 elements with 1 duplicates
Merged 20 and 20 elements with 7 duplicates
Merged 40 and 10 elements with 17 duplicates
0
6
9
11
12
13
13
14
15
17
19
23
25
25
25
26
26
28
28
28
28
30
32
32
32
34
39
41
41
44
44
44
44
44
50
50
9
15
50
9
15
19
26
50
50
9
15
11
14
50
Sorry for the long wall of text, I've tried resolving this on my own and after countless hairs pulled I can't figure it out. Please help me figure out what I'm doing wrong. I think my problem lies in either the way I'm joining threads, or my merge function but since I cant be sure, i just included the whole thing.
It took a while but finally I got there :)
The problem is with this line:
int *partition2 = numbers + (i+1 * (size));
which is equivalent to (due to operator precedence).
int *partition2 = numbers + (i + size);
and is not what you want.
It should be:
int *partition2 = numbers + ((i+1) * (size));
Notice the additional brackets. Without which, the partition2 index is calculated incorrectly. Hence, merging with different parts of the array.

Resources