Related
Given a number N and a sorted array A, design an algorithm - using the Divide and Conquer approach - to check if there exist index i and index j such that A[i]*A[j] == N (return 1 if present, 0 if not).
I'm having a hard time proceeding in the required (recursive) way. I think I figured out only a part of one possible solution, but even there I'm not a 100% sure if it's correct: I thought that if the product between the first element and the central element of the array is greater than N, then the numbers I'm looking for (if present) are certainly in the first half of the array, so I can recursively call the function on that part, like so (I'm using C):
int productN(int A[], int i, int j, int N){
// missing base case
int m = (i+j)/2;
if(A[i]*A[m] > N){
return productN(A, i, m, N);
} else{
// do something else
}
}
int main(){
int A[]={1, 2, 3, 4, 5};
printf("%d\n", productN(A, 0, 4, 15)); // initial value for i and j are the first and last index of the array
return 0;
}
Apart from that, I'm stuck, I can't even think of a base case, so any help will be greatly appreciated, thanks.
Edit:
Based on your very helpful answers, using binary search, I think I got it:
int productN(int A[], int i, int j, int N){
int m = (i+j)/2; // central element of the current array
int x;
for(x=i; x<=m; x++){
if(N%A[x]==0 && binarySearch(A, m, j, N/A[x]))
return 1;
}
if(i!=j){
if(productN(A, i, m, N) || productN(A, m+1, j, N))
return 1;
}
return 0;
}
Is it good? Can it be better?
Edit: it's been a while now since I asked this question, but I wrote another solution, simplier to read. I'll leave it here in case anyone is interested.
int productN(int A[], int i, int j, int N, int size){
if(i==j){ // base case
if(N%A[i]==0)
return binarySearch(A, 0, size-1, N/A[i]);
else
return 0;
}
int m = (i+j)/2;
if((N%A[m])==0){
if(binarySearch(A, 0, size-1, N/A[i]))
return 1;
}
return productN(A, i, m, N, size) || productN(A, m+1, j, N, size);
}
Using Divide and Conquer, you can use an approach similar to merge sort algorithm. As the comments suggest, there are easier approaches. But if Divide and Conquer is a must, this should suffice.
(I'm not proficient in C, so I'll just write the algorithm)
def productN(arr):
x = len(arr)
left_half = arr[0:x/2]
right_half = arr[x/2:]
if productN(left_half) or productN(right_half):
return True
for i in left_half:
if N%i==0 and binary_search(right_half, N/i):
return True
return False
im learning C and i am a bit confused with array handling.
I have a task that asks me to sum up 2 arrays of non negative integers.
example array 1 = {1,2,3} , array 2 = {4,5,6} -> 123+456 = 579
I searched a bit for a solution on how to convert those arrays of integers to an integer, but didnt really get helpful information.
I ended up with a code:
#include <stdio.h>
int sum(int A[],int B[], int n){
int i,j,t,k;
for(i=0;i<n;i++){
t= t+A[i];
}
for(j=0;j<n;j++){
k= k+B[j];
}
return t+k;
}
int main()
{
int n = 3;
int a[n] = {1,2,3};
int b[n] = {4,5,6};
printf("%d",sum(a,b,n));
return 0;
}
But my result is 1225283 which of course is wrong.
I found a solution where people write something like "t= 10* t+A[i]" , i dont get where that "10* " comes from, but i tested it and then "t" gets "123" but if i try the same for "k" it doesnt work, returning "k" doesnt give me "456". I am a bit confused, whats the proper way of handling this problem?
Thanks for any help.
You're basically adding digits 1+2+3 instead of creating the number 123. Your code also has various other flaws, like uninitialized variables. Here is a working example:
int array2int(int A[], int n) {
int ret = 0;
for(int i=0, k=1; i<n; i++){
ret = ret + k * A[i];
k *= 10;
}
return ret;
}
int sum(int A[],int B[], int n){
return array2int(A, n) + array2int(B, n);
}
First of all, in sum function, you haven't initialized neighter t nor k but you keep summing them and use later, so every time your code is executed, you chould get different result.
On the other hand, in something like "t= 10 t+A[i]", 10 comes from basic math, where a number could be resolved as a10^0 + b10^1 +c*10^2 + .... + m * 10^n. As a result, starting from least significant digit, everytime you try to add new digit (from least to most significant), you need your multipliciant to be 10 times greater.
int sum(int A[],int B[], int n){
int i,j,t=0,k=0,ten=1;
for(i=n-1;i>=0;i--){
t += ten*A[i];
ten *= 10;
}
ten = 1; /* initialize again*/
for(j=n-1;j>=0;j--){
k += ten*B[j];
ten *= 10;
}
return t+k;
}
Something like that should work.
I'm trying to use the qsort() function in order to sort only the even numbers of an array (the odds remains in their positions).
For instance if I have the array:
5 122 3 26 48
After sorting one would get:
5 26 3 48 122
My intuition was only to make a sort when both numbers pointed by a and b are even.
This is my attempt:
#include <stdio.h>
#include <stdlib.h>
int comp_even(const void *a, const void *b) {
int l = *(int *)a;
int r = *(int *)b;
if ( !(l&1) && !(r&1) ) //if both are even, then sort them in ascending order
return (l-r);
return 0;
}
int main() {
int i, n;
int a[1001];
scanf("%d", &n);
for (i = 0; i < n; i++) {
scanf("%d", &a[i]);
}
qsort(a, n, sizeof(int), comp_even);
for (i = 0; i < n; i++) {
printf("%d ", a[i]);
}
return 0;
}
Unfortunately, qsort doesn't support anything like that; for any two elements, the comparison function must return one of three results:
a negative integer, meaning that its first argument should end up before its second;
a positive integer, meaning that its first argument should end up after its second;
zero, meaning that either ordering is fine (in which case qsort makes no guarantees about which element ends up before the other).
And, crucially, the function must do this using only the values of its two arguments; it doesn't know the array-indices that they came from.
Instead, you can take one of two approaches:
copy all of the even elements into an array, sort that array using a straightforward comparison function, and then copy the elements of that array back over the even elements in the original array.
create an int[][2] that stores not just the values in the array, but also their original indices. You can then sort the elements such that if either value is odd, then the element with the lesser original index comes first.
It seems impossible to design a comparison function for your purpose, but you can make a copy of the even numbers, sort that with qsort and dispatch the sorted subset over the even numbers of the original array:
#include <stdio.h>
#include <stdlib.h>
int comp_int(const void *a, const void *b) {
const int *ap = a;
const int *bp = b;
return (*ap > *bp) - (*ap < *bp);
}
int main(void) {
int i, j, n, n_even;
int a[1001];
int even[1001];
if (scanf("%d", &n) != 1 || n <= 0 || n > 1001)
return 1;
n_even = 0;
for (i = 0; i < n; i++) {
scanf("%d", &a[i]);
if ((a[i] & 1) == 0) {
even[n_even++] = a[i];
}
}
qsort(even, n_even, sizeof(int), comp_int);
for (i = j = 0; i < n; i++) {
if ((a[i] & 1) == 0) {
a[i] = even[j++];
}
for (i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
return 0;
}
Note also that your original comparison function uses a trick to compare integers that fails for large values. The difference of 2 integers may be outside the range of int. So returning l - r may be incorrect for many values of l and r.
For example l=INT_MIN and r=1 will return INT_MIN-1, the subtraction causes an arithmetic overflow, which invokes undefined behavior and in current 2s complement architectures evaluates to INT_MAX, a positive value although INT_MIN < 1.
i tried the brute force way:
#include <stdio.h>
int sum(int a [],int b[], int m);
int main (void)
{
int a [] = {1,2,3,4,5};
int b [] = {4,3,5,2,6};
int i;
printf("Enter to find a given number:\n");
scanf("%d",&i);
printf("%s\n",sum(a,b,i) ? "True":"False");
return 0;
}
int sum(int a[], int b[],int m)
{
int i=0,j=0;
for (i=0;i<=sizeof(a)/sizeof(int)+1;i++)
for(j=0;j<=sizeof(b)/sizeof(int)+1;j++)
if (a[i]+b[j]==m)
return 1;
return 0;
}
as you can see the run time is O(n^2), are there any clever way to minimise this?
The faster possible solution (O(n)) is to use hash table. Just put all the elements from the first array in it and then while iterating over the second one check if the difference between the target number and the current one is in the hash table.
Here is implementation in C++:
int main(){
int a [5] = {1,2,3,4,5};
int b [5] = {4,3,5,2,6};
int m;
printf("Enter to find a given number:\n");
scanf("%d",&m);
set<int> s;
for (int i = 0; i < 5; i++)
s.insert(a[i]);
for (int i = 0; i < 5; i++)
if (s.count(m-b[i]) > 0) {
printf("True\n");
return 0;
}
printf("False\n");
}
This is just another formulation of "Find all pairs of elements (a1,b1) such that a1 belongs to Array A and b1 belongs to Array B whose sum a1+b1 = k".
In short, use a hash table.
No need for an hash table!!
You could sort the arrays (one increasing the other one decreasing) and then compare the first elements of the arrays. At each step then move on the first array increasing and on the second decreasing (if the sum is too large you should move on the decreasing array, if the sum too small you have to move on the increasing array)
This algorithm is 2N log(N) + 2 N
You can precompute the "sums" array, put it in size order, and then bsearch(3) the array:
int sums = { /* list of sorted sums */ };
int compare(void *a, void *b) { return ((int *)a - (int *)b); }
if (bsearch((void *)int_to_test, (void *)sums, number_of_items_in_sums, sizeof(int), compare) == NULL)
errx(1, "not in list!");
printf("found it\n!");
The bsearch syntax is from memory, so double-check that in your man pages.
I find this Quicksort partitioning approach confusing and wrong, yet it seems to work. I am referring to this pseudocode. Note: they also have a C implementation at the end of the article, but it's very different from their pseudocode, so I don't care about that.
I have also written it in C like this, trying to stay true to the pseudocode as much as possible, even if that means doing some weird C stuff:
#include <stdio.h>
int partition(int a[], int p, int r)
{
int x = a[p];
int i = p - 1;
int j = r + 1;
while (1)
{
do
j = j - 1;
while (!(a[j] <= x));
do
i = i + 1;
while (!(a[i] >= x));
if (i < j)
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}
else
{
for (i = 1; i <= a[0]; ++i)
printf("%d ", a[i]);
printf("- %d\n", j);
return j;
}
}
}
int main()
{
int a[100] = //{8, 6,10,13,15,8,3,2,12};
{7, 7, 6, 2, 3, 8, 4, 1};
partition(a, 1, a[0]);
return 0;
}
If you run this, you'll get the following output:
1 6 2 3 4 8 7 - 5
However, this is wrong, isn't it? Clearly a[5] does not have all the values before it lower than it, since a[2] = 6 > a[5] = 4. Not to mention that 7 is supposed to be the pivot (the initial a[p]) and yet its position is both incorrect and lost.
The following partition algorithm is taken from wikipedia:
int partition2(int a[], int p, int r)
{
int x = a[r];
int store = p;
for (int i = p; i < r; ++i)
{
if (a[i] <= x)
{
int t = a[i];
a[i] = a[store];
a[store] = t;
++store;
}
}
int t = a[r];
a[r] = a[store];
a[store] = t;
for (int i = 1; i <= a[0]; ++i)
printf("%d ", a[i]);
printf("- %d\n", store);
return store;
}
And produces this output:
1 6 2 3 8 4 7 - 1
Which is a correct result in my opinion: the pivot (a[r] = a[7]) has reached its final position.
However, if I use the initial partitioning function in the following algorithm:
void Quicksort(int a[], int p, int r)
{
if (p < r)
{
int q = partition(a, p, r); // initial partitioning function
Quicksort(a, p, q);
Quicksort(a, q + 1, r); // I'm pretty sure q + r was a typo, it doesn't work with q + r.
}
}
... it seems to be a correct sorting algorithm. I tested it out on a lot of random inputs, including all 0-1 arrays of length 20.
I have also tried using this partition function for a selection algorithm, in which it failed to produce correct results. It seems to work and it's even very fast as part of the quicksort algorithm however.
So my questions are:
Can anyone post an example on which the algorithm DOESN'T work?
If not, why does it work, since the partitioning part seems to be wrong? Is this another partitioning approach that I don't know about?
I think the partitioning is correct. 7 is the pivot. The original array is partitioned into a sub array of length 5 containing elements less than or equal to 7 and a sub array of length 2, containing elements greater or equal to 7.
Extending on from above here is what it should look like
void swap(int *a, int *b)
{
int x;
x = *a;
*a = *b;
*b = x;
}
int partition(int s[], int l, int h)
{
int i;
int p;/* pivot element index */
int firsthigh;/* divider position for pivot element */
p = h;
firsthigh = l;
for (i = l; i < h; i++)
if(s[i] < s[p]) {
swap(&s[i], &s[firsthigh]);
firsthigh++;
}
swap(&s[p], &s[firsthigh]);
return(firsthigh);
}
void quicksort(int s[], int l, int h)
{
int p;/* index of partition */
if ((h - l) > 0) {
p = partition(s, l, h);
quicksort(s, l, p - 1);
quicksort(s, p + 1, h);
}
}
int main()
{
int a[100] = //{8, 6,10,13,15,8,3,2,12};
{7, 7, 6, 2, 3, 8, 4, 1};
quicksort(a, 0, 7);
return 0;
}
From Wikipedia (I've emphasized the part that I think addresses your question directly):
This is the in-place partition
algorithm. It partitions the portion
of the array between indexes left and
right, inclusively, by moving all
elements less than or equal to
array[pivotIndex] to the beginning of
the subarray, leaving all the greater
elements following them. In the
process it also finds the final
position for the pivot element, which
it returns. It temporarily moves the
pivot element to the end of the
subarray, so that it doesn't get in
the way. Because it only uses
exchanges, the final list has the same
elements as the original list. Notice
that an element may be exchanged
multiple times before reaching its
final place. It should also be noted
that in case of pivot duplicates in
the input array, they can be spread
across left subarray, possibly in
random order. This doesn't represent a
partitioning failure, as further
sorting will reposition and finally
"glue" them together.
Could that be what you were missing?
You are getting confused between the index of the item and the iten value
Look at your header
int partition(int a[], int p, int r) ;
Now if we changed the data type on the array a to some weird data type you will see the problem
int partition( Otherdatatype a[], int p, int r) ;
You call the function from within your main with
partition(a, 1, a[0]);
See the problem a[0] is the value of the entry in a[0] not an index value.
Imagine a[0] had the value 200 in your code simply change the first item value to 200 and you will get a runtime error "attempt to access memory out of range" because if you follow
thru a[0] = 200 that is passed into partition as value r then follow what happens inside partition.
The thing to remember is this is a sort routine in your partition header the list in array a may not be of the same type as the indexes .. p and r of your header are clearly indexes referring to an index position and a is the list to be sorted.
Thus your main start to a sort is
partition(a, 0, items_in_array-1);
Do you see why? Array a runs from a[0] ... a[items_in_array-1]
So in your sample above you have preloaded 8 values into your array so your partition call from main should be
partition(a, 0, 7);