Find minimum GCD of a pair of elements in an array - primes

Given an array of elements, I have to find the MINIMUM GCD possible
between any two pairs of the array in least time complexity.
Example
Input
arr=[7,3,14,9,6]
Constraint
N= 10^ 5
Output
1
Explanation
min gcd can be of pair(7,3)
My naive solution- O(n^2) bad naive brute force
int ans=INT_MAX;
for (int i = 0; i < n; ++i)
{
for(int j=i+1; j<n; j++){
int g= __gcd(arr[i],arr[j]);
ans=min(ans,g);
}
}
return ans;
Can you suggest a better method of least time complexity?

This solution works in time O(n + h log h), where h is the maximum number in the array. Let's solve a harder problem: for each x <= h, count the number d[x] of unordered pairs (i, j) such that 0 <= i, j < n and GCD(a[i], a[j]) = x. To solve your problem, just find the smallest x such that d[x] is positive. Also note that counting ordered pairs (i, j) with i = j does not influence the solution. The solution uses Mobius inversion - basically a variation of Inclusion-Exclusion for divisors of integers.
Mobius inversion can be used to solve the following problem: you need to find an array y, but you're given an array z such that z[k] = y[k] + y[2*k] + y[3*k] + .... Surprisingly, it works in-place and it's just three lines of code!
This is exactly what we need, first we'll find the number of ordered pairs (i, j) such that d[x] divides GCD(a[i], a[j]), but we need the number of ordered pairs (i, j) such that d[x] is GCD(a[i], a[j]).
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
int main() {
int n, h = 0;
cin >> n;
vector<int> a(n);
for (int& x : a) {
cin >> x;
h = max(h, x);
}
h++;
vector<ll> c(h), d(h);
for (int x : a)
c[x]++;
for (int i=1; i<h; i++)
for (int j=i; j<h; j+=i)
d[i] += c[j];
// now, d[x] = no. of indices i such that x divides a[i]
for (int i=1; i<h; i++)
d[i] *= d[i];
// now, d[x] = number of pairs of indices (i, j) such that
// x divides a[i] and a[j] (just square the previous d)
// equiv. x divides GCD(a[i], a[j])
// apply Mobius inversion to get proper values of d
for (int i=h-1; i>0; i--)
for (int j=2*i; j<h; j+=i)
d[i] -= d[j];
for (int i=1; i<h; i++) {
if (d[i]) {
cout << i << '\n';
return 0;
}
}
}

Related

How to print the count of all distinct pairs that their sum equals k more faster than O(n^2) c++?

#include <iostream>
#include <vector>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
vector<int> v(n);
for (int i = 0; i < n; i++) {
cin >> v[i];
}
int cnt = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (v[i] + v[j] == k)
cnt++;
}
}
cout << cnt;
return 0;
}
This code searches for all distinct pairs that their sum equals k then count how many distinct pairs are equal to k. Is there a way to do this task faster e.g. faster than O(n^2).
Put the numbers into a hash table, with their occurrence count, it's an O(n) operation.
Then for each v[i] number, check the hash table for k-v[i], and sum the count, it's an O(n) operation as well.
The result will be the summed count divided by 2.

Why does the for loop in selection sort have n-1 steps instead of n? C++

I have the following attempt to write a selection sort in C++:
#include <iostream>
using namespace std;
int main()
{
int a[10], k, i, j, n, aux;
cin >> n;
for (i = 0; i <= n-1; i++)
cin >> a[i];
k = a[0];
for (i = 0; i <= n - 2; i++) {
for (j = i + 1; j <= n-1; j++)
if (k > a[j])
k = a[j];
for (j = i + 1; j <= n-1; j++)
if (k == a[j]) {
aux = a[i];
a[i] = a[j];
a[j] = aux;
}
k = a[i + 1];
}
for (i = 0; i <= n-1; i++)
cout << a[i];
return 0;
}
From my tests it returns sorted arrays, so I think it's correct.
But I also have to explain why the main for loop of the sort only takes n-1 steps instead of n. Could anyone explain the "why" part to me?
Consider how many steps are required if n is 1.
Basically, you don't need to sort the first element.
The sorting is done by comparing pairs of elements.
How many pairs are there in an array of N elements? (hint: N-1)
This animation might help explain how the algorithm works.

