获得N长度的所有排列

时间:2016-07-08 14:10:22

标签: objective-c set

给定输入NSArray,我试图返回长度为n的所有唯一排列。订购并不重要。当n为0或1时,我的代码有效。但是,如果n大于1,它还会返回长度小于n的所有计算排列。我只想要长度为n的排列。

这是我的代码:

+ (NSMutableSet *) combinations: (NSArray *) inputArray n: (int) n {
NSMutableSet *permutations = [[NSMutableSet alloc] init];
[ permutations addObject: [[NSSet alloc] init] ];

for (int i=0; i<n; i++) {
    NSMutableSet *newPermutations = [[NSMutableSet alloc] init];
    for (int j=0; j<[inputArray count]; j++) {
        NSSet *single = [NSSet setWithObjects:inputArray[j],nil];
        for (id permutation in permutations) {
            NSSet *newPermutation = [permutation setByAddingObjectsFromSet:single];
            [newPermutations addObject: newPermutation];
        }
    }
    permutations = [newPermutations copy];
}

return permutations;
}

有人能发现这里有什么问题吗?我从一个空集开始,然后面包优先扩展它。通过在每个循环之后将newPermutations变量赋值给排列,它应该删除前一级别的所有排列。 这是我使用Objective-C的第一天,所以我不知道它为什么表现得不像我认为的那样。

2 个答案:

答案 0 :(得分:0)

我设法找到了这个bug。我在循环遍历每个级别的所有输入数组元素时组合集合。添加集合中已存在的元素并不会使它更长,我忽略了这一点。

所以解决方法是在将newPermutation变量添加到newPermutations集之前添加额外的检查以查看newPermutation变量是否确实增长了1。

答案 1 :(得分:0)

您正在构建的实际上是组合,而不是排列;排列是有序的。您只需选择原始集合的子集。

我认为有一种更简单的方法可以做到这一点。您所需的结果与All possible combinations without repetition from an NSArray非常相似,添加了对组合大小的限制。我推荐的相同技术将在这里工作:数到2 num_items 并使用objectsAtIndexes:来创建子数组。

与另一个问题一样,这仅适用于计数为64或更小的数组,但这应该是合理的:来自大小 n的集合的大小 k 的组合数被称为 n选择k ,可以通过 n来计算! /(k!(n-k)!)。除非 k 小或接近 n ,否则 n 会快速增强非常。例如, 64选择12 为3,284,214,703,056。你没有足够的记忆。

为了使我的答案适应,我们只需要能够计算索引变量中设置的位数。这称为“popcount”(人口数),非常方便地提供as a builtin function by the compiler。特别是,我们将使用__builtin_popcountll()版本,该版本在unsigned long long上运行。

这演示了该技术(使用我在另一个答案中分享的NSIndexSet类别方法)。如果您这样选择,可以很容易地将其放入类别方法-[NSArray QNAllSubarraysOfSize:(uint64_t)]

NSArray * items = @[...];

uint64_t subarray_size = ...;

NSMutableArray * combos = [NSMutableArray array];
// Loop through all possible index subsets
for( uint64_t index_mask = 0; index_mask < UINT64_MAX; index_mask++ ){
    // Check the size of this subset; pass if it's not right.
    uint64_t num_set_bits = __builtin_popcountll(index_mask)
    if( num_set_bits != subarray_size ){
        continue;
    }

    // If the size is correct, collect the subarray
    NSIndexSet * indexes = [NSIndexSet WSSIndexSetFromMask:index_mask];
    [combos addObject:[items objectsAtIndexes:indexes]];
}