分区 N,其中零件的数量和每个零件的数量都是 2 的幂,并且零件的大小和数量受到限制

Lan*_*ard 6 javascript arrays algorithm math binary

你如何取一个代表项目列表的数字,并将其划分为块,其中块的数量是 2 的幂,并且每个块也有项目的 2 的幂(大小去最多为 2 的最大幂,所以 1、2、4、8、16、32、32 是最大值)?这甚至可能吗?

因此,例如,8 个项目可以分为 1 个桶(两个桶的幂)和 8 个项目(两个项目的幂):

[8]
Run Code Online (Sandbox Code Playgroud)

9个项目可能是:

[8, 1]
Run Code Online (Sandbox Code Playgroud)

这是有效的,因为两个数字都是 2 的幂,并且数组的大小是 2(也是 2 的幂)。

让我们试试 11:

[8, 2, 1]
Run Code Online (Sandbox Code Playgroud)

,那不起作用。因为数组的大小是 3,它不是 2 的幂,即使它加上 11。这个怎么样?

[4, 4, 2, 1]
Run Code Online (Sandbox Code Playgroud)

那个有效!它是 4 个元素,是 2 的幂。

[2, 2, 2, 1, 1, 1, 1, 1]
Run Code Online (Sandbox Code Playgroud)

这也有效,因为有 8 个桶(8 是 2 的幂),每个桶有 1 或 2 个项目(每个是 2 的幂)。但[4, 4, 2, 1]更好,因为它更短。

我想一个更好的(在收到评论后)会是这个,虽然我第一次没有看到它:

[8, 1, 1, 1]
Run Code Online (Sandbox Code Playgroud)

那个很短,也是从最大的数字开始的。

因此,按照这种模式,这里有一些其他数字:

13:

[8, 1, 1, 1, 1, 1] // no, 6 not power of 2
[8, 2, 1, 1, 1] // no, 5 not power of 2
[8, 2, 2, 1] // yes, 4 is power of 2
[8, 4, 1] // no, 3 not power of 2
Run Code Online (Sandbox Code Playgroud)

14:

[8, 2, 2, 2]
Run Code Online (Sandbox Code Playgroud)

15:

[8, 4, 2, 1]
Run Code Online (Sandbox Code Playgroud)

16:

[16]
Run Code Online (Sandbox Code Playgroud)

18:

[16, 2]
Run Code Online (Sandbox Code Playgroud)

200:

[32, 32, 32, 32, 32, 32, 4, 4]
Run Code Online (Sandbox Code Playgroud)

当桶树中第一层桶的大小增长到超过 32 时,则进行嵌套。所以以数字 1234 为例。这可以用 38 个 32 后接 16 个再接 4 来表示。

[32, 32, 32, 32, 32, 32, 32, 32,
 32, 32, 32, 32, 32, 32, 32, 32,
 32, 32, 32, 32, 32, 32, 32, 32,
 32, 32, 32, 32, 32, 32, 32, 32,
 32, 32, 32, 32, 32, 32, 16, 4]
Run Code Online (Sandbox Code Playgroud)

但是现在桶的大小是 40 个项目,这不是 2 的幂并且它大于 32。所以它应该嵌套!我无法在脑海中完全想象这一点很抱歉,如果这不是一个完美的例子,我想你可以理解我的意思。

// the parent "x" is 2 in length
x = [a, b]
// child "a" is 32 in length
a = [32, 32, 32, 32, 32, 32, 32, 32,
     32, 32, 32, 32, 32, 32, 32, 32,
     32, 32, 32, 32, 32, 32, 32, 32,
     32, 32, 32, 32, 32, 32, 32, 32]
// child "b" is 8 in length
b = [32, 32, 32, 32, 32, 32, 16, 4]
Run Code Online (Sandbox Code Playgroud)

再上一层,假设我们有一个非常大的数字(我不知道最小的大数字是多少),需要另一层嵌套。关于这一层,我们可以说的是,x长度为 32,但我们也会有一个y至少为 1 的。

_x_ = [a, b, c, d, e, f, g, h,
       i, j, k, l, m, n, o, p,
       q, r, s, t, u, v, w, x,
       y, z, a2, b2, c2, d2, e2, f2]
_y_ = [a3]
a   = [32, 32, 32, ..., ?]
...
f2   = [32, ..., ?]
Run Code Online (Sandbox Code Playgroud)

然后,一旦我们有_x__y__z_,... 32总的这些,我们建立在顶部的另一个层。

什么是算法/方程,它将采用一个数字并将其划分为这个桶/项目大小的树,这些树的大小都是 2 的幂,最大为 32(在本例中为 32)?

一个子目标是最小化级别的数量。没有特定的限制,但我想象在整个运行时不超过 100 万个或最多 10 亿个节点,所以看起来你可能只有 3 或 4 个级别,我不知道具体如何来计算它。

这将用于数组查找。从本质上讲,我将一个单一的、大的、任意大小的“连续”数组分解为大小为 2 的幂的小连续块,长度可达 32。这平衡了查找性能(即适合 cpu 缓存)与内存碎片。

更新

我认为尝试以某种方式合并以构建我试图描述的嵌套树会有所帮助。现在缺少的最后一件事是使嵌套数组的大小正确地调整为二的幂值......

到目前为止我能做的最好的是:

tri*_*cot 2

这是一个可能的算法:

\n

检查输入数字n的最低5位,并在数组中生成相应的2的幂。例如对于n = 13,我们得到 [1, 4, 8]

\n

n除以32,忽略上述位(下限)。

\n

将n 模 32的值添加到上面的数组中。例如,对于输入 = 77,我们得到 [1, 4, 8, 32, 32]

\n