Number of Inversion Count in an array. Using Merge Sort

For those how not know about inversion.
Inversion-
Given an array A of N integers, an inversion of the array is defined as any pair of indexes (i,j) such that i < j and A[i] > A[j].
Inshort:
{inv}(A) = {(A(i),A(j)), i < j { and } A(i) > A(j)}
For example, the array a={2,3,1,5,4} has three inversions: (1,3), (2,3), (4,5), for the pairs of entries (2,1), (3,1), (5,4).
Total inversion count = 3.
Well, I tried to solve this question by utilizing the standard merge sort.
Here is how I think it works.
Assume that at some stage, partA and partb of your merge sort is
partA- [1,2,3].
partB- [4,5]
Now, Let X be the element of the first array, partA. Y be for second array, partB.
If X is copied to output array(i.e if X < Y) - Then we have no Inversion.
Else If Y is copied to output array(i.e if X > Y).
Then we have Inversion count = count + mid - i+1. (i being the position of that element). As it is sorted in increasing order, all the elements at position j > i, X[j] > Y.
Here's the code further details.
#include <iostream>
#include <vector>
using namespace std;
vector<int> a;
vector<int> c;
void merge(int low, int high, int mid);
void mergesort(int low, int high)
{
int mid;
if (low < high)
{
mid=(low+high)/2;
mergesort(low,mid);
mergesort(mid+1,high);
merge(low,high,mid);
}
return ;
}
int count ; //to store the inversion count
void merge(int low, int high, int mid)
{
int i, j, k;
i = low;
k = low;
j = mid + 1;
// standard merging from merge sort
while (i <= mid && j <= high)
{
if (a[i] < a[j])
{
c[k] = a[i];
k++;
i++;
}
else
{
c[k] = a[j];
k++;
j++;
// cout<<a[i]<<" "<<mid<<" "<<i<<"\n";
count += mid - i+1; // This is where the trick occurs, if X > Y,
//eg. in [3, 4, 5] and [1,2]
//if(3>1) then 4,5 is obviously greater then 1, thus making count as mid - i+1
}
}
while (i <= mid)
{
c[k] = a[i];
k++;
i++;
}
while (j <= high)
{
c[k] = a[j];
k++;
j++;
}
for (i = low; i < k; i++)
{
a[i] = c[i];
}
}
int main()
{
//int a[20], i, b[20];
int T;
cin>>T;
while(T--){
//cout<<"enter the elements\n";
int N;
cin>>N;
count =0;
a.clear(); a.resize(N);
c.clear(); c.resize(N);
for (int i = 0; i < N; i++)
{
cin>>a[i];
}
mergesort(0, N-1);
cout<<count<<"\n";
}
}
OK, Now coming to my doubt, I believe that the above implemented logic is legit enough to solve the number of inversions, but for some strange reason its not, I'm not sure whats causing WA here.
I'm stuck at this for some time, not able to figure it out.
It's not a homework Question, its just that I see no wrong with the logic and the code still doesn't work, what may be the possible reason? Help!.
Ideone Link - https://ideone.com/nmvl7i
Question on Spoj - http://www.spoj.com/problems/INVCNT/
Note: The first two test cases are working fine, While submitting I'm getting WA.
The problem for your solution is the result may be larger than the integer range, for example, if the sequence are n, n - 1, ... , 1, (non increasing) the number of inversion will be n*(n - 1)/2 and with n = 2*10^5, the result will be much larger than integer range.
So, change int count into long long count and
Change this line:
count += mid - i + 1;
into:
count += (long long)mid - (long long) i + 1L;
You will get accepted answer.
My accepted code

How can I properly add and access items to a multidimensional vector using loops?

