3

I was interested in creating a simple array problem with running time and space constraints. It seems that I have found a solution to my problem. Please read the initial description comment of the problem in the following java code:

 /*
 * Problem: Given two integer arrays, a and b, return whether array a is a permutation of array b.
 * Running time complexity: O(n)
 * Space complexity: O(1)
 * Example 1:
 * a = [1, 2, -3]
 * b = [2, 3, 1]
 * false
 * Example 2:
 * a = [1, 2, 3]
 * b = [0, 1]
 * false
 * Example 3:
 * a = [2, 7, 3, 5]
 * b = [5, 7, 2, 3]
 * true
 * Example 4:
 * a = [1, -2, 10000]
 * b = [10000, 1, -2]
 * true
 * Example 5:
 * a = [1, 2, 2, 3]
 * b = [3, 2, 1, 3]
 * false
 * Example 6:
 * a = [2, 2, 4, 4]
 * b = [3, 3, 3, 3]
 * false
 * Example 7:
 * a = [4, 4, 2, 2, 4, 4]
 * b = [4, 3, 3, 3, 3, 4]
 * false
 * ----------------------
 * Input is two space separated lines of integers
 * Output is true or false
 * Terminal Example:
 * 1 4 9 25
 * 4 25 9 2
 * false
 * ----------------------
 * Solution:
 * 1. Average displacement (delta) between the elements of array a and array b equals 0
 * AND
 * 2. xor-ing all of the values between the elements of array a and array b equals 0
 * AND
 * 3. mins are the same and maxs are the same
 * @author (David Brewster)
 * @version (27.01.2016) (requires java 1.8)
 */

