求特征属性的第k个二进制数的算法

时间:2013-12-16 00:13:52

标签: algorithm binary sequence catalan

我们假设我们将考虑长度为2nn的二进制数可能约为1000。我们正在寻找具有以下属性的kth号码(k受10^9限制):

  • 1's的金额等于0's的金额,可以描述如下:#(1) = #(0)
  • 此号码的每个前缀必须至少包含0's 1's。否定句子后可能更容易理解它,即:没有前缀包含1's而不是0's

基本上就是这样。 所以要说清楚,让我们做一些例子: n=2k=2 我们必须采用长度为2n的二进制数:

0000
0001
0010
0011
0100
0101
0110
0111
1000
and so on...

现在我们必须找到满足这两个要求的2nd号码。因此,我们看到0011是第一个,0101是第二个。 如果我们更改k=3,则答案不存在,因为存在具有相同数量的相反位的数字,但对于0110,存在前缀011,因此数字不满足第二约束和所有以1为最高位的数字相同。

那么我到目前为止所做的是找到算法?

我的第一个想法是生成所有可能的位设置,并检查它是否具有这两个属性,但生成它们都需要O(2^(2n)),这不是n=1000的选项。

此外,我发现没有必要检查所有小于0011的{​​{1}},n=2 000111的数字,等等......坦率地说那些一半最重要的位仍然是“未被触及”的,因为这些数字不可能满足n=3条件。使用它我可以将#(1) = #(0)减少一半,但它没有多大帮助。而不是2 *永远我有永远运行算法。它仍然是n复杂度,这太大了。

算法有什么想法吗?

结论

这篇文章是在读完安迪·琼斯的帖子之后根据我的想法创建的。

首先,我不会发布我用过的代码,因为它是Andy的帖子Kasa 2009中的第6点。您所要做的就是将O(2^n)视为我所描述的nr。取消Dyck单词算法,可以帮助我们更快地找到答案。但它有一个瓶颈。

k

考虑到while (k >= C(n-i,j)) ,加泰罗尼亚数字可能非常大,甚至n <= 1000。我们可以使用一些大数字算术,但另一方面我提出了一个小技巧来绕过它并使用标准整数。

我们不想知道加泰罗尼亚数字实际有多大,只要它大于C(999,999)。所以现在我们将在k表中创建缓存部分和的加泰罗尼亚数字。

n x n

要生成它是非常简单的:

...                                     ...
5 |                              42     ...
4 |                        14    42     ...
3 |                   5    14    28     ...
2 |             2     5     9    14     ...
1 |       1     2     3     4     5     ...
0 | 1     1     1     1     1     1     ...
   ----------------------------------   ...
    0     1     2     3     4     5     ...

所以我们只能看到这一点:

C(x,0) = 1
C(x,y) = C(x,y-1) + C(x-1,y)  where y > 0 && y < x
C(x,y) = C(x,y-1) where x == y

会导致溢出。

让我们停在这一点并提供定义。

C(x,y) = C(x,y-1) + C(x-1,y) where y > 0 && y < x - 它不是整数的实际溢出,而是k-flow的值大于C(x,y)的信息。

我的想法是在每次运行上述公式后检查k是否大于C(x,y)或任何和组件是k。如果是,我们会将-1代替-1作为标记,k-flow已经发生。我想很明显,如果k-flow数字与任何正数相加,那么它仍为k-flowed,特别是2 k-flowed个数的总和为k-flowed

我们必须证明的最后一点是,不可能创造真正的溢出。真正的溢出可能只发生在我们总结a + b,其中不存在k-flowed,但总和它们会产生真正的溢出。

当然这是不可能的,因为最大值可以描述为a + b <= 2 * k <= 2*10^9 <= 2,147,483,647,其中此不等式中的最后一个值是带符号的int的值。我还假设int有32位,就像我的情况一样。

2 个答案:

答案 0 :(得分:4)

您描述的数字与Dyck words相对应。 Kasa 2009的第2点给出了一个简单的算法,用于按字典顺序枚举它们。如果你想进一步阅读,它的参考应该是有用的。

