在数字不减的数字序列中找到 N 的索引不重构序列

问题描述

考虑数字按非降序排列的所有非负整数的序列,即从左到右排序的数字(OEIS 中的 A009994)。此序列以 0 开头,包括 29、33 和 112 等数字,但不包括 30、31、32 或 105。

现在,给定一个数字不减的数字 N,我需要找到它在序列中的位置。或者,我需要找出有多少个非减数字小于 N。

如果我们简单地生成序列中的每个数字直到找到 N,这可以在 O(N) 时间内完成。此外,我们可以预先计算序列直到某个最大数字,然后使用二分搜索来找到索引在 O(log N) 中。但是因为在我的程序中,对于不同的 N 值,这个计算需要重复数千次(在一个内部循环内),如果我有一个更有效的算法甚至一个封闭的公式来计算索引,那就太好了。

以防万一,在我的程序中,N 是一个以 7 为基数的四位数字,即 0 7。

我程序中 N 的 maximum index 是 C(7+4-1,4) - 1 = 10! / 4! / 6! - 1 = 209。

编辑:由于一些贡献者强调涉及重建序列的解决方案实际上有多好,我想指出内存使用也很重要。我承认上述解决方案并不可怕,但它们确实看起来很浪费。寻找排列索引的有点类似的问题有 a simple solution,不需要重构序列。

解决方法

我找到了一个封闭式解决方案,扩展了 A009994 是按字典顺序重复的组合序列的概念。

解决方案总结为以下Python代码:

>>> from math import factorial as fact
>>> C = lambda n,k: n>=k and fact(n) // fact(k) // fact(n-k) or 0
>>> I = lambda a,b,c,d: C(10,4) - C(9-a,4) - C(8-b,3) - C(7-c,2) - C(6-d,1) - 1
>>> I(0,0)
0
>>> I(2,2,3,5)
147
>>> I(6,6,6)
209

说明

让我们考虑以 10 为基数从左到右排序的 3 位 (S=3) 数字的序列 K (B=10)

N 中的任何数字 S 都可以通过其中出现的每个数字的计数来唯一标识,例如如果我们有 1 次出现数字“2”和 2 次出现数字“3”,那么 N 必须是“233”。

知道所有可用数字后,我们可以使用 stars & bars notation 来表示 N。在基数 10 中,我们有 10 个可用数字,因此我们需要 B-1=9 条来分隔槽:

|||||||||

对于 N = 233,将星星放入它们的位置:

||*|**||||||

现在我们有一个 B-1+K=12 个对象(9 条 + 3 星)的列表。列表中星星的位置(从0开始)为:(2,4,5).

简而言之,要将任何 N 转换为其星形和条形 3 元组表示,我们只需将 N 中的每个数字添加到其位置 N 本身:

233 -> (2+0,3+1,3+2) -> (2,5)   (formula I)

同样,这个 3 元组唯一标识以 10 为基数的任何 3 位数字,其中的数字已排序。相反,为了生成任何数字排序的 3 位数字,我们可以从 12 个位置 {0,1,...,10,11} 的集合中选择三个不同的项目。 S 和所有可能的 3 元组的集合 T 之间存在一一对应的关系,这些三元组具有从 0 到 11 的不同数字。

这种表示的优点是不会发生重复,因为两颗星不能在同一位置。这意味着 T 只是 12 个项目的 3-combinations 的集合。

也很容易看出,按字典顺序排序 T 等同于按数字排序 S

从这点来说,我们可以使用教科书的公式进行组合。如问题中所述,这就是我用来查找 S 长度的方法。我不知道的是 formula to find the position of a combination in the list of all possible combinations sorted by ascending order 是:

I = C(n,k) - C(n-p1,k) - C(n-p2,k-1) - ... - C(n-pk,1)  (formula II)

哪里:

  • C 是 binomial coefficient:C(n,k) = n! / (k!* (n-k)!)
  • n 是可用项目的总数(在我们的例子中 n=B-1+K=12)
  • k 是每个组合中的项目数 (k=K=3)
  • p1,p2,pk 是选择的项目(即我们的三元组的项目)

最后,将公式 I 塞入公式 II,我们有:

I = C(n,k) - C(n-d1,k) - C(n-d2+1,k-1) - ... - C(n-dk+k-1,1)

其中 d1,d2,dk 是 N 的数字

注意:当 N 的最高位数为 B-1=9 时,此公式将包括 C(n,k) 其中 n

这是问题的一般解决方案。

,

但是因为在我的程序中这个计算需要重复 对于不同的 N 值,数千次(在内部循环内), 如果我有一个更有效的算法就好了

让我们对不构建和索引序列的时间成本进行全面检查。让我们实现一个头脑简单的基于字典的解决方案:

from numpy import base_repr

table = {}

for number in range(int('6666',7) + 1):
    representation = base_repr(number,base=7)

    a,d,*rest = representation + '7' * 3

    if b >= a and c >= b and d >= c:
        table[int(representation)] = len(table)

计时一百万次查找,在我的系统上,这比您的“通用解决方案”快 15 倍。但是 2/5 的更快时间用于生成表。让我们更进一步,静态定义表:

table = {
    0: 0,1: 1,2: 2,3: 3,4: 4,5: 5,# ...
    4666: 204,5555: 205,5556: 206,5566: 207,5666: 208,6666: 209,}

在我的系统上进行一百万次查找,这比您的“通用解决方案”快 25 倍。

我们仍然有办法找到一个闭式公式解决方案,它也是一种高效算法

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...