3

My aim is to find all permutations of a 64 byte array, and for each permutation check if after performing a function F, it is equal to given byte array.

Consider a Small scale Example: Suppose I have 1234, I would like to generate all the permutations of a 4 digit number _ _ _ _ and check each time if it equals 1234

My first thought was to implement a recursive function to generate the permutations. But considering the size, The stack will overflow.

Any efficient way to generate all the permutations? Given Java has large number of libraries?

user3041058
  • 1,250
  • 2
  • 9
  • 14
  • 1
    I think rather than stack size, where a depth of 64 is not too much, you'll run into a heap memory problem if you try to actually generate all those permutations and keep them in memory. – RealSkeptic Apr 09 '15 at 17:47
  • 8
    There are 64! ~ 1.26886932e89 possible permutations. You won't be able to go through them all. – Teepeemm Apr 09 '15 at 17:47
  • Let's do the math: number of permutations with repetition = 256^64 ~ 1x10^154. To put in perspective: number of estimated hydrogen atoms in the universe: 1x10^82 – copeg Apr 09 '15 at 17:52
  • These are *not* permutations. They're *cartesian products* of the alphabet set which is a range of values from 0-255. – Shashank Apr 09 '15 at 17:55
  • 2
    @Shashank, I was going to make the same comment, but look at the yellow part of the question. It actually seems to be what the OP is asking for. The word "permutation" is probably incorrect in the OP. – RealSkeptic Apr 09 '15 at 17:56
  • 2
    @RealSkeptic Well then I'm confused, unfortunately...Either it's 256^64 in case of cartesian products or 64! in the case of permutations with repetition for a given 64 byte array. Either way...it's too much to compute reasonably. – Shashank Apr 09 '15 at 18:01
  • 1
    @Shashank indeed. Otherwise it would have been quite easy to crack arbitrary passwords by brute force. – RealSkeptic Apr 09 '15 at 18:02
  • @user3041058: What do you mean by 64 byte array exactly? Thanks! – erol yeniaras Apr 09 '15 at 20:51
  • Are you trying to get the index of a particular permutation? – higuaro Apr 10 '15 at 00:16

3 Answers3

3

If I understood correctly, you need to generate all the 64! permutations of a 64 byte array, that is:

64! = 126886932185884164103433389335161480802865516174545192198801894375214704230400000000000000 permutations!

If every permutation and comparison takes one millisecond (worst case time scenario) to be calculated, you'll need:

4023558225072430368576654912961741527234446859923426946943236123009091331506849.3150684931506849315 years to calculate them all in one machine! (The 100th of this monstrosity if every permutation takes a 100th of a millisecond).

So, you should reduce the search space of your problem by applying some heuristics instead of naively listing all possible solutions.

