浅析组合子集和的互异性

若存在正整数集合 A 和 B 且 B⊆A,则 B 集合中所有元素之和定义为 A 的一个子集和,根据定义 A 一共有 Card(A)i=0CiCard(A) 个标准子集和,即 2Card(A) 个组合子集和。但是,这些标准子集和中有些可能是相同的,所以实际子集和的个数必不多于标准子集和。

关于组合子集和的互异性的探究,先从一个问题入手。假如存在六堆货物,每堆货物都由无限多的不计质量的箱子组成,每堆货物性质相同,其中五堆是每箱重 1kg 的重物,剩余一堆是每箱质量忽略不计的泡沫。问如何在不接触箱子的情况下,用一架只能称一次的精准的天平和无限多个单位千克质量的砝码将那堆泡沫货物分辨出?

这个问题很简单。将六堆货物分别从 1 到 6 编号,再从第 i 堆货物中取出 i 个箱子,这样取出 21 个箱子放置于天平左端,在右端放置 jkg 砝码使天平平衡,泡沫货物的编号即为 21 - j。

这个算法的正确性证明如下:设泡沫货物的编号为 k,则天平左端的货物实际质量为 (21 - k)kg,即 21 – k = j,推出 k = 21 - j,得证。但是,这个问题探讨的只是集合中元素的互异性,探讨组合子集和的互异性的问题如下:若货物仍为六堆,但泡沫货物有若干堆(也可能一堆都没有),其余条件不变,问如何找出所有泡沫货物?

这个问题转化为数学语言叙述如下:求一个包含六个互异正整数元素的集合,使得该集合的标准子集和个数等于其实际子集和个数。不过,这个描述有不全面性。稍作思索,便可发现这样的集合有无数多个。于是,为了解的最优化,我们提出了两个最优解的条件:(1)使该集合中最大元素最小;(2)使该集合中所有元素之和最小。

这个问题似乎用高中知识难以解答,作者本人也无法就该问题给出一个简便的算法。如此看来,枚举似乎成为解答该问题的思维较为简洁的方法。该问题的枚举算法用伪代码的形式表现如下:

array[0 to n], m, n: integer
number[0 to n, 0 to 2 ^ n], flag: bool
DFS_Recursion(now)
    if n < now
        print array
        return
    for array[now] := array[now - 1] + 1 to 2 ^ (n - 1)
        flag := true
        for i := 0 to m
            if number[now - 1, i]
                if number[now - 1, array[now] + i]
                    flag := false
                else
                    number[now, i] := number[now, array[now] + i] := true
        m := m + array[now]
        if flag
            DFS_Recursion(now + 1)
        number[now, 0 to 2 ^ n] := false
        m := m – array[now]
Conbinated_Subaggregate_Sum(void)
    array[0] := m := 0
    n := 6
    number[0 to n, 0 to 2 ^ n] := false
    number[0, 0] := true
    DFS_Recursion(1)

该算法的时间复杂度大致为 O( 2(n2) )。

解决该问题的一种特殊情况其实有一种数论方法:二进制法,即从编号为i的货物中取出 2i1 个箱子,用数学语言描述即该六元集合可以取 {1, 2, 4, 8, 16, 32},关于其正确性的证明如下:将上述六元集合转为二进制表示为 {1, 10, 100, 1000, 10000, 100000},显而易见其实际子集和为二进制下的 0 到 111111,即十进制下的 0 到 63,共 26 个,得证。关于这一证明详见下文。

事实上,以上叙述的二进制解即为使集合内所有元素之和最小的解。而满足使集合内最大元素最小的解为 {11, 17, 20, 22, 23, 24},这一点可被证明。在每堆总箱数有限的情况下,这一组解确实可以尽可能地完成题目所叙述的情境。然而,这并不是个实际的解。

至于以上解,则衍生出了另一个可怕的问题:子集和问题,即已知砝码重量而反推出泡沫货物编号的问题,用数学语言叙述即已知集合 A 和正整数 s,求一个 A 的子集使得其所有元素之和为 s,保证有唯一解。对于二进制解来说,这一问题非常简单,由于所有自然数均有其二进制的表述,所以将s转成二进制按数位从右向左从 1 到 6 编号,高位不足补零,则当第 i 位上数值为 1 则说明 2i 为和 s 的加数之一,即编号为 i 的货物为泡沫。这也可以作为以上证明的补充。虽然如此,但是对于一般的不特殊的集合,子集和问题是一个可怕的问题,因为子集和问题已被证明为 NPC 问题。NPC 问题也称之为 NP 完全问题,是指不大可能存在在多项式时间内可以求解该问题的算法的问题。所以说,像 {11, 17, 20, 22, 23, 24} 这样的解可以说是不太现实的,因为知道和 s 后需要花费大量时间用于对其加数的分离,虽然对于六个数来说运用信息化手段进行求解时间尚可接受,但是一旦集合中元素个数多了之后,该时间将呈现指数式增长。枚举算法更是如此,但是目前除枚举算法之外还没有找到一个更易于实现而时间复杂度又低的算法。子集和问题的枚举算法伪代码如下:

array[0 to n], n, s: integer
base[0 to n]: bool
Recursion(now, sum)
    if n < now
        if s = sum
            for i := 1 to n
                if base[i]
                    print array[i]
        return
    base[i] := false
    Recursion(now + 1, sum)
    base[i] := true
    Recursion(now + 1, sum + array[i])
Subaggregate_Sum(void)
    Conbinated_Subaggregate_Sum(void)
    Recursion(1, 0)

从时间复杂度看,该枚举算法为 O( 2n )指数级的算法,可见其耗时巨大。综上所述,求解该问题的方法不仅耗时大,而且得出的解实际意义不大,易陷入 NPC 问题的陷阱。与之相反的是,数论方法二进制算法得出的解不仅在总数上最优,而且得出的解实际意义大,对解题有实际帮助。故该问题的最优解为二进制算法。

若求一元素个数为 n 的集合 A 使得 A 的组合子集和互异且已知该集合 A 的一个子集和存在一个可以在多项式时间内求出该子集的算法,则 A = { 2k | 0 ≤ k < n, k ∈ Z}。

公元二〇一六年二月七日

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值