分区设置使得笛卡尔积遵守约束

时间:2017-10-30 06:50:02

标签: javascript algorithm set combinatorics cartesian-product

我正在阅读this question,其中描述了以下问题陈述:

  

您有两个整数:NK。 Lun the dog对满足以下条件的字符串感兴趣:

     
      
  • 该字符串包含完全N个字符,每个字符都是' A'或者' B'。
  •   
  • 字符串s完全K(i, j) 0 <= i < j <= N-1),s[i] = 'A's[j] = 'B'
  •   
     

如果存在满足条件的字符串,请查找并返回任何此类字符串。否则,返回一个空字符串

我觉得这个问题等同于:

  

确定是否存在0...N-1的任何2个分区,其中笛卡尔积恰好包含K元组(i, j) i < j

元组元素表示字符串AB的字符串索引的赋值。

这产生了非常幼稚(但正确)的实现:

  • 确定集合0...N-1
  • 的所有2个分区
  • 对于每个这样的分区,生成子集的笛卡尔积
  • 对于每个笛卡尔积,计算(i, j)
  • 的元组i < j的数量
  • 选择此计数为K
  • 的任何2分区

这是JS中的一个实现:

const test = ([l, r]) =>
  cart(l, r).reduce((p, [li, ri]) => p + (li < ri ? 1 : 0), 0) === k

const indices = _.range(0, n)
const results = partitions(indices).filter(test)

您可以在原始问题here的上下文中测试结果。 n = 13k = 29的一些示例输出:

"aababbbbbbbbb", "babaaabbbbbbb", "baabababbbbbb", "abbaababbbbbb", ...

这里的第一步的复杂性就是分配集合的方式的数量:对于S(n, k)来说,这是相当令人生畏的Stirling number of the second kind k = 2

enter image description here

例如n=13这适用于4095,这不是很好。

显然,如果我们只需要一个满足要求的单个分区(这是原始问题所要求的),并且懒惰地计算所有内容,我们通常不会进入最坏的情况。但总的来说,这里的方法似乎仍然非常浪费,因为我们计算的大多数分区都不满足k中笛卡尔积中i < j元组的属性。

我的问题是,是否有一些进一步的抽象或同构可以被识别,以使其更有效。例如。是否有可能构建一个2分区的子集,以便通过构造满足笛卡尔积的条件?

2 个答案:

答案 0 :(得分:1)

(这是一种算法构建所有解决方案的方法;您可能正在寻找更多数学方法。)

在链接问题的this answer中,我给出了一种查找按字典顺序最小的解决方案的方法。这告诉您可以构建解决方案的B的最小数量。如果您转动方法并从一个所有B的字符串开始并从左侧添加A,您可以找到可以构建解决方案的最大B数。

要为此范围内的特定数量的B构建所有解,您可以再次使用递归方法,但不是仅添加B到结尾并使用N-1递归一次,您需要添加B,然后学士学位,然后是BAA ......并对所有将产生有效解决方案的案例进行递归。再次考虑N = 13和K = 29的例子,其中B的最小数量是3,最大值是10;您可以构建所有解决方案,例如4 B是这样的:

N=13 (number of digits)  
K=29 (number of pairs)  
B= 4 (number of B's)  

(13,29,4)=

(12,20,3) + "B"  
(11,21,3) + "BA"  
(10,22,3) + "BAA"  

此时你知道你已经到了将产生解决方案的案件的最后,因为(9/2) 2 &lt; 23.因此,在每个级别,您都可以使用:

N = N - length of added string  
K = K - number of A's still to be added  
B = B - 1  

当达到B为1或N-1的递归级别时,您可以构造字符串而无需进一步递归。

实际上,你所做的是你从尽可能多的B开始,然后一个接一个地向左移动它们,同时通过向右移动其他B来补偿它,直到你已经到达B的位置尽可能多的左侧。请参阅此代码段的输出:

function ABstring(N, K, B, str) {
    if ((N - B) * B < K) return;
    str = str || "";
    if (B <= 1 || B >= N - 1) {
        for (var i = N - 1; i >= 0; i--)
            str = (B == 1 && i == K || B == N - 1 && N - 1 - i != K || B == N ? "B" : "A") + str;
        document.write(str + "<br>");
    } else {
        var prefix = "B";
        --B;
        while (--N) {
            if (K - (N - B) >= 0 && B <= N)
                ABstring(N, K - (N - B), B, prefix + str);
            prefix += "A";
        }
    }
}
ABstring(13, 29, 4);

如果对B的所有值运行此代码从3到10,则得到(N,K)=(13,29)的所有194个解。您可以为B的所有值从0到N运行此算法,而不是计算B的最小和最大数量(并且一旦您不再获得解决方案就停止)。

这是(N,K,B)=(16,24,4)的模式:

enter image description here

答案 1 :(得分:0)

令P为函数,对于给定的AB字符串,返回良好对(i, j), s[i] = 'A', s[j] = 'B'的数量。

首先考虑长度N的字符串,其中B's的数量是固定的,比如说b。包含(N-b) A's的字符串。调用这组字符串S_bS_b上的最小值为0,左侧全部为B's(称为此字符串O)。 S_b上的最高P为b*(N-b),所有B's都在右侧。这是对sS_b不存在所需属性的简单检查。

考虑交换邻居BA的操作 - &gt; AB。该操作会更改+1的P.仅使用该操作(从字符串O开始)可以使用b B's构造每个字符串。如果b*(N-b) >= K s S_b B中的O具有必需属性,则会为我们提供此信息。

N-b中最右边的B's可以移动到字符串B位置的末尾。由于无法交换两个B,因此最右边B左侧的B's可以移动到最右边m_i,...移动次数{{ 1}}(0 <= m_1 <= m_2 <= ... <= m_b <= N-b)可以制作s

这样,找到长度N的所有AB字符串b B's P(s)=K其中K等同于查找整数{{1}的所有分区最多b部分,其中部分为<= N-b。要查找所有字符串,需要检查所有b b*(N-b) >= K