我有一个NSNrray的NSNumber,其整数值如[1,10,3]。我想得到这些数字的所有可能子集的总和。例如,对于1,10和3,我会得到:
1,10,3,1 + 10 = 11,1 + 3 = 4,10 + 3 = 13,1 + 10 + 3 = 14
有2 ^ n种可能的组合。我理解它的数学但是我很难将它放入代码中。那么我怎么能把它放到一个方法中,该方法将采用初始数字数组并返回一个包含所有子集总和的数组?例如-(NSArray *) getSums:(NSArray *)numbers;
我知道结果会成倍增长,但我会将其用于小数组。
答案 0 :(得分:4)
一个简单的递归解决方案 - 可能效率不高,而且未经测试,但总的想法应该是明确的。
- (NSArray*)getSums:(NSArray*)numbers {
return [[self getSumsHelper:numbers startingFrom:0] copy];
}
- (NSMutableArray*)getSumsHelper:(NSArray*)numbers startingFrom:(NSUInteger)index {
/* (1) */
if (index >= [numbers count])
return [NSMutableArray arrayWithObject:[NSNumber numberWithFloat:0]];
/* (2) Generate all the subsets where the `index`th element is not included */
NSMutableArray* result = [self getSumsHelper:numbers startingFrom:index+1];
/* (3) Add all the cases where the `index`th element is included */
NSUInteger i, n = [result count];
float element = [[numbers objectAtIndex:index] floatValue];
for (i = 0; i < n; i++) {
float element2 = [[result objectAtIndex:i] floatValue];
[result addObject:[NSNumber numberWithFloat:element+element2]];
}
return result;
}
问题的关键是生成给定数组的所有子集。这可以通过识别以下内容递归完成:
空数组的子集是空数组,空数组中的元素总和为零。这由标有(1)
。
如果数组不为空,则让第一个元素为X.任何子集都包含X或不包含它。因此,我们将生成不包含X的数组的所有子集(有递归,由(2)
标记,计算每个子集的总和,然后复制总和数组并将X添加到每个子集中重复部分中的总和(由(3)
标记)。
如果原始数组中只有少数项目(例如,少于16个),那么您也可以在for
循环中从0到2 n -1计数;然后,每个索引将对应于一个子集。可以通过在基数2中写i
并从数组中选择与基数2形式中的数字1相对应的那些项来确定i
子集中的元素。我相信这比我上面的解决方案效率更低。
答案 1 :(得分:1)
[警告:提前“聪明”的代码。做一些更简单的事情你可能会更好。但这很有趣,有些技术值得了解。]
如果集合足够小(它们最好是,因为否则你将耗尽内存和时间),你可以使用n元素集的子集== n-位数。所以不需要递归:从0到1的循环&lt;
这样做的一个缺点是,对于每个子集,您可能需要每次都从头开始考虑所有元素。因此,下一个技巧:使用格雷码(http://en.wikipedia.org/wiki/Gray_code#Constructing_an_n-bit_Gray_code),使得集合k具有对应于k ^(k> 1)中的1比特的元素。现在,每个子集与其前一个子集的区别仅在于一个位,您可以将其与另一个异或操作隔离。
好的,但是现在你有一个不同的问题:你有2的幂,想知道数组中哪个元素对应。所以请参阅http://www-graphics.stanford.edu/~seander/bithacks.html#IntegerLogLookup并查看开头“如果你知道v是2的幂”。
所以代码最终看起来像这样(注意:完全未经测试)......
static const int bitPositions[32] = {
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
NSUInteger n = [numbers count];
NSUInteger lim = 1<<n;
NSUInteger k, prevSubset=0;
float prevSum = 0;
NSMutableArray * result = [NSMutableArray arrayWithCapacity: lim];
[result replaceObjectAtIndex: 0 withObject: [NSNumber numberWithFloat: prevSum]]; /* empty subset */
for (k=1; k<lim; ++k) {
NSUInteger thisSubset = k^(k>>1);
NSUInteger changed = thisSubset^prevSubset;
int index = bitPositions[(changed * 0x077CB531U) >> 27];
float delta = [[numbers objectAtIndex: index] floatValue];
if (thisSubset&changed) prevSum += delta; else prevSum -= delta;
[result replaceObjectAtIndex: k withObject: [prevsum floatValue]];
}
进一步的警告:除了“聪明”,因此可能有缺陷和不可维护之外,上面会累积整个计算中的任何浮点错误。所以,如果你要采取这种方法,这里有一个更好的方法(注意:代码与最后一批一样未经测试):
static const int bitPositions[32] = {
0, 1, 28, 2, 29, 14, 24, 3, 30, 22, 20, 15, 25, 17, 4, 8,
31, 27, 13, 23, 21, 19, 16, 7, 26, 12, 18, 6, 11, 5, 10, 9
};
NSUInteger n = [numbers count];
NSUInteger lim = 1<<n;
NSUInteger k;
NSMutableArray * result = [NSMutableArray arrayWithCapacity: lim];
[result replaceObjectAtIndex: 0 withObject: [NSNumber numberWithFloat: prevSum]]; /* empty subset */
for (k=1; k<lim; ++k) {
NSUInteger firstOne = k & ~(k-1); /* one 1-bit (as it happens, the lowest) */
NSUInteger predecessor = k^firstOne; /* what we get by removing firstOne */
int index = bitPositions[(firstOne * 0x077CB531U) >> 27];
float smaller = [[result objectAtIndex: predecessor] floatValue];
float delta = [[numbers objectAtIndex: index] floatValue];
[result replaceObjectAtIndex: k withObject: [(smaller+delta) floatValue]];
}
为了获得更高的效率,如果您真的这样做,您可能首先要构建一个数组,其中不包含上面bitPositions
的魔术位索引,而是来自numbers
的相应值,从而为每个子集保存一个NSArray访问权限。如果您关心效率,那么您应该将numbers
复制到float
的纯粹C风格的数组中,这样您就不必打电话给floatValue
。< / p>