您可以先创建并填充一个二维数组,以记录有效尾部的数量,该数组称为“有效尾数”,将其称为 NVT 。具有给定值的特定位置。例如, NVT [4] [6] = 3,因为在#4位置具有6的组合可以以3种不同的方式结束:(…,6、7),(…,6, 8)和(…,6,9)。
- 要填充 NVT ,请从 NVT [ n ] […]开始,该行全为1。然后回到原来的位置;例如, NVT [2] [5] == NVT [3] [6] + NVT [3] [7] + NVT [3] [8],因为在位置#3处开始的值为5的“尾巴”将由5加上在位置#4处开始的值为6、7或8的“尾巴”组成。
- 请注意,我们不在乎是否有有效的方法到达给定的尾巴。例如, NVT [4] [1] = 3是因为有效的尾数(1、2,(1、3、3)和(1、4),即使没有完整的(em)组合形式(...,1 、、 _)。
完成此操作后,就可以计算组合 C 的等级,如下所示:
- 对于位置1,从位置1开始计数所有有效尾部,且其值小于 C [1]。例如,如果 C 以3开头,则为 NVT [1] [1] + NVT [1] [2],代表以1或2开头的所有组合。
- 然后对所有后续职位执行相同的操作。这些表示的组合将以与 C 相同的方式开始直到指定位置,但在该位置具有较小的值。
- 例如,如果 C 为(1、3、5、8、9),则会显示为
0 +
( NVT [2] [1] + NVT [2] [2])+
( NVT [3] [1] + NVT [3] [2] + NVT [3] [3] + NVT [3] [4])
( NVT [4] [1] + NVT [4] [2] + NVT [4] [3] + NVT [4] [4] + NVT [4] [5] + NVT [4] [6] + NVT [ 4] [7])+
( NVT [5] [1] + NVT [5] [2] + NVT [5] [3] + NVT [5] [4] + NVT [5] [5] + NVT [5] [6] + NVT [ 5] [7] + NVT [5] [8])。
相反,您可以找到具有给定等级 r 的组合 C ,如下所示:
- 为“剩余排名”创建一个临时变量 rr ,最初等于 r 。
- 要找到 C [1] —位置#1的值—计算从位置#1开始的有效尾巴,从最小的可能值(即1)开始,一旦超过则停止 rr 。例如,由于 NVT [1] [1] = 66和 NVT [1] [2] = 27,排名75的组合必须以2开头(因为75 ≥66和75 <66 + 27)。然后从 rr 中减去该总和(在这种情况下为75−66 = 9)。
- 然后对所有后续位置执行相同的操作,请确保记住与先前位置相同的最小值。继续使用 r = 75, C [1] = 2和 rr = 9的示例,我们知道 C [2]≥3;并且由于 NVT [2] [3] = 23 >> rr ,我们立即发现 C [2] = 3。
复杂度分析:
- 空格:
- NVT 需要 O ( nk )空间。
- 将组合返回为length- k 数组本质上需要 O ( k )空间;但是如果我们一次返回一个组合值(通过调用回调或打印到控制台等),则计算本身实际上并不依赖于此数组,只需要 O ( 1)多余的空间。
- 除此之外,其他所有内容都可以在 O (1)空间中进行管理;我们不需要任何递归或临时数组或任何其他东西。
- 时间:
- 填充 NVT 需要花费 O ( nkd )时间。 (注意:如果 d 大于 n ,那么我们可以将 d 设置为等于 n 。)< / li>
- 给出 NVT ,计算给定组合的排名将花费最坏情况下的 O ( nk )时间。
- 给出 NVT ,计算具有给定等级的组合将花费最坏情况下的 O ( nk )时间。
实施说明:上面的细节有些古怪;如果没有具体的数据需要查看,那么很容易出现一个错误,或者混淆两个变量,或者什么都不会。由于您的示例只有168个有效组合,因此我建议生成所有组合,以便在调试期间可以引用它们。
可能的其他优化:如果您期望 n 很大,并且希望对“组合”和“组合不组合”进行大量查询,那么创建组合可能会很有用第二个数组,我将其称为“ NVTLT ”,以表示“有效尾部的数量少于”,以记录从某个位置开始的有效“尾部”的数量,其值小于< / em>给定值。例如, NVTLT [3] [5] == NVT [3] [1] + NVT [3] [2] + NVT [3] [3] + NVT [3] [4],或者,如果您愿意, NVTLT [3] [5] == NVTLT [3] [4] + NVT [3] [4]。 (您可以将其作为就地转换来完成,完全覆盖 NVT ,因此它是 O ( nk )传递,没有额外的空间。 )在查询中使用 NVTLT 而不是 NVT 将使您对值进行二进制搜索,而不是线性搜索,从而给出最坏情况的 O ( k log n )时间而不是 O ( nk )时间。请注意,此优化甚至比上述优化更为棘手,因此,即使您打算执行此优化,我还是建议从上述内容开始,使其完美运行,然后再添加此优化。