I have this program that is trying to determine how many unique items are within some intersecting sets. The amount of input entirely depends on the the first value n, and then the amount of sets entered afterward. For example, if I start with entering n = 2, I am expected to enter 2 integers. The program then determines how many intersections there are between n items (this is like choosing 2 items from n items). This goes on as k increments. But that's kind of beyond the point. Just some background info.
My program adapts correctly and accepts the proper amount of input, but it stops working properly before the first for loop that is outside of the while loop. What I have tried to do is make a vector of integer vectors and then add every other row (when index starts at 0 AND index starts at 1). But I am guessing I have constructed my vectors incorrectly. Does anybody see an error in my vector logic?
#include <iostream>
#include <vector>
using namespace std;
int fact (int m) {
if (m <= 1)
return 1;
return m * fact(m - 1);
}
int comb (int n, int k) {
return fact(n)/(fact(n-k)*fact(k));
}
int main() {
int n = 0;
int k = 2;
int sum = 0;
int diff = 0;
int final = 0;
vector <vector <int> > arr;
cin >> n;
while (n > 0) {
vector <int> row;
int u;
for (int i = 0; i < n ; ++i) {
cin >> u;
row.push_back(u);
}
arr.push_back(row);
n = comb(row.size(), k);
k++;
}
for (int i = 0; i < arr.size(); i+2)
for (int j = 0; j < arr[i].size(); ++j)
sum += arr[i][j];
for (int i = 1; i < arr.size(); i+2)
for (int j = 0; j < arr[i].size(); ++j)
diff += arr[i][j];
final = sum - diff;
cout << final;
return 0;
}
for (int i = 0; i < arr.size(); i+=2)
^
You want to do i+=2 or i=i+2, else the value of i is never changed, leading to an infinite loop.

Number of tuples

I am given N numbers a[1..N] and 2 other integers L and H. How can I Count the number of tuples (i,j,k) satisfying i < j < k and L <= a[i] + a[j] + a[k] <= H.
1 <= T <= 100
1 <= N <= 1000
1 <= L <= H <= 1000000
1 <= a[i] <= 1000000
PS: Need Better Solution than N2logn
Solution
Since my C/C++ is somewhat rusty and this is primarily an algorithms question, I will write in pseudocode (mostly correct C/C++ with bits of algorithms that would take a while to write out).
If you have at least sizeof(int)*10^12 bytes of memory and time available, you can use this algorithm with time complexity O(n^2 * log(n)).
// Sort the N numbers using your favorite, efficient sorting method. (Quicksort, mergesort, etc.) [O(n*log(n))].
int[] b = sort(a)
int[] c = int[length(b)^2];
// Compute the sums of all of the numbers (O(n^2))
for(int i = 0; i < length(b); i++){
for (int j = i; j < length(b); j++){
c[i*length(b)+j] = b[i]+b[j];
}
}
// Sort the sum list (you can do the sorts in-place if you are comfortable) - O(n^2*log(n))
d = sort(c);
// For each number in your list, grab the list of of sums so that L<=num+sum<=H O(n)
// Use binary search to find the lower, upper bounds O(log(n))
// (Total complexity for this part: O(n*log(n))
int total = 0;
for (int i = 0; i < b; i++){
int min_index = binary_search(L-b[i]); // search for largest number <= L-b[i]
int max_index = binary_search(H-b[i]); // search for smallest number >= H-b[i]
total += max_index - min_index + 1; // NOTE: This does not handle edge cases like not finding any sums that work
}
return total;
A basic approach:
for (i=0; i<N; i++) {
for (j=i+1; j<N; j++) {
for (k=j+1; k<N; k++) {
int sum = a[i] + a[j] + a[k];
if (L <= sum && sum <= H) number_of_tuples++;
}
}
}
Possibly better (might have a mistake in it, but the basic idea is to break if you're already over the maximum):
for (i=0; i<N; i++) {
if (a[i] > H) continue;
for (j=i+1; j<N; j++) {
if (a[i] + a[j] > H) continue;
for (k=j+1; k<N; k++) {
int sum = a[i] + a[j] + a[k];
if (L <= sum && sum <= H) number_of_tuples++;
}
}
}
int find_three(int arr[], int c, int l,int h)
{
int i, j, e, s, k;
int count =0;
sort(arr,arr+c);
c--;
while(arr[c]>h)
c--;
int sum=0;
for (int i = 0; i<=c-2;i++)
{ sum=arr[i]+arr[i+1]+arr[i+2];
if(sum>h)
break;
for(j=i+1;j<=c-1;j++)
{
for(k=j+1;k<=c;k++)
{ sum=arr[i]+arr[j]+arr[k];
if(sum>=l &&sum<=h)
count++;
if(sum>h)
break;
}
if(sum>h)
break;
}
}
return count;
}