import java.util.Scanner;
import java.util.Arrays;
public class ArrayProb
{
    public static int xorComparison(int[] a, int[] b, int i, int xortotal)
    {
        return i == a.length ?
                xortotal : xorComparison(a, b, i + 1, xortotal ^ a[i] ^ b[i]);
    }
    public static int deltaComparison(int[] a, int[] b, int i, int deltatotal)
    {
        return i == a.length ?
                deltatotal : deltaComparison(a, b, i + 1, deltatotal + a[i] - b[i]);
    }
    public static int minComparison(int[] a, int[] b, int i, int amin, int bmin)
    {
        return i == a.length ?
                amin-bmin : minComparison(a, b, i + 1,
                a[i] < amin ? a[i] : amin,
                b[i] < bmin ? b[i] : bmin);
    }
    public static int maxComparison(int[] a, int[] b, int i, int amax, int bmax)
    {
        return i == a.length ?
                amax-bmax : maxComparison(a, b, i+1,
                a[i] > amax ? a[i] : amax,
                b[i] > bmax ? b[i] : bmax);
    }
    public static boolean arePermutations(int[] a, int[] b)
    {
        if (a.length == b.length)
        {
            boolean d = xorComparison(a, b, 0, 0) == 0;
            boolean e = deltaComparison(a, b, 0, 0) == 0;
            boolean f = maxComparison(a, b, 0, Integer.MIN_VALUE, Integer.MIN_VALUE) == 0;
            boolean g = minComparison(a, b, 0, Integer.MAX_VALUE, Integer.MAX_VALUE) == 0;
            return d && e && f && g;
        }
        else
        {
            return false;
        }
    }
    public static void main(String[] args)
    {
        Scanner input = new Scanner(System.in);
        int[] a = Arrays.stream(input.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
        int[] b = Arrays.stream(input.nextLine().split(" ")).mapToInt(Integer::parseInt).toArray();
        System.out.println(arePermutations(a, b));
    }
}

Even thought the algorithm seems to work for most cases (at least all that I have tested so far), how would I go about proving that the solution is correct 100% of the time?

Paul R
  • 208,748
  • 37
  • 389
  • 560
ljeabmreosn
  • 169
  • 8
  • 3
    what does your code return for a=[2,2,4,4], b=[3,3,3,3]? – twin Jan 29 '16 at 09:00
  • One approach I like to prove/disprove an algorithm is to start trying it with induction. Try to make a claim about the loop invariant. Check if you can have a strong enough claim to prove the correctness, and if you can prove this invariant. If you see you are failing to prove it - it will often give you a hint to what's wrong with the algorithm. – amit Jan 29 '16 at 09:04
  • @twin Very clever.... I knew my algorithm was not sound when duplicates came into the picture. Do you think there is any way to express this problem mathematically so I can try to incorporate another check or prove that this problem is impossible? – ljeabmreosn Jan 29 '16 at 09:05
  • 1
    Also, as side note: Your algorithm is O(n) space, as it uses linear number of recursion calls. Since it is tail recursion - it can be pretty easily modified to a loop with constant space - but java won't do it for you automatically, afaik – amit Jan 29 '16 at 09:06
  • @amit Yes! Technically it is because of java. I originally wrote this program in F#, and F# has tail cell optimization. I am less focused on the technical aspects of the code but more interested on the mathematical concept. – ljeabmreosn Jan 29 '16 at 09:09
  • 1
    Your idea is that if two arrays of the same size sum to the same value and xor to the same value, they must be permutations of each other. One, you don't need to write an entire program to express this idea, and two, it doesn't work, which is very easy to see. Consider only arrays that contain each value an even number of times, and for each number n also contain -n the same number of times. For example, (1,1,-1,-1) and (2,2,-2,-2). All such arrays xor to zero and sum to zero, but they all cannot be permutations of each other, as the examples show. – n. m. could be an AI Jan 29 '16 at 09:30
  • 1
    Even without duplicates, your algorithm doesn't work - e.g. a=[1,2], b=[0,3] – twin Jan 29 '16 at 09:50
  • And your 3rd check (min/max) doesn't help - if you have a case for which the original algorithm returns a false "true", you can trivially find an example for which the new algorithm failes - e.g. a=[-10,1,2,10], b=[-10,0,3,10] – twin Jan 29 '16 at 09:56
  • @twin I updated the program to include max and min checks; this would realistically put the running time to O(~4n), thus staying linear. The point of my doing this is to not only solve the problem but also formulate some type of proof (or lack thereof). How can I prove that the question is impossible (if it is) to solve with the space and time requirements? – ljeabmreosn Jan 29 '16 at 09:58
  • @twin could it be fair to say that this algorithm could only possibly apply to an array of natural numbers ({1, 2, 3, etc.})? – ljeabmreosn Jan 29 '16 at 10:35
  • @ljeabmreosn I couldn't come up with such a proof yet, but my suspicion is that it can't be done. So what I would do is assume that it can be done, and prove that in that case some other algorithm can also be done in less time than it minimally requires. – biziclop Jan 29 '16 at 11:47

2 Answers2

2

What you are basically trying to do is computing a fixed size fingerprint of each array (representing a multiset) and then determining equality of the multisets by comparing the fingerprints.

This is clearly not possible because there is an infinite number of multisets but if space is constant, there is only a limited number of fingerprints. So you will inevitably find examples where two different multisets map to the same fingerprint.

lex82
  • 11,173
  • 2
  • 44
  • 69
  • This approach also gives you a hint that while asymptotically you can't do any better, in certain cases you can improve the actual running time. For example if you expect most tests to fail, you can employ a [Bloom filter](https://en.wikipedia.org/wiki/Bloom_filter) (which is just the sort of fixed size fingerprint we're talking about) to quickly find a negative answer for most of your tests. – biziclop Jan 29 '16 at 13:59
  • Using a pseudo-random fingerprint and given the size of the arrays and the fingerprints, the probability of a false positive (i.e. incorrectly answering the arrays are permutations) could be calculated. Reducing this probability to, say, 10^(-100) will make this as deterministic as anything in the world. Thus, the answer in **yes**, you can check this is linear time and constant space (for a given array size and error parameters). – Dan Getz Feb 05 '16 at 18:58
0

I'm not Java literate, but could you just sort each array and then check the sorted arrays iteratively for equality at each element?

David Shaked
  • 3,171
  • 3
  • 20
  • 31