子集总和排序


22

一组n正数具有2^n子集。如果这些子集的总和都不相同,则将其称为“ nice”。{2, 4, 5, 8}就是这样的一套。由于没有一个子集具有相同的总和,因此我们可以按总和对子集进行排序:

[{}, {2}, {4}, {5}, {2, 4}, {2, 5}, {8}, {4, 5}, {2, 8}, {2, 4, 5}, {4, 8}, {5, 8}, {2, 4, 8}, {2, 5, 8}, {4, 5, 8}, {2, 4, 5, 8}]

如果我们标记数字 [2, 4, 5, 8]用符号按升序,则将[a, b, c, d]获得以下抽象顺序:

[{}, {a}, {b}, {c}, {a, b}, {a, c}, {d}, {b, c}, {a, d}, {a, b, c}, {b, d}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}]

另一组不错的正数可以具有相同的抽象顺序,也可以具有不同的抽象顺序。例如,[3, 4, 8, 10]是一个具有不同抽象顺序的漂亮集合:

[{}, {a}, {b}, {a, b}, {c}, {d}, {a, c}, {b, c}, {a, d}, {b, d}, {a, b, c}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}]

在这个挑战中,您必须计算一组n正数的不同抽象顺序的数目。该序列为OEIS A009997,并且已知值从开始n=1是:

1, 1, 2, 14, 516, 124187, 214580603

例如,对于 n=3,以下是两种可能的抽象顺序:

[{}, {a}, {b}, {c}, {a, b}, {a, c}, {b, c}, {a, b, c}]
[{}, {a}, {b}, {a, b}, {c}, {a, c}, {b, c}, {a, b, c}]

对于n=4,以下是14种可能的抽象顺序,以及带有该顺序的示例示例:

[{}, {a}, {b}, {a, b}, {c}, {a, c}, {b, c}, {a, b, c}, {d}, {a, d}, {b, d}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 4, 2, 1]                                       
[{}, {a}, {b}, {a, b}, {c}, {a, c}, {b, c}, {d}, {a, b, c}, {a, d}, {b, d}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [10, 6, 3, 2]                                      
[{}, {a}, {b}, {a, b}, {c}, {a, c}, {d}, {b, c}, {a, d}, {a, b, c}, {b, d}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [10, 7, 4, 2]                                      
[{}, {a}, {b}, {a, b}, {c}, {a, c}, {d}, {a, d}, {b, c}, {a, b, c}, {b, d}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 6, 4, 1]                                       
[{}, {a}, {b}, {a, b}, {c}, {d}, {a, c}, {b, c}, {a, d}, {b, d}, {a, b, c}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [10, 8, 4, 3]                                      
[{}, {a}, {b}, {a, b}, {c}, {d}, {a, c}, {a, d}, {b, c}, {b, d}, {a, b, c}, {a, b, d}, {c, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 7, 4, 2]                                       
[{}, {a}, {b}, {c}, {a, b}, {a, c}, {b, c}, {a, b, c}, {d}, {a, d}, {b, d}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [10, 4, 3, 2]                                      
[{}, {a}, {b}, {c}, {a, b}, {a, c}, {b, c}, {d}, {a, b, c}, {a, d}, {b, d}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 4, 3, 2]                                       
[{}, {a}, {b}, {c}, {a, b}, {a, c}, {d}, {b, c}, {a, d}, {a, b, c}, {b, d}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 5, 4, 2]                                       
[{}, {a}, {b}, {c}, {a, b}, {a, c}, {d}, {a, d}, {b, c}, {a, b, c}, {b, d}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [10, 7, 6, 2]                                      
[{}, {a}, {b}, {c}, {a, b}, {d}, {a, c}, {b, c}, {a, d}, {b, d}, {a, b, c}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 6, 4, 3]                                       
[{}, {a}, {b}, {c}, {a, b}, {d}, {a, c}, {a, d}, {b, c}, {b, d}, {a, b, c}, {c, d}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [10, 8, 6, 3]                                      
[{}, {a}, {b}, {c}, {d}, {a, b}, {a, c}, {b, c}, {a, d}, {b, d}, {c, d}, {a, b, c}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [8, 6, 5, 4]                                       
[{}, {a}, {b}, {c}, {d}, {a, b}, {a, c}, {a, d}, {b, c}, {b, d}, {c, d}, {a, b, c}, {a, b, d}, {a, c, d}, {b, c, d}, {a, b, c, d}], [7, 6, 5, 3]

以下不是有效的抽象顺序:

{}, {a}, {b}, {c}, {d}, {a,b}, {e}, {a,c}, {b,c}, {a,d}, {a,e}, {b,d}, {b,e}, {c,d}, {a,b,c}, {a,b,d}, {c,e}, {d,e}, {a,b,e}, {a,c,d}, {a,c,e}, {b,c,d}, {b,c,e}, {a,d,e}, {b,d,e}, {a,b,c,d}, {c,d,e}, {a,b,c,e}, {a,b,d,e}, {a,c,d,e}, {b,c,d,e}, {a,b,c,d,e}

此顺序表示:

d < a + b
b + c < a + d
a + e < b + d
a + b + d < c + e

将这些不平等现象相加得出:

2a + 2b + c + 2d + e < 2a + 2b + c + 2d + e

这是一个矛盾。您的代码不得计算此顺序。这样的反例首先出现在n=5。来自的例子本文,例如2.5 3页上。

尽管A < B暗示了A U C < B U C,对于和的任何C脱节,该排序都是无效的。AB


您的代码或程序必须足够快才能在以下位置运行它才能完成 n=4在提交之前。

提交的内容可能与往常一样是程序,功能等。

与往常一样,标准漏洞是被禁止的。这是代码高尔夫球,因此最短答案以字节为单位。随时在评论中提出澄清的问题。


好久不见以撒!
orlp

PPPpPqpq一种bC

答:可以。 是不够紧,例如:{ 一个Ç } { b c ^ }pPqpq{一种C}{bC}
orlp

@orlp好回来了!我认为在可预见的未来,我将主要做一些问题
isaacg '18

您还可以为n = 4添加14种可能的排序吗?
彼得·泰勒

Answers:


11

蟒3 + SciPy的,396 390 385 351 336 355字节

from scipy.optimize import*
n=int(input())
r=range(n)
def f(u):
 s=linprog(r,u,[-n]*len(u),options={'tol':.1});c=s.success;y=sorted(range(c<<n),key=lambda a:s.x.round()@[a>>i&1for i in r])
 for a,b in zip(y,y[1:]):
  v=[(a>>i&1)-(b>>i&1)for i in r]
  if~-(v in u):c+=f(u+[[-z for z in v]]);u+=v,
 return+c
print(f([[(i==j-1)-(i==j)for i in r]for j in r]))

在线尝试!

现在运行 在大约5秒钟内 n = 5。该if~-(v in u):可-18字节,但是一个巨大的性能损失被删除。

如果要打印找到的所有抽象顺序而不是仅对它们进行计数,请if c:print(s.x.round(),y)for循环之前添加。(子集由二进制整数表示,其中每个位对应于一个元素的存在或不存在:{ acd }↔1101 2 =13。)

怎么运行的

f递归地计算满足给定约束列表的抽象顺序。我们先从约束ñ + ñbb + ñçç + ñd。使用线性编程,我们找到了约束的解决方案(如果没有约束,则返回0)—在这种情况下,我们得到a = 4,b = 8,c = 12,d =16。我们将解决方案四舍五入为整数,然后按照所有子集的总和对它们进行排序,从而计算出参考排序:

{ a },{ b },{ c },{ ab },{ d },{ ac },{ ad },{ bc },{ bd },{ abc },{ cd },{ abd },{ acd },{ bcd },{ abç d }

舍入不能使任何约束都违反n / 2以上,这就是为什么我们增加了n的余量

由于Python sorted是稳定的,因此子集之间的任何联系都以我们生成它们的相同逆字典顺序被打破。因此,我们可以想象用{ a ·2 ^ n替换{ abcd } + 2 ^ 0,b ·2 ^ Ñ + 2 ^ 1,Ç ·2 ^ Ñ + 2 ^ 2,d ·2 ^ n + 2 ^ 3}以获得没有任何约束的相同顺序。

计划是通过案例分析,基于所有其他抽象顺序首先与参考顺序不一致的地方,对它们进行分类:

{ a }> { b }
或{ a } <{ b }> { c }
或{ a } <{ b } <{ c }> { ab }
或{ a } <{ b } < { c } <{ ab }> { d },

在每种情况下,我们以n的余量添加这些新约束,然后f以添加的新约束递归调用。

笔记

有一阵子我猜想(但没有假设)约束上有余量为1的线性程序解将始终是整数。事实证明这是错误的:n = 7 的反例是{2.5,30,62.5,73.5,82,87.5,99.5}。

Python,606字节(速度更快,没有外部库)

n=int(input())
r=range(n)
e=enumerate
def l(u,x):
 for i,v in e(u):
  for j,a in e(v):
   if a<0:break
  else:return[0]*len(x)
  if sum(b*x[k]for k,b in e(v))>0:
   x=l([[b*w[j]-a*w[k]for k,b in e(v)if k!=j]for w in u[:i]],x[:j]+x[j+1:]);x.insert(j,0)
   for k,b in e(v):
    if k!=j:x[j]+=b*x[k];x[k]*=-a
 return x
def f(u,x):
 x=l(u,x);c=any(x);y=sorted(range(c<<n),key=lambda a:sum(x[i]*(a>>i&1)for i in r))
 for a,b in zip(y,y[1:]):
  v=[(a>>i&1)-(b>>i&1)for i in r]+[1]
  if~-(v in u):c+=f(u+[[-z for z in v[:-1]]+[1]],x);u+=v,
 return+c
print(f([[(i==j-1)-(i==j)for i in r]+[1]for j in r],[1]*(n+1)))

在线尝试!

这会在四分之一秒内运行n = 5,在230秒内运行n = 6(在PyPy中为75秒)。

它包括一个手动编码的线性规划求解器,该求解器在齐次坐标中使用整数数学运算,以避免出现浮点舍入问题。



@ Mr.Xcoder当然,谢谢!
安德斯·卡塞格

@琳恩谢谢!我妥协了一下,因为我不想慢下来太多了,它已经需要近3分钟N = 5
安德斯Kaseorg

1
@AlonAmit看起来花了大约55分钟,n =6。SciPy并不是LP中最好的。我有一个使用GLPK而不是SciPy的版本在70秒内执行n = 6。更令人担忧的是,SciPy版本得到了错误的答案(而GLPK是正确的答案)……所以,……很有趣……我想知道这是否是SciPy#6690
安德斯·卡塞格

1
@AlonAmit#6690是不是。但我补充说options={'tol':.1},这似乎可以解决这个问题。
安德斯·卡塞格

0

Ruby,308个字节,更快

在〜150ms内运行案例4。没有使用专门的库。

->n{t=2**(n-1)
n==0 ?[[0]]:P[n-1].map{|a|b=a.map{|i|i+t}
[*0..t].repeated_combination(t).select{|m|m[0]>=a.index(n-1)}.map{|m|c,d=a.dup,b.dup;m.reverse.map{|i|c.insert(i,d.pop)};c}}.flatten(1).select{|p|p.combination(2).all?{|(x,y)|x&~y==0||y&~x!=0&&n.times.all?{|i|x!=y<<i+1}&&p.index(x&~y)<p.index(y&~x)}}}

例如,它递归地散布小案件的结果

[{}, {a}, {b}, {c}, {a, b}, {a, c}, {b, c}, {a, b, c}]

并添加了附加元素的相应子集-它们必须保持相同的相对顺序。它还确保在所有先前的单例之后添加新的单例。

检查符合性的部分与以前相同,但是要测试的组合要少得多。

扩展和注释版本:

->n{
    t=2**(n-1)
    if n==0
        [[0]]
    else
        # for each one of the previous nice orderings
        P[n-1].map { |a|
            # create the missing sets, keep order
            b = a.map{|i|i+t}
            # intersperse the two sets
            [*0..t].repeated_combination(t) # select t insertion points
                .select do |m|
                    # ensure the new singleton is after the old ones
                    m[0] >= a.index(n-1)
                end
                .map do |m|
                    # do the interspersion
                    c,d=a.dup,b.dup
                    m.reverse.map{|i|c.insert(i, d.pop)}
                    c
                end
        }.flatten(1).select{ |p|
            # check if the final ordering is still nice
            p.combination(2).all? { |(x,y)|
                (x&~y==0) || 
                (y&~x!=0) && 
                n.times.all?{|i|x!=y<<i+1} && 
                (p.index(x&~y)<p.index(y&~x))
            }
        }
    end
}

Ruby,151个字节,相当慢

(三个元素的情况取<< 1,四个仍然在运行)

->n{[*1...2**n-1].permutation.select{|p|p.combination(2).all?{|(x,y)|x&~y==0||y&~x!=0&&n.times.all?{|i|x!=y<<i+1}&&p.index(x&~y)<p.index(y&~x)}}.count}

它适用于子集的位域表示,因此如果需要显示子集本身,则可能必须对输出进行处理。

格式化:

-> n {
  [*1...2**n-1]. # prepare permutations of non-empty and non-full sets
    permutation.
    select { |p|
      p.combination(2). # check all ordered pairs
        all? { |(x, y)|
          # first is subset of second 
          x &~ y == 0 ||
          # second is not subset of first
          y &~ x != 0 &&
          # first is not a right shift of second
          # (this normalizes the ordering on atoms)
          n.times.all? { |i| x != y << i+1 } &&
          # after taking out common elements, ordering agrees 
          p.index(x &~ y) < p.index(y &~ x)
        }
    }.
    count
}

我无法在我的机器上测试高于3的值,但此值(139字节)应在功能上与您的解决方案相同。更改:...x-1=> ..x-2.select{...}.count=> .count{...}|(x,y)|=> |x,y|x&~y==0||y&~x!=0=> x&~y<1||y&~x>0因为a&~b如果我没有记错的话就不能为负
Asone Tuhid

1
看一下n=5我刚刚添加的反例。如果我没记错的话,您的代码会接受它。
isaacg

2
TIO链接在反例中无法正常显示:在线尝试!
isaacg

1
您的较新版本似乎是一个名为的递归函数P,因此它不能是匿名的。另外,由于我发布的反例,我认为它仍然失败。
isaacg

1
对于更快的解决方案:280字节在线尝试!。请注意,您必须包含一个递归函数的名称(P=)。另外,我认为您必须返回一个数字,因此您可能必须将其合并.size到某个地方。
Asone Tuhid
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.