How do you take a number representing a list of items, and divide it into chunks, where the number of chunks is a power-of-two, AND where each chunk also has a power-of-two
(Note, the following answers the restriction on part size and the restriction on the number of parts being a power of 2. I missed the part about the number of parts also being restricted, indicating nesting. I'll try to get to that next.)
A simple proof that's also a method is that our minimal number of parts, MIN, is M = floor(N / max_power_of_2) plus the number of set bits in the binary representation of N - M*max_power_of_2; and the maximal number of parts, MAX, is N, where each part is 1.
Each time we divide one of the powers of 2, P, in the power-of-two representation of M (which starts as some count of max_power_of_2) or N-M*max_power_of_2, we have one count less of P, and two more of P/2, another power of 2. This action adds just one part to the partition, meaning we can represent any number of parts between MIN and MAX.
Greedy JavaScript code:
function f(n, maxP){
const maxPowerOf2 = 1 << maxP;
const m = ~~(n / maxPowerOf2);
const A = new Array(maxP + 1).fill(0);
A[maxP] = m;
let nm = n - m * maxPowerOf2;
let p = 0;
let bitCount = 0;
while (nm){
if (nm & 1){
bitCount += 1;
A[p] = 1;
}
nm >>= 1;
p += 1;
}
const min = m + bitCount;
let target = 1;
while (target < min)
target *= 2;
if (target > n)
return -1;
if (target == min)
return A.map((c, p) => [1 << Number(p), c]);
if (target == n)
return [n];
// Target is between MIN and MAX
target = target - min;
let i = m ? maxP : p;
while (target && i > 0){
if (!A[i]){
i -= 1;
continue;
}
const max = Math.min(target, A[i]);
A[i] -= max;
A[i-1] += 2*max;
target -= max;
i -= 1;
}
return target ? -1 : A.map((c, p) => [1 << Number(p), c]);
}
var ns = [74, 85, 87, 127, 1279, 12790, 127901, 63];
var maxP = 5;
for (let n of ns){
let result = f(n, maxP);
let [_n, numParts] = result.reduce(([_n, numParts], [p, c]) => [_n + p * c, numParts + c], [0, 0]);
console.log(n, maxP);
console.log(JSON.stringify(result));
console.log(JSON.stringify([_n, numParts]));
console.log('');
}