作为一个旁边(并且警告我在写这篇文章时我睡着了,所以这可能是错的),维基百科文章指出长度为2n的Dyck单词的数量是{{1}加泰罗尼亚号,n。您可能希望找到最小的C(n),使n大于您要查找的C(n),然后从k开始枚举Dyck字词。

答案 1 :(得分:0)

我很抱歉上次误解了这个问题,所以我编辑它,现在我可以保证更正,你可以先测试代码,复杂度为O(n^2),详细答案如下


首先,我们可以将问题等同于下一个问题

我们正在寻找具有以下属性的kth largest号码(k受10^9限制):

  • 1's的金额等于0's的金额,可以描述如下:#(1) = #(0)
  • 此号码的每个前缀必须至少包含[[1's0's]],这意味着:没有前缀包含更多[[0's 1's]]。

让我们举一个例子来解释一下:让n=3k=4,满意的数量是5,下面的图片解释了我们应该在之前的问题中确定什么新问题:

|                       000111  ------>    111000                          ^
|                       001011  ------>    110100                          |
|                       001101  ------>    110010                          |
|  previous 4th number  010011  ------>    101100  new 4th largest number  |
v                       010101  ------>    101010                          |

所以在我们解决了新问题之后,我们只需按位而不是。

现在主要问题是如何解决新问题。首先,让A为数组,因此A[m]{1<=m<=2n}只能是1或0,让DP[v][q]{A[2n-v+1]~A[2n]}中满足条件2和条件#(1)= q的数量,所以DP[2n][n]是满意数量。

A[1]只能为1或0,如果A[1]=1,则数量为DP[2n-1][n-1],如果为A[1]=0,则数量为DP[2n-1][n] ,现在我们要查找kth largest号码,如果k<=DP[2n-1][n-1]kth largest号码的A[1]必须为1,那么我们可以A[2]判断DP[2n-2][n-2] };如果k>DP[2n-1][n-1]kth largest号码A[1]必须为0且k=k-DP[2n-1][n-1],则我们可以A[2]判断DP[2n-2][n-1]。因此,使用相同的理论,我们可以逐个判断A[j],直到没有可比较的数字。现在我们举一个例子来理解(n=3, k=4)

(我们使用dynamic programming确定DP矩阵,DP方程 DP [v] [q] = DP [v-1] [q-1] + DP [v- 1] [q]

   Intention: we need the number in leftest row can be compared,
              so we add a row on DP's left row, but it's not include by DP matrix
              in the row, all the number is 1.
              the number include by bracket are initialized by ourselves
              the theory of initialize just follow the mean of DP matrix

   DP matrix = (1) (0) (0) (0)                4<=DP[5][2]=5  -->  A[1]=1
               (1) (1) (0) (0)                4>DP[4][1]=3  -->  A[2]=0, k=4-3=1
               (1) (2) (0) (0)                1<=DP[3][1]=3  -->  A[3]=1
               (1) (3)  2  (0)                1<=1  -->  a[4]=1
               (1) (4)  5  (0)                no number to compare, A[5]~A[6]=0
               (1) (5)  9   5                 so the number is 101100

如果您不清楚,可以使用代码来理解

意向 DP[2n][n]增长非常快,因此代码只能在问题n<=19中的n<1000时有效,因此您可以使用大数编程,并且可以通过位操作优化代码,因此代码只是一个参考

/*-------------------------------------------------- 
    Environment: X86 Ubuntu GCC 
    Author: Cong Yu 
    Blog: aimager.com                              
    Mail: funcemail@gmail.com                      
    Build_Date: Mon Dec 16 21:52:49 CST 2013 
    Function: 
--------------------------------------------------*/

#include <stdio.h>

int DP[2000][1000];
// kth is the result
int kth[1000];

void Oper(int n, int k){
    int i,j,h;
    // temp is the compare number
    // jishu is the 
    int temp,jishu=0;

    // initialize
    for(i=1;i<=2*n;i++)
        DP[i-1][0]=i-1;
    for(j=2;j<=n;j++)
        for(i=1;i<=2*j-1;i++)
            DP[i-1][j-1]=0;
    for(i=1;i<=2*n;i++)
        kth[i-1]=0;

    // operate DP matrix with dynamic programming
    for(j=2;j<=n;j++)
        for(i=2*j;i<=2*n;i++)
            DP[i-1][j-1]=DP[i-2][j-2]+DP[i-2][j-1];

    // the main thought
    if(k>DP[2*n-1][n-1])
        printf("nothing\n");
    else{
        i=2*n;
        j=n;
        for(;j>=1;i--,jishu++){
            if(j==1)
                temp=1;
            else
                temp=DP[i-2][j-2];

            if(k<=temp){
                kth[jishu]=1;
                j--;
            }
            else{
                kth[jishu]=0;
                if(j==1)
                    k-=1;
                else
                    k-=DP[i-2][j-2];
            }
        }
        for(i=1;i<=2*n;i++){
            kth[i-1]=1-kth[i-1];
            printf("%d",kth[i-1]);
        }
        printf("\n");
    }
}

int main(){
    int n,k;
    scanf("%d",&n);
    scanf("%d",&k);
    Oper(n,k);
    return 0;
}