大多数情况下,该数组的长度不会超过 32,但最多可达 36:[1, 2, 4, 8, 16, 32, ..., 32]。如果发生这种情况,请从数组末尾提取 16 个值并将它们存储在“进位”中:此进位将在稍后处理。因此,不考虑这种潜在的进位,我们确保最终得到的数组不长于 32。

\n

然后将数组中的最大值分成两半(从而使数组增加一个单位),直到数组的长度为 2 的幂。例如,对于 77 的情况,我们将有几个进行这样的迭代以获得 [1, 4, 8, 8, 8, 16, 16, 16]

\n

n32,忽略余数(下限)。

\n

再次考虑n 模 32。如果它不为零,我们就找到了 32*32 的被加数,因此我们为每个被加数创建一个新数组 [32, ..., 32],并将其与之前建立的数组组合成一个新树。例如对于 1037,我们可以得到

\n
[\n  [1,4,4,4],\n  [32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32]\n]\n
Run Code Online (Sandbox Code Playgroud)\n

如果有空间添加潜在进位(即顶层数组的长度不是 32),则这样做。

\n

如果数组的长度还不是 2 的幂,请应用与前面提到的类似的算法,尽管现在分成两半涉及数组而不是原始值。

\n

重复除以 32 以确定更高的嵌套被加数,因此这些是完整的 32*32*32 树,然后在下一次迭代中,这些是完整的 32 4树,依此类推,直到所有n都被计算在内。

\n

最后,检查进位是否仍然存在并且无法添加到某处:如果是这种情况,请向树(顶部)添加一个额外的级别,以将实现的结果与此进位组合起来,因此它们是兄弟姐妹2 的数组。

\n

执行

\n

这是一个交互式片段:输入一个数字,生成的树将实时显示。请注意,嵌套树实际上是在此实现中创建的,并且会消耗相当多的内存,因此为了保持 JavaScript 中可接受的响应时间,我将允许的输入限制为 7 位数字,但理论上唯一的限制是内存和浮动点精度(在此脚本中保证高达 2 53)。

\n

\r\n
\r\n
[\n  [1,4,4,4],\n  [32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32,32]\n]\n
Run Code Online (Sandbox Code Playgroud)\r\n
// Utility functions\nconst sum = arr => arr.reduce((a, b) => a+b, 0);\nconst powersOf2andZero = [0,1,2,4,8,16,32];\nconst clone = tree => JSON.parse(JSON.stringify(tree));\n\nfunction createTree(n) {\n    let tree = [];\n    let carry = null;\n    // Isolate 5 least significant bits\n    for (let pow of [1, 2, 4, 8, 16]) if (n & pow) tree.push(pow);\n    n = Math.floor(n / 32);\n    for (let i = n % 32; i > 0; i--) tree.push(32);\n    // This array could have more than 32 values, up to 36.\n    //   If that happens: remove 16 occurrences of 32, and consider this as carry-over for later treatment.\n    if (tree.length > 32) carry = tree.splice(-16); // pop off 16 x 32s.\n    // Make the array length a power of 2 by splitting the greatest value (repeatedly)\n    let j = tree.length;\n    while (!powersOf2andZero.includes(tree.length)) {\n        if (j >= tree.length) j = tree.indexOf(tree[tree.length - 1]); // first occurrence of greatest\n        // Split greatest value into 2; keep list sorted\n        tree.splice(j, 1, tree[j] / 2, tree[j] / 2); // delete, and insert twice the half at same spot\n        j += 2;\n    }\n    // Isolate and count factors of 32, 32\xc2\xb2, 32\xc2\xb3, ...etc. \n    //   Add a superiour level in the tree for each power of 32 that is found:\n    n = Math.floor(n / 32);\n    let template = 32;\n    while (n) {\n        if (tree.length > 1) tree = [tree]; // nest\n        if (n % 32 < 31 && carry !== null) { // we have room to dump the carry here\n            tree.push(carry);\n            carry = null;\n        }\n        template = Array(32).fill(template); // nest the template tree, "multiplying" by 32.\n        for (let i = n % 32; i > 0; i--) tree.push(clone(template));\n        if (tree.length === 1 && typeof tree[0] !== "number") tree = tree[0]; // Eliminate useless array wrapper\n        // Turn this top level into a length that is a power of 2 by splitting the longest array (repeatedly)\n        let j = tree.length;\n        while (!powersOf2andZero.includes(tree.length)) {\n            if (j >= tree.length) j = tree.findIndex(elem => elem.length === tree[tree.length - 1].length);\n            // Split longest array into 2; keep list sorted\n            let size = tree[j].length / 2;\n            tree.splice(j, 1, tree[j].slice(0, size), tree[j].slice(size)); // delete, and insert twice the half\n            j += 2;\n        }\n        n = Math.floor(n / 32);\n    }\n    // Is the carry still there? Then we cannot avoid adding a level for it\n    if (carry) return [tree, carry];\n    return tree;\n}\n\n// I/O handling\nlet input = document.querySelector("input");\nlet output = document.querySelector("pre");\n\n(input.oninput = function () {\n    let n = +input.value;\n    if (isNaN(n) || n % 1 !== 0 || n < 1 || n > 9999999) {\n        output.textContent = "Please enter an integer between 1 and 9999999";\n    } else {\n        let tree = createTree(n);\n        output.textContent = pretty(tree);\n    }\n})();\n\nfunction pretty(tree) {\n    return JSON.stringify(tree, null, 2)\n           .replace(/\\[\\s*\\d+\\s*(,\\s*\\d+\\s*)*\\]/g, m => m.replace(/\\s+/g, ""))\n           .replace(/\\b\\d\\b/g, " $&");\n}
Run Code Online (Sandbox Code Playgroud)\r\n
pre { font-size: 8px }
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n