Home > Software engineering >  Is it possible to improve this array intersection code
Is it possible to improve this array intersection code

Time:11-26

This is the minimum code to obtain arrays intersection without any repetition in the final array. Can it be improved ? I think it can't because it uses the minimum number of iteration thanks to the break in the inner loop and also that it can't be parallelized due to a critical section inside the if clause, am I wrong ? I tried to try this function and the Matlab one (intersect) with the same output and the latter is much faster, how is it possible ?

int intersection(int* array1, int* array2, int len1, int len2, int size) {
    int j, k, t, intersectC = 0;
    int* tmp = (int*)malloc(sizeof(int) * size);


    for (j = 0; j < len1; j  ) {
        for (k = 0; k < len2; k  ) {
            if (array1[j] == array2[k]) {
                    for (t = 0; t < intersectC; t  ) {
                        if (tmp[t] == array1[j]) {
                            break;
                        }
                    }
                    if (t == intersectC) {
                        tmp[intersectC  ] = array1[j];
                    }
                }
            }
        }

    free(tmp);
    return intersectC;
}

P.S. size is the greatest between len1 and len2

CodePudding user response:

It can be improved. You have O(n3). If you sort both arrays, you can get the intersection in O(max(len1, len2)). Sorting both of them is O(len*log(len)).

Other idea is to encode each array as an unordered set via binary decision diagrams. Doing intersections is linear time using BDD.

CodePudding user response:

Your algorithm is O(N3), which is insanely bad considering it can be done quickly in O(N).

The following sorts the arrays (using a base2 radix sort), and then uses an approach akin to merge sort to find the intersection of the sorted arrays.

(I used uint32_t. I leave it to you to adapt to int.)

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>


#define ARRAY_LEN(a) ( sizeof(a) / sizeof(*a) )

#define MALLOC(t, n) ( (t*)malloc(sizeof(t) * n) )
#define REALLOC(p, t, n) ( (t*)realloc(p, sizeof(t) * n) )


static void _sort_uint32s(uint32_t *a, size_t n, uint32_t mask) {
   if ( n <= 1 )
      return;

   uint32_t *p = a;
   uint32_t *q = a   n;
   while (1) {
      while (1) {
         if ( ( *p & mask ) != 0 )
            break;
         if (   p == q )
            goto DONE_GROUPING;
      }

      while (1) {
         if ( p == --q )
            goto DONE_GROUPING;
         if ( ( *q & mask ) == 0 )
            break;
      }

      uint32_t tmp = *p;
      *p = *q;
      *q = tmp;
   }

DONE_GROUPING:
   mask >>= 1;
   if ( !mask )
      return;

   if ( q > a )
      _sort_uint32s(a, q-a, mask);
   if ( q < a n )
      _sort_uint32s(q, a n-q, mask);
}


static void sort_uint32s(uint32_t *a, size_t n) {
   _sort_uint32s(a, n, 0x80000000);
}


static size_t min_size_t(size_t a, size_t b) {
   return a < b ? a : b;
}


// Returns 0 on success.
// Returns -1 and sets errno on error.
// Will modify (sort) a1 and a2.
// Note that *set_p == NULL is possible on success.
static int intersect(uint32_t *a1, size_t n1, uint32_t *a2, size_t n2, uint32_t **set_p, size_t *n_p) {
   size_t n = min_size_t(n1, n2);
   uint32_t *set = MALLOC(uint32_t, n);
   if (!set) {
      *set_p = NULL;
      *n_p   = 0;
      return -1;
   }

   sort_uint32s(a1, n1);
   sort_uint32s(a2, n2);

   n = 0;
   while ( n1 && n2 ) {
      if ( *a1 < *a2 ) {
         while ( --n1 && *(  a1) < *a2 );
      }
      else if ( *a2 < *a1 ) {
         while ( --n2 && *(  a2) < *a1 );
      }
      else {
         uint32_t v = *a1;
         set[n  ] = v;
         while ( --n1 && *(  a1) == v );
         while ( --n2 && *(  a2) == v );
      }
   }

   if ( !n ) {
      free(set);
      *set_p = NULL;
      *n_p   = 0;
      return 0;
   }

   uint32_t *tmp = REALLOC(set, uint32_t, n);
   *set_p = tmp ? tmp : set;
   *n_p   = n;
   return 0;
}


int main(void) {
   uint32_t a1[] = { 11, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 15 };
   size_t n1 = ARRAY_LEN(a1);

   uint32_t a2[] = { 12, 1, 5, 2, 2 };
   size_t n2 = ARRAY_LEN(a2);

   uint32_t *set;
   size_t n;
   if ( intersect(a1, n1, a2, n2, &set, &n) < 0 ) {
      perror(NULL);
      exit(1);
   }

   printf("Intersection:");
   for (size_t i=0; i<n;   i)
      printf(" %" PRIu32, set[i]);

   printf("\n");

   free(set);

   return 0;
}

CodePudding user response:

In the end, following comments to this question, I used the radix sort to sort the two arrays and a classic intersection algorithm with O(n m). Probably, this code is a bit slower than the one posted by @ikegami but much faster than the one in the question.

int getMax(int* arr, int n){
    int mx = arr[0];
    for (int i = 1; i < n; i  )
        if (arr[i] > mx)
            mx = arr[i];
    return mx;
}

void countSort(int* arr, int* output, int n, int exp){
    int i, count[10] = { 0 };

    for (i = 0; i < n; i  )
        count[(arr[i] / exp) % 10]  ;

    for (i = 1; i < 10; i  )
        count[i]  = count[i - 1];

    for (i = n - 1; i >= 0; i--) {
        output[count[(arr[i] / exp) % 10] - 1] = arr[i];
        count[(arr[i] / exp) % 10]--;
    }

    for (i = 0; i < n; i  )
        arr[i] = output[i];
}

void radixsort(int* arr, int n){
    int m = getMax(arr, n);
    int* output = (int*)malloc(sizeof(int) * n);

    for (int exp = 1; m / exp > 0; exp *= 10) {
        countSort(arr, output, n, exp);
    }

    free(output);
}

int intersection(int* arr1, int* arr2, int len1, int len2, int size){
    int t, intersectC = 0;
    int* tmp = (int*)malloc(sizeof(int) * size);

    radixsort(arr1, len1);
    radixsort(arr2, len2);

    int i = 0, j = 0;
    while (i < len1 && j < len2) {
        if (arr1[i] < arr2[j])
            i  ;
        else if (arr2[j] < arr1[i])
            j  ;
        else /* if arr1[i] == arr2[j] */
        {
            for (t = 0; t < intersectC; t  ) {
                if (tmp[t] == arr1[j]) {
                    break;
                }
            }
            if (t == intersectC) {
                tmp[intersectC  ] = arr1[j];
            }
            i  ;
        }
    }

    free(tmp);
    return intersectC;
}
  • Related