Related
**I have 3 arrays a[1...n] b[1...n] c[1....n] which contain integers.
It is not mentioned if the arrays are sorted or if each array has or has not duplicates.
The task is to check if there is any common number in the given arrays and return true or false.
For example : these arrays a=[3,1,5,10] b=[4,2,6,1] c=[5,3,1,7] have one common number : 1
I need to write an algorithm with time complexity O(n^2).
I let the current element traversed in a[] be x, in b[] be y and in c[] be z and have following cases inside the loop : If x, y and z are same, I can simply return true and stop the program,something like:
for(x=1;x<=n;x++)
for(y=1;y<=n;y++)
for(z=1;z<=n;z++)
if(a[x]==b[y]==c[z])
return true
But this algorithm has time complexity O(n^3) and I need O(n^2).Any suggestions?
There is a pretty simple and efficient solution for this.
Sort a and b. Complexity = O(NlogN)
For each element in c, use binary search to check if it exists in both a and b. Complexity = O(NlogN).
That'll give you a total complexity of O(NlogN), better than O(N^2).
Create a new array, and save common elements in a and b arrays. Then find common elements in this array with c array.
python solution
def find_d(a, b, c):
for i in a:
for j in b:
if i==j:
d.append(i)
def findAllCommon(c, d):
for i in c:
for j in d:
if i==j:
e.append(i)
break
a = [3,1,5,10]
b = [4,2,6,1]
c = [5,3,1,7]
d = []
e = []
find_d(a, b, c)
findAllCommon(c, d)
if len(e)>0:
print("True")
else:
print("False")
Since I haven't seen a solution based on sets, so I suggest looking for how sets are implemented in your language of choice and do the equivalent of this:
set(a).intersection(b).intersection(c) != set([])
This evaluates to True if there is a common element, False otherwise. It runs in O(n) time.
All solutions so far either require O(n) additional space (creating a new array/set) or change the order of the arrays (sorting).
If you want to solve the problem in O(1) additional space and without changing the original arrays, you indeed can't do better than O(n^2) time:
foreach(var x in a) { // n iterations
if (b.Contains(x) && c.Contains(x)) return true; // max. 2n
} // O(n^2)
return false;
A suggestion:
Combine into a single array(z) where z = sum of the entries in each array. Keep track of how many entries there were in Array 1, Array 2, Array 3.
For each entry Z traverse the array to see how many duplicates there are within the combined array and where they are. For those which have 2 or more (ie there are 3 or more of the same number), check that the location of those duplicates correspond to having been in different arrays to begin with (ruling our duplicates within the original arrays). If your number Z has 2 or more duplicates and they are all in different arrays (checked through their position in the array) then store that number Z in result array.
Report result array.
You will traverse the entire combined array once and then almost (no need to check if Z is a duplicate of itself) traverse it again for each Z, so n^2 complexity.
Also worth noting that the time complexity will now be a function of total number of entries and not of number of arrays (your nested loops would become n^4 with 4 arrays - this will stay as n^2)
You could make it more efficient by always checking the already found duplicates before checking for a new Z - if the new Z is already found as a duplicate to an earlier Z you need not traverse to check for that number again. This will make it more efficient the more duplicates there are - with few duplicates the reduction in number of traverses is probably not worth the extra complexity.
Of course you could also do this without actually combining the values into a single array - you would just need to make sure that your traversing routine looks through the arrays and keeps track of what it finds the in the right order.
Edit
Thinking about it, the above is doing way more than you want. It would allow you to report on doubles, quads etc. as well.
If you just want triples, then it is much easier/quicker. Since a triple needs to be in all 3 arrays, you can start by finding those numbers which are in any of the 2 arrays (if they are different lengths, compare the 2 shortest arrays first) and then to check any doublets found against the third array. Not sure what that brings the complexity down to but it will be less than n^2...
there are many ways to solve this here few selected ones sorted by complexity (descending) assuming n is average size of your individual arrays:
Brute force O(n^3)
its basicaly the same as you do so test any triplet combination by 3 nested for loops
for(x=1;x<=n;x++)
for(y=1;y<=n;y++)
for(z=1;z<=n;z++)
if(a[x]==b[y]==c[z])
return true;
return false;
slightly optimized brute force O(n^2)
simply check if each element from a is in b and if yes then check if it is also in c which is O(n*(n+n)) = O(n^2) as the b and c loops are not nested anymore:
for(x=1;x<=n;x++)
{
for(ret=false,y=1;y<=n;y++)
if(a[x]==b[y])
{ ret=true; break; }
if (!ret) continue;
for(ret=false,z=1;z<=n;z++)
if(a[x]==c[z])
{ ret=true; break; }
if (ret) return true;
}
return false;
exploit sorting O(n.log(n))
simply sort all arrays O(n.log(n)) and then just traverse all 3 arrays together to test if each element is present in all arrays (single for loop, incrementing the smallest element array). This can be done also with binary search like one of the other answers suggest but that is slower still not exceeding n.log(n). Here the O(n) traversal:
for(x=1,y=1,z=1;(x<=n)&&(y<=n)&&(z<=n);)
{
if(a[x]==b[y]==c[z]) return true;
if ((a[x]<b[y])&&(a[x]<c[z])) x++;
else if ((b[y]<a[x])&&(b[y]<c[z])) y++;
else z++;
}
return false;
however this needs to change the contents of arrays or need additional arrays for index sorting instead (so O(n) space).
histogram based O(n+m)
this can be used only if the range of elements in your array is not too big. Let say the arrays can hold numbers 1 .. m then you add (modified) histogram holding set bit for each array where value is presen and simply check if value is present in all 3:
int h[m]; // histogram
for(i=1;i<=m;i++) h[i]=0; // clear histogram
for(x=1;x<=n;x++) h[a[x]]|=1;
for(y=1;y<=n;y++) h[b[y]]|=2;
for(z=1;z<=n;z++) h[c[z]]|=4;
for(i=1;i<=m;i++) if (h[i]==7) return true;
return false;
This needs O(m) space ...
So you clearly want option #2
Beware all the code is just copy pasted yours and modified directly in answer editor so there might be typos or syntax error I do not see right now...
Let's say I have x sets of objects, and each set has a certain number objects. I want create an array which will store all the unique "and" combinations of these objects.
For example, if I have 5 objects in set A, 10 objects in set B, and 8 objects in set C, then I know that there are 5*10*8 = 400 unique ways of picking one object from each set. But I want to actually store these combinations in an array.
So the array would be multidimensional, something like:
{
{ a, a, a }
{ a, a, b }
{ a, a, c }
...
{ a, b, a }
{ a, b, b }
and so on...
}
I need the solution to as efficient as possible, because I am dealing with situations where there are potentially tens of millions of combinations. I am not exactly sure how to begin to approach this problem.
Sorry if it's not clear, but I don't really know what to call what I am trying to achieve, so I am just describing it as best I can. Thank you for any help you can provide.
Edit: Here is some more information about the problem:
The purpose of this problem is that I am going to compute a "score" value from each resulting array. Then, I want to find the top n scores and return them to the user. So actually, I believe that I wouldn't need to have the entire array in memory. I can just iterate through the array, calculate the score, and add it to the returned array if its score is high enough. That way, I only need the top n objects in memory continuously.
I hope this makes things more clear.
Quick python, probably can't get much more efficient, since you need to iterate at some point...
getItems(A, B, C):
for a in A:
for b in B:
for c in C:
items = (a, b, c) ## or [a, b, c], as desired
yield items
Or, if you're familiar with generator expressions:
gen = ((a, b, c) for a in A for b in B for c in C)
Then to use:
for combo in getItems(A, B, C): ## or for combo in gen:
## do stuff here
Edit:
def getItems(*allSets):
if len(allSets) == 0:
yield []
return
thisSet, theRest = allSets[0], allSets[1:]
for value in thisSet:
for values in getItems(*theRest):
yield [value] + values
Do you know the number of sets at design-time? If so, I would do nested for loops. If you don't know the number of sets, then you're potentially do some form of recursion to handle the looping.
With that said, I think what you're doing is, by definition, NOT efficient. Is there a reason you need to store all the possible combinations in memory, rather than generating them as needed on the fly?
Lets say these are the start arrays:
[a,b,c]
[d]
[e,f]
What algorithm could produce the following arrays?
[a,d,e]
[a,d,f]
[b,d,e]
[b,d,f]
[c,d,e]
[c,d,f]
The number of start arrays can vary.
Depends on language, but formally something like this (when you have 3 arrays as you specified)
for el1 in first_array do
for el2 in second_array do
for el3 in third_array do
begin
create new element in result array as [e1, el2, el3]
end
The simplest algorithm you can think of is the best you can have. As the answer is of size the multiplied dimensions of all the arrays not much of an improvement can be made here. I personally recommend using recursion as the number of arrays can not be too big without making the number of resulting arrays really huge.
Let there are k arrays of n1, n2... nk elements respectively.
Writing all combinations is very like to writing all numbers in mixed radix.
So, simply loop over all possible numbers from 0 to (n1n2...nk-1) and write it down in mixed radix representation with "digits" taken from your arrays - only two nested loops are required.
Another method is a graphical one, you start with the originals sets and you move their content clockwise and store the combinations. Example first rotate the last row, and after the last rotation move the last - 1 row (in this case d, but you can't rotate it) so you will rotate the first row. It's like the binary sum.
[a,b,c] [a,b,c]---->[b,c,a] [b,c,a]---->[c,a,b] [c,a,b]
[d] [d] [d] [d] [d] [d]
[e,f]------>[f,e]------>[e,f]------>[f,e]------>[e,f]------>[f,e]
PD: you will only save the first elements of each array always.
The proposed solution above has a big problem in the sense the developer needs to know the number of arrays ahead and create the number of loops accordingly.
The following solution in C# does it dynamically based on the number of arrays that you actually have and it's type agnostic:
static void Main(string[] args)
{
List<List<char>> masterListChar = new List<List<char>>();
List<char> c1 = new List<char>() { 'a', 'b', 'c' };
List<char> c2 = new List<char>() { 'd' };
List<char> c3 = new List<char>() { 'e', 'f'};
masterListChar.Add(c1);
masterListChar.Add(c2);
masterListChar.Add(c3);
//PrintCombinations(masterListInt);
PrintCombinations(masterListChar);
Console.ReadKey();
}
public static void PrintCombinations<T>(List<List<T>> masterArray)
{
T[] combination = new T[masterArray.Count];
WalkOnSubArray(combination, masterArray, 0);
}
public static void WalkOnSubArray<T>(T[] combination, List<List<T>> masterArray, int masterIndex)
{
List<T> currentArray = masterArray[masterIndex];
for (int i = 0; i < currentArray.Count; ++i)
{
combination[masterIndex] = currentArray[i];
if(masterIndex != masterArray.Count - 1)
WalkOnSubArray(combination, masterArray, masterIndex + 1);
else
Console.WriteLine(String.Join(",", combination));
}
}
Can someone please assist in how I would solve this problem; I need to figure out a way to eliminate like elements in multiple arrays in the best/quickest order in order to drive my array to 0 elements. I.E. if I had the following arrays:
'a {1,12,10,31}'
'b {12,21}'
'c {12,18,5,21}'
'd {12,18,21}'
I'd want to remove 12 -> 21 (b is done) then -> 18 (d is done)
This problem is really related to software incompatibilities... Any ideas would be helpful.
Thanks,
Pat
Well, it depends on how many is multiple arrays. If you only have two, you can sort them individually, and iterate over both at the same time in order, and remove.
However, this gets complicated quickly when you have an arbitrary number of arrays.
In this case, it is easiest to:
Put everything (merge) in a single array (named ARRAY)
Sort the array (ARRAY)
Iterate over the array (ARRAY), while removing elements occurring just once, and leaving a single copy of elements occurring multiple times
Then for each original array (eg. A, B, C, D), iterate over this original array (eg. A) along with ARRAY together, and remove elements in A that are also found in ARRAY.
For step 4., you probably want something like (written in pseudo-C code):
foreach (A = arrays [A, B, C, D]) { // for each original array
int j=0;
for (int i=0;i<A.size;i++) { // iterating over array A
// increase index j to iterate ARRAY (find closest # in ARRAY >= A[i])
while (j<ARRAY.size-1 && A[i]>ARRAY[j]) j++;
if (ARRAY[j]==A[i]) /* remove it */;
else /* keep it */;
}
}
I am currently working on a project for my algorithms class and am at a bit of a standstill. We were assigned to do improvements to merge sort, that was in the book, by implementing specific changes. I have worked fine through the first 2 changes but the 3'rd one is killer.
Merge sort, the one we are improving, copies the contents of the input array into the temporary array, and then copies the temporary array back into the input array. So it recursively sorts the input array, placing the two sorted halves into the temporary array. And then it merges the two halves in the temporary array together, placing the sorted sequence into the input array as it goes.
The improvement is that this double copying is wasteful can be done without. His hint is that: We can make it so that each call to Merge only copies in one direction, but the calls to Merge alternate the direction.
This is supposedly done by blurring the lines between the original and temporary array.
I am not really looking for code as I am confident that I can code this. I just have no idea what i'm supposed to be doing. The professor is gone for the day so I can't ask him until next week when I have his course again.
Has anyone done something like this before? Or can decipher and put it into laymans terms for me :P
The first improvement, simply has it use insertion sort whenever an Array gets small enough that it will benefit greatly, timewise, from doing so.
The second improvement stops allocating two dynamic arrays (the 2 halves that are sorted) and instead allocates 1 array of size n and that is what is used instead of the two dynamic arrays. That's that last one I did. The code for that is :
//#include "InsertionSort.h"
#define INSERTION_CUTOFF 250
#include <limits.h> // needed for INT_MAX (the sentinel)
void merge3(int* inputArray, int p, int q, int r, int* tempArray)
{
int i,j,k;
for (i = p; i <= r; i++)
{
tempArray[i] = inputArray[i];
}
i = p;
j = q+1;
k = p;
while (i <= q && j <= r)
{
if (tempArray[i] <= tempArray[j])
{
inputArray[k++] = tempArray[i++];
}
else
{
inputArray[k++] = tempArray[j++];
}
}
}//merge3()
void mergeSort3Helper(int* inputArray, int p, int r, int* tempArray)
{
if (r - p < INSERTION_CUTOFF)
{
insertionSort(inputArray,p,r);
return;
}
int q = (p+r-1)/2;
mergeSort3Helper(inputArray,p,q,tempArray);
mergeSort3Helper(inputArray,q+1,r,tempArray);
merge3(inputArray,p,q,r,tempArray);
}//mergeSort3Helper()
void mergeSort3(int* inputArray, int p, int r)
{
if (r-p < 1)
{
return;
}
if (r - p < INSERTION_CUTOFF)
{
insertionSort(inputArray,p,r);
return;
}
int* tempArray = malloc((r-p)+1*sizeof(int));
tempArray[r+1] = INT_MAX;
mergeSort3Helper(inputArray,p,r,tempArray);
// This version of merge sort should allocate all the extra space
// needed for merging just once, at the very beginning, instead of
// within each call to merge3().
}//mergeSort3()
The algorithm is like this:
A1: 7 0 2 9 5 1 4 3
A2: (uninitialized)
Step 1:
A1 : unchanged
A2: 0 7 2 9 1 5 3 4
Step 2:
A1: 0 2 7 9 1 3 4 5
A2: unchanged
Step 3:
A1: unchanged
A2: 0 1 2 3 4 5 7 9
This involves you copying only one way each time and follows the steps of mergesort. As your professor said, you blur the lines between the work array and the sorted array by alternating which is which, and only copying once things are sorted.
I suspect it would be difficult and ultimately unprofitable to avoid all copying. What you want to do instead is to avoid the copy you currently do with each merge.
Your current merge3(inputArray, p,q,r, tempArray) returns the merged result in its original array, which requires a copy; it uses its tempArray buffer only as a resource. In order to do better, you need to modify it to something like merge4(inputArray, p,q,r, outputArray), where the result is returned in the second buffer, not the first.
You will need to change the logic in mergeSort3Helper() to deal with this. One approach requires a comparable interface change, to mergeSort4Helper(inputArray, p,q,r, outputArray), such that it also yields its result in its second buffer. This will require a copy at the lowest (insertion sort) level, and a second copy in the top-level mergeSort4() if you want your final result in the same buffer it came in. However, it eliminates all other unnecessary copies.
Alternately, you could add a boolean parameter to mergeSort4Helper() to indicate whether you want the result returned in the first or second buffer. This value would alternate recursively, resulting in at most one copy, at the lowest level.
A final option might be to do the merging non-recursively, and alternate buffers at each pass. This would also result in at most one copy; however, I would expect the resulting access pattern to be inherently less cache-friendly than the recursive one.