3

Given a list of integers, how can all pairs (ie. a x b = c x d; such that a != b != c != e) be found under minimal time complexity?

I've tried using a hashmap data type, which basically does the product calculation, checks whether it's already found within a hashmap, and if it is it attributes the value of the increment counter of the nested for loops with the Pair object type found within the hashmap.

Pair is an object storing the index of the first and second numbers in a pair. The hashmap stores the product as the key and the pair as the value.

The problem with my code is that when it comes to the following scenario...

a x b = c x d = e x f

...it doesn't work due to the fact it only makes the following links...

a x b = c x d and a x b = e x f

...and is unable to reach:

c x d = e x f

For example the following array produces incorrect results:

int[] A = {1,2,3,4,6,12};

I expect the sole problem is because a hashmap only takes one value for a given key. I've tried to maybe change the hashmap declaration to an array of pairs but quickly realised I would be needing to add another for loop, and thus increasing the time complexity.

Any ideas what I can do to maintain the O(n²) and provide correct results?

drew181
  • 333
  • 1
  • 10
  • Sounds like an O(n4) problem – Maurice Perry May 10 '19 at 12:14
  • Actually, storing an array of pairs instead of a single pair in your Hashmap seems to be the right approach. The extra loop will indeed increase the execution time, but the algorithm complexity will still be O(n²) – Gruntzy May 10 '19 at 12:21
  • Note that `HashMap.containsKey` is O(n), so your algorithm is already a `O(n^3)` algorithm. – Sweeper May 10 '19 at 12:32
  • 2
    @Sweeper Why do you think that looking up a key in hash map is O(n)? – Henry May 10 '19 at 12:34
  • @Sweeper - Is it? I'm not sure but I found this: https://stackoverflow.com/questions/8923251/what-is-the-time-complexity-of-hashmap-containskey-in-java – drew181 May 10 '19 at 12:35
  • @Henry See http://bigocheatsheet.com `HashMap` uses a hash table underlyingly, and hash tables are O(n) in the worst case. – Sweeper May 10 '19 at 12:36
  • @Sweeper, hashmap uses a HashTable to store data, for which the get and put operations are O(1) – Gruntzy May 10 '19 at 12:38
  • @Sweeper true, in the worst case it is O(n). But this is extremely unlikely to happen if one uses a decent hash function. – Henry May 10 '19 at 12:46

1 Answers1

3

My take at it:

Store a Set of Pairs for each product. This should take care of duplicates. You need to consider Pairs equal if they consist of the same numbers.

import java.util.HashMap;
import java.util.HashSet;
import java.util.Objects;

public class Main {

  static int[] data = {1,2,3,4,6,12};

  static class Pair {

    public Pair(int x, int y) {
      this.x = x;
      this.y = y;
    }

    public int x;
    public int y;

    @Override
    public boolean equals(Object o) {
      if (this == o) return true;
      if (o == null || getClass() != o.getClass()) return false;
      Pair pair = (Pair) o;
      return
        x == pair.x && y == pair.y ||
        x == pair.y && y == pair.x;
    }

    @Override
    public int hashCode() {
      return Objects.hash(x * y);
    }
  }

  public static void main(String[] args){

    HashMap<Integer, HashSet<Pair>> products = new HashMap<>();

    // index all pairs by product in o^2 loop
    for (int i=0;i<data.length;i++){
      for (int j=i+1;j<data.length;j++){
        int a = data[i];
        int b = data[j];
        Integer p = a*b;
        if (!products.containsKey(p)){
          products.put(p, new HashSet<>());
        }
        HashSet<Pair> knownPairs = products.get(p);
        Pair pair = new Pair(a, b);
        knownPairs.add(pair);
      }
    }

    // output results
    for (Integer product: products.keySet()) {
      System.out.print("product: "+product+" -");
      HashSet<Pair> pairs = products.get(product);
      for (Pair pair : pairs) {
        System.out.print(" "+pair.x+"x"+pair.y);
      }
      System.out.println();
    }


  }

}


Slawomir Chodnicki
  • 1,525
  • 11
  • 16
  • Yes, you could check that `pairs.size() > 1` before printing the product and the pairs. – Maurice Perry May 10 '19 at 12:48
  • Thanks! For bigger lists I can't seem to match my answers with my friends so it still seems to not perform as intended but at least it gets me closer to the answer. Could you also please explain what the equals method that is overriding does? – drew181 May 10 '19 at 14:54
  • 1
    The equals and hashCode overrides ensure that the Set data structure considers `new Pair(a,b)` and `new Pair(b,a)` as equivalent. https://stackoverflow.com/questions/2265503/why-do-i-need-to-override-the-equals-and-hashcode-methods-in-java If I had to hazard a guess as to why it differs from your friends: duplicate entries in the integer array, i.e. `{2,3,4,4,4,3,4}`. Dealing with such things may or may not be in scope for the task at hand :) – Slawomir Chodnicki May 10 '19 at 14:58
  • 1
    Hmm.. I think I got it why it differs. The array {1, 2, 2, 4} returns the pair 1 * 4 and 2 * 2 but every integer should be different. – drew181 May 10 '19 at 15:36
  • 1
    Yes, it is not clear to me whether such inputs are possible and how they should be handled. If you want to exclude them entirely, just insert a check for equality of `a` and `b` before calculating the product, and if they are equal, just `continue` the loop ignoring them. – Slawomir Chodnicki May 10 '19 at 15:39
  • I should have figured that out. :p Matched my answer with my friends' by checking for a != b. Thank you so much for your patience. Learned a lot. – drew181 May 10 '19 at 15:44