After you reduce your search space to a more treatable number number, like for example: 14! (2 years of computation time in the "one millisecond" case scenario), you can split your calculations over several machines by using Factoradics (an implementation here) to calculate the starting and ending permutation for every machine and then use the following code in every node (an implementation of the Knuth's L-algorithm) to search the solution in every machine:

public class Perm {
    private static byte[] sequenceToMatch;
    private static byte[] startSequence;    
    private static byte[] endingSequence;        
    private static final int SEQUENCE_LENGTH = 64;

    public static void main(String... args) {
        final int N = 3;

        startSequence = readSequence(args[0]);
        endingSequence = readSequence(args[1]);
        sequenceToMatch = readSequence(args[2]);                

        permutations();
    }    

    private static boolean sequencesMatch(byte[] s1, byte[] s2) {
        for (int i = 0; i < SEQUENCE_LENGTH; i++) {
            if (s1[i] != s2[i]) {
                return false;
            }
        }
        return true;
    }

    private static byte[] readSequence(String argument) {
        String[] sBytes = argument.split(",");
        byte[] bytes = new byte[SEQUENCE_LENGTH];
        int i = 0;
        for (String sByte : sBytes) {
            bytes[i++] = Byte.parseByte(sByte, 10);
        }
        return bytes;
    }

    private static void swap(byte[] elements, int i, int j) {
        byte temp = elements[i];
        elements[i] = elements[j];
        elements[j] = temp;
    }

    /**
     * Reverses the elements of an array (in place) from the start index to the end index 
     */
    private static void reverse(byte[] array, int startIndex, int endIndex) {
        int size = endIndex + 1 - startIndex;
        int limit = startIndex + size / 2;
        for (int i = startIndex; i < limit; i++) {
            // swap(array, i, startIndex + (size - 1 - (i - startIndex)));
            swap(array, i, 2 * startIndex + size - 1 - i);
        }
    }

    /**
     * Implements the Knuth's L-Algorithm permutation algorithm 
     * modifying the collection in place
     */
    private static void permutations() {
        byte[] sequence = startSequence;

        if (sequencesMatch(sequence, sequenceToMatch)) {
            System.out.println("Solution found!");
            return;
        }

        // For every possible permutation 
        while (!sequencesMatch(sequence, endingSequence)) {

            // Iterate the array from right to left in search 
            // of the first couple of elements that are in ascending order
            for (int i = SEQUENCE_LENGTH - 1; i >= 1; i--) {
                // If the elements i and i - 1 are in ascending order
                if (sequence[i - 1] < sequence[i]) {
                    // Then the index "i - 1" becomes our pivot index 
                    int pivotIndex = i - 1;

                    // Scan the elements at the right of the pivot (again, from right to left)
                    // in search of the first element that is bigger
                    // than the pivot and, if found, swap it
                    for (int j = SEQUENCE_LENGTH - 1; j > pivotIndex; j--) {
                        if (sequence[j] > sequence[pivotIndex]) {
                            swap(sequence, j, pivotIndex);
                            break;
                        }
                    }

                    // Now reverse the elements from the right of the pivot index
                    // (this nice touch to the algorithm avoids the recursion)
                    reverse(sequence, pivotIndex + 1, SEQUENCE_LENGTH - 1);
                    break;
                }
            }

            if (sequencesMatch(sequence, sequenceToMatch)) {
                System.out.println("Solution found!");
                return;
            }
        }
    }
}
higuaro
  • 14,758
  • 3
  • 32
  • 40
  • Your answer is, of course, the best so far. But I do believe that the OP actually meant all combinations of 64 bytes rather than the permutations of a specific one (see the part in yellow in OP). So that would be 256⁶⁴ combinations. You advice still applies, of course. – RealSkeptic Apr 10 '15 at 09:44
2

As for non-recursive, this answer might help: Permutation algorithm without recursion? Java

As for the simple example, here is a recursive solution that I worked out:

public class Solution {
    public List<List<Integer>> permute(int[] num) {
        boolean[] used = new boolean[num.length];
        for (int i = 0; i < used.length; i ++) used[i] = false;

        List<List<Integer>> output = new ArrayList<List<Integer>>();
        ArrayList<Integer> temp = new ArrayList<Integer>();

        permuteHelper(num, 0, used, output, temp);

        return output;

    }

    public void permuteHelper(int[] num, int level, boolean[] used, List<List<Integer>> output, ArrayList<Integer> temp){

        if (level == num.length){
            output.add(new ArrayList<Integer>(temp));
        }
        else{

            for (int i = 0; i < num.length; i++){
                if (!used[i]){
                    temp.add(num[i]);
                    used[i] = true;
                    permuteHelper(num, level+1, used, output, temp);
                    used[i] = false;
                    temp.remove(temp.size()-1);

                }
            }

        }

    }
}

Edit: 10 seems to be the max input that completes in a reasonable amount of time for the recursive approach.

With an input array of length 10:

Permutation execution time in milliseconds: 3380
Community
  • 1
  • 1
Daniel Nugent
  • 40,780
  • 13
  • 103
  • 126
1

Edit: I had a quick search for possible implementations and came across an algorithm suggested as a part of an answer to another question.

Below is a code developed by: https://stackoverflow.com/users/2132573/jon-b for your convenience. It correctly creates and prints all the permutations of an integer list. You may easily use the same logic with array (Credits go to jon-b !!).

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class HelloWorld {
    public static int factorial(int x) {
            int f = 1;
            while (x > 1) {
                    f = f * x;
                    x--;
            }
            return f;
    }

    public static List<Integer> permute(List<Integer> list, int iteration) {
            if (list.size() <= 1) return list;
            int fact = factorial(list.size() - 1);
            int first = iteration / fact;
            List<Integer> copy = new ArrayList<Integer>(list);
            Integer head = copy.remove(first);
            int remainder = iteration % fact;
            List<Integer> tail = permute(copy, remainder);
            tail.add(0, head);
            return tail;
    }

    public static void main(String[] args) throws IOException {
            List<Integer> list = Arrays.asList(4, 5, 6, 7);
            for (int i = 0; i < 24; i++) {
                System.out.println(permute(list, i));
            }
    }
}

I have tested it in http://www.tutorialspoint.com/compile_java8_online.php and it worked well as is.

Hope that helps!

Community
  • 1
  • 1
erol yeniaras
  • 3,463
  • 2
  • 18
  • 37
  • Thanks for your note. I am checking it now! – erol yeniaras Apr 09 '15 at 18:21
  • I suggest you try to test it with a list of 15 integers. Don't forget to update the loop limit to 15! in main. ;-) – RealSkeptic Apr 09 '15 at 20:17
  • @RealSkeptic: Thumbs up for your catch! I have been testing it for 15 and OMG... working on it.. – erol yeniaras Apr 09 '15 at 20:44
  • @RealSkeptic: The array is 64 bytes and I am assuming that it has 16 integers max. So at some point we will need 16! = 2.092279e+13. outside boundaries of int but inside long. Do you have any suggestion? – erol yeniaras Apr 09 '15 at 20:48
  • 1
    Ah, but you can't use chunks of four bytes in an integer, because you'll need to permute the bytes that are in the same integer as well. So you'll end up with 64!. That is, if we're assuming the OP meant permutations (see comments to the OP). Even if one permutation takes one nanosecond to run, it'll take about 10⁷² years to run. So, my suggestion? Leave it. – RealSkeptic Apr 09 '15 at 20:56