Skip to content

Finding two elements of array with maximum product

An answer to this question on Stack Overflow.

Question

I need to find two elements of array with the maximum product and output their multiplication. I tried to sort the array and then search for maximum product but my code doesn't work with several inputs (I will give them at the end of the question) because I'm getting 0.00 as a result. I suppose my if statement is not finding the right value to assign to 'sand' but I have no idea how else can I write it. This is the code I'm using:

#include <iostream>
#include <algorithm>
#include <iomanip>
#include <functional>
using namespace std;
int main() {
  int n;
  double a[10], sand=0; 
  cin >> n;
 
  for(int i = 0; i < n; i++)
  { 
    cin >> a[i];
  }
sort(a,a+n);
  for(int i = 0; i <n; i++)
  { for (int j=i+1;j<n;j++){
    if(a[i]*a[j]>i*j)
    {    sand=(a[i])*(a[j]);
  }
  }
  }
  
  cout<<fixed<<setprecision(2)<<sand;
}

This is the input:

2
5 -8

This is the output I should get:

-40.00

I am getting 0.00 instead. Second input:

4
0 0.2 0.4 0.5

Output I should get:

0.20

I'm once again getting 0.00 as an output with my code.

Answer

There are four potential algorithms you can use, as shown below:

#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstdio>
#include <iostream>
#include <iomanip>
#include <vector>
//O(N^2): Compare all elements against each other and keep the biggest product
//We pass `data` by value so the order of the array as invariant between tests
template<class T>
T Method1(std::vector<T> &data){
  assert(data.size()>=2);
  T bestval=data[0]*data[1];
  for(int i=0;i<data.size();i++)
  for(int j=i+1;j<data.size();j++){
    const auto val = data[i]*data[j];
    if(val>bestval)
      bestval = val;
  }
  return bestval;
}
//O(N log N): Sort the elements and look at the largest potential products
//We pass `data` by value so the order of the array as invariant between tests
template<class T>
T Method2(std::vector<T> &data){
  assert(data.size()>=2);
  std::sort(data.begin(), data.end());
  return std::max(data[0]*data[1], data[data.size()-1]*data[data.size()-2]);
}
//O(N): Use Quickselect to look at the largest potential products
//We pass `data` by value so the order of the array as invariant between tests
template<class T>
T Method3(std::vector<T> &data){
  assert(data.size()>=2);
  std::nth_element(data.begin(),data.begin()+1,data.end());
  auto smallval = data[0]*data[1];
  std::nth_element(data.begin(),data.end()-2,data.end());
  auto bigval = (data[data.size()-2]*data[data.size()-1]);
  return std::max(smallval,bigval);
}
//O(N): Explicitly find the pairs with the largest potential products
//We pass `data` by value so the order of the array as invariant between tests
template<class T>
T Method4(std::vector<T> &data){
  assert(data.size()>=2);
  T small1 = std::numeric_limits<T>::max();
  T small2 = std::numeric_limits<T>::max();
  T large1 = std::numeric_limits<T>::min();
  T large2 = std::numeric_limits<T>::min();
  for(const auto &x: data){
    if(x<=small1){
      small2 = small1;
      small1 = x;
    } else if(x<=small2){
      small2 = x;
    }
    if(x>=large1){
      large2 = large1;
      large1 = x;
    } else if(x>=large2){
      large2 = x;
    }
  }
  return std::max(small1*small2,large1*large2);
}
template<class Func>
void TimeIt(std::string message, Func func){
  long x; //Dummy variable to prevent compiler optimizations
  const auto start = std::chrono::high_resolution_clock::now();
  const int TEST_COUNT=100;
  for(int i=0;i<TEST_COUNT;i++)
    x += func();
  const auto finish = std::chrono::high_resolution_clock::now();
  std::cout << message <<" time: " << std::fixed << ((std::chrono::duration<double>)(finish-start)).count()/TEST_COUNT << "s "<<std::endl;
}
int main(){
  const int N=100000;
  std::vector<long> data;      //Don't change this
  std::vector<long> data_orig; //Let the methods mutate this
  data.reserve(N);
  for(int i=0;i<N;i++){
    data.push_back(rand()%2000001-1000000); //Get a random number in the range [-1000000,1000000]
  }
  data_orig = data;
  std::cout<<"Method1 = "<<Method1(data)<<std::endl;
  std::cout<<"Method2 = "<<Method2(data)<<std::endl;
  std::cout<<"Method3 = "<<Method3(data)<<std::endl;
  std::cout<<"Method4 = "<<Method4(data)<<std::endl;
  //Do they all give the same answer?
  assert(Method1(data)==Method2(data));
  assert(Method2(data)==Method3(data));
  assert(Method3(data)==Method4(data));
  TimeIt("Method1", [&](){data=data_orig; return Method1(data);});
  TimeIt("Method2", [&](){data=data_orig; return Method2(data);});
  TimeIt("Method3", [&](){data=data_orig; return Method3(data);});
  TimeIt("Method4", [&](){data=data_orig; return Method4(data);});
}

On my machine these all give the same answer, which is nice evidence that they're correct:

Method1 = 999976000143
Method2 = 999976000143
Method3 = 999976000143
Method4 = 999976000143

The timing results are as we might expect, averaged over 100 iterations I get:

Method1 time: 2.624980s 
Method2 time: 0.004581s 
Method3 time: 0.000806s 
Method4 time: 0.000118s