Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating permutations of NSArray elements

Let's say I have an NSArray of NSNumbers like this: 1, 2, 3

Then the set of all possible permutations would look something like this:

1, 2, 3

1, 3, 2

2, 1, 3

2, 3, 1

3, 1, 2

3, 2, 1

What's a good way to do this in objective-c?

like image 504
node ninja Avatar asked Sep 24 '10 21:09

node ninja


People also ask

How do you generate all permutations of an array?

You take first element of an array (k=0) and exchange it with any element (i) of the array. Then you recursively apply permutation on array starting with second element. This way you get all permutations starting with i-th element.

How do you generate all permutations of a number?

Heap's algorithm is used to generate all permutations of n objects. The idea is to generate each permutation from the previous permutation by choosing a pair of elements to interchange, without disturbing the other n-2 elements.

What does it mean to permute an array?

A permutation is a rearrangement of members of a sequence into a new sequence. For example, there are 24 permutations of [a, b, c, d]. Some of them are [b, a, d, c], [d, a, b, c] and [a, d, b, c].

How do you create an array of permutations in Java?

Build Array from Permutation - LeetCode. Given a zero-based permutation nums (0-indexed), build an array ans of the same length where ans[i] = nums[nums[i]] for each 0 <= i < nums. length and return it. A zero-based permutation nums is an array of distinct integers from 0 to nums.


2 Answers

I have used the code from Wevah's answer above and discovered some problems with it so here my changes to get it to work properly:

NSArray+Permutation.h

@interface NSArray(Permutation)

- (NSArray *)allPermutations;

@end

NSArray+Permutation.m

#import "NSArray+Permutation.h"

#define MAX_PERMUTATION_COUNT   20000

NSInteger *pc_next_permutation(NSInteger *perm, const NSInteger size);
NSInteger *pc_next_permutation(NSInteger *perm, const NSInteger size) 
{
    // slide down the array looking for where we're smaller than the next guy
    NSInteger pos1;
    for (pos1 = size - 1; perm[pos1] >= perm[pos1 + 1] && pos1 > -1; --pos1);

    // if this doesn't occur, we've finished our permutations
    // the array is reversed: (1, 2, 3, 4) => (4, 3, 2, 1)
    if (pos1 == -1)
        return NULL;

    assert(pos1 >= 0 && pos1 <= size);

    NSInteger pos2;
    // slide down the array looking for a bigger number than what we found before
    for (pos2 = size; perm[pos2] <= perm[pos1] && pos2 > 0; --pos2);

    assert(pos2 >= 0 && pos2 <= size);

    // swap them
    NSInteger tmp = perm[pos1]; perm[pos1] = perm[pos2]; perm[pos2] = tmp;

    // now reverse the elements in between by swapping the ends
    for (++pos1, pos2 = size; pos1 < pos2; ++pos1, --pos2) {
        assert(pos1 >= 0 && pos1 <= size);
        assert(pos2 >= 0 && pos2 <= size);

        tmp = perm[pos1]; perm[pos1] = perm[pos2]; perm[pos2] = tmp;
    }

    return perm;
}

@implementation NSArray(Permutation)

- (NSArray *)allPermutations
{
    NSInteger size = [self count];
    NSInteger *perm = malloc(size * sizeof(NSInteger));

    for (NSInteger idx = 0; idx < size; ++idx)
        perm[idx] = idx;

    NSInteger permutationCount = 0;

    --size;

    NSMutableArray *perms = [NSMutableArray array];

    do {
        NSMutableArray *newPerm = [NSMutableArray array];

        for (NSInteger i = 0; i <= size; ++i)
            [newPerm addObject:[self objectAtIndex:perm[i]]];

        [perms addObject:newPerm];
    } while ((perm = pc_next_permutation(perm, size)) && ++permutationCount < MAX_PERMUTATION_COUNT);
    free(perm);

    return perms;
}

@end
like image 196
Pegolon Avatar answered Oct 09 '22 18:10

Pegolon


There might be a better way to do this (in-place, or something), but this seems to work:

Header:

@interface NSArray (PermutationAdditions)

- (NSArray *)allPermutations;

@end

Implemetation:

@implementation NSArray (PermutationAdditions)

NSInteger *pc_next_permutation(NSInteger *p, const NSInteger size) {
    // slide down the array looking for where we're smaller than the next guy
    NSInteger i;
    for (i = size - 1; p[i] >= p[i + 1]; --i) { }

    // if this doesn't occur, we've finished our permutations
    // the array is reversed: (1, 2, 3, 4) => (4, 3, 2, 1)
    if (i == -1)
        return NULL;

    NSInteger j;
    // slide down the array looking for a bigger number than what we found before
    for (j = size; p[j] <= p[i]; --j) { }

    // swap them
    NSInteger tmp = p[i]; p[i] = p[j]; p[j] = tmp;

    // now reverse the elements in between by swapping the ends
    for (++i, j = size; i < j; ++i, --j) {
        tmp = p[i]; p[i] = p[j]; p[j] = tmp;
    }

    return p;
}

- (NSArray *)allPermutations {
    NSInteger size = [self count];
    NSInteger *perm = malloc(size * sizeof(NSInteger));

    for (NSInteger idx = 0; idx < size; ++idx)
        perm[idx] = idx;

    NSInteger j = 0;

    --size;

    NSMutableArray *perms = [NSMutableArray array];

    do {
        NSMutableArray *newPerm = [NSMutableArray array];

        for (NSInteger i = 0; i <= size; ++i)
            [newPerm addObject:[self objectAtIndex:perm[i]]];

        [perms addObject:newPerm];
    } while ((perm = pc_next_permutation(perm, size)) && ++j);

    return perms;
}

@end

To use it, simply #import the header file, and call [yourArray allPermutations]; the method will return an array containing arrays for each permutation.

[Code adapted from PHP code here.]

like image 31
Wevah Avatar answered Oct 09 '22 18:10

Wevah