Partition N where the count of parts and each part are a power of 2, and part size and count are restricted

后端 未结 3 856
被撕碎了的回忆
被撕碎了的回忆 2021-01-12 15:42

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

3条回答
  •  佛祖请我去吃肉
    2021-01-12 16:10

    (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('');
    }

提交回复
热议问题