[Внимание: впереди «умный» код.Вы, вероятно, лучше делать что-то попроще.Но это довольно забавно, и о некоторых приемах стоит знать.]
Если наборы достаточно малы (что должно быть лучше, потому что в противном случае у вас не хватит памяти и времени)), вы можете использовать тот факт, что подмножества n-элементного набора == n-битных чисел.Поэтому нет необходимости в рекурсии: цикл от 0 до 1 <<p> Один из недостатков заключается в том, что для каждого подмножества вам, возможно, придется каждый раз рассматривать все его элементы с нуля.Итак, следующий прием: используйте код Грея (http://en.wikipedia.org/wiki/Gray_code#Constructing_an_n-bit_Gray_code), чтобы в наборе k были элементы, соответствующие 1-битам в k ^ (k >> 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
в простой старый C-стиль массива float
s, чтобы вам никогда не приходилось вызывать floatValue
.