从三角形阵列中删除点而不会丢失三角形


17

我想提出OEIS方面的组合问题 -问题是我没有足够的用语。此代码挑战是帮助我计算更多的术语,而提交者中包含最多术语的用户将是赢家。


问题

假设我给你一个三角形的灯泡,边长为n

     o
    o o
   o o o
  o o o o
 o o o o o
o o o o o o
1 2  ...  n

我将打开三个灯泡,这些灯泡形成一个“直立”等边三角形,如以下示例所示:

     o
    o x
   o o o
  o o o o
 o x o o x
o o o o o o

在我打开灯之前,您的工作是从阵列中移除尽可能多的灯泡,而又不会推论已打开的灯泡三角形的功能。为了清楚起见,如果已卸下灯泡,则打开其位置时不会点亮。

例如,如果您卸下以下灯泡(用标记.),则只会看到以下两个灯点亮(用标记x),这足以唯一地推断出第三个(未点亮)位置:

     .              .
    . o            . x
   . . o          . . o
  o o o .   =>   o o o .
 o o o o .      o x o o . <- the third unlit position
o . . . o o    o . . . o o

a(n)是可以不引入任何歧义除去灯泡的最大数量。


使用天真的算法,我检查了直到边长为7的三角形的值,如下所示:

                                                                      .
                                                      .              . o
                                        .            . o            o . o
                           .           . .          . . o          . o o .
              .           . .         . o o        o o o .        o o . o .
 .           . .         . o o       o o . o      o o o o .      o . o . o o
. .         . o o       . o o o     o . . o o    o . . . o o    o . o . o o o

a(2) = 3    a(3) = 4    a(4) = 5    a(5) = 7     a(6) = 9       a(7) = 11

计分

计算[a(2), a(3), ..., a(n)]最大n次获胜的顺序的提交。如果两个提交具有相同的顺序,则较早发布的一个将获胜。

尽管对于提交来说不是必需的,但是如上例所示,如果发布生成的三角数组的构造对我很有帮助。


1
这不是代码挑战,而是最快的代码吗?
Don Thousand

6
我认为您应该选择一个时间限制(例如60秒),因此比赛与某人花了多长时间运行他们的代码无关。
dylnan's

好问题。“直立”三角形是什么意思?
达米安

Answers:


10

Python 3n=8

import itertools
from ortools.sat.python import cp_model


def solve(n):
    model = cp_model.CpModel()
    solver = cp_model.CpSolver()
    cells = {
        (y, x): model.NewBoolVar(str((y, x)))
        for y in range(n) for x in range(y + 1)}
    triangles = [
            {cells[v] for v in ((y1, x1), (y2, x1), (y2, x1 + y2 - y1))}
            for (y1, x1) in cells.keys() for y2 in range(y1 + 1, n)]
    for t1, t2 in itertools.combinations(triangles, 2):
        model.AddBoolOr(t1.symmetric_difference(t2))
    model.Minimize(sum(cells.values()))
    solver.Solve(model)
    return len(cells) - round(solver.ObjectiveValue())


for n in itertools.count(2):
    print('a(%d) = %d' % (n, solve(n)))

使用Google OR-Tools的CP-SAT求解器。

运行约30秒后,它将输出以下内容:

a(2) = 3
a(3) = 4
a(4) = 5
a(5) = 7
a(6) = 9
a(7) = 11
a(8) = 13

n=9n6a(9)=15n=8

怎么运行的

T1T2T1T2

因此,该问题可以改写为SAT问题,每对三角形都有一个约束。

PS: 我非常想为举一个例子n=8,但是SAT解算器存在问题,后者显然希望自己保留所有解决方案。



2

从@ Delfad0r的程序获取解决方案

我将@ Delfad0r的程序扩展为输出解决方案。它还提供了中间结果,因此您将获得如下输出:

Solving n = 8:
a(8) >= 9
a(8) >= 10
a(8) >= 11
a(8) >= 12
a(8) >= 13
       o
      . o
     . o o
    . o o .
   o o . o o
  o o o o . .
 o . . o o o .
o . . o . o o o
a(8) = 13

Solving n = 9:
a(9) >= 10
a(9) >= 13
a(9) >= 14
a(9) >= 15
        o
       o o
      o . .
     o . o o
    . o . o o
   . o o o o o
  o o o . o . .
 o o o . . . o o
. o o o o o o . .
a(9) = 15

此计算花费了几个小时。

如果您不耐烦,并Ctrl-C在找到一些可能不是最佳的解决方案后按一下,程序将显示该解决方案。因此,很快就可以做到这一点:

                   .
                  o o
                 . o o
                . o o o
               o o . o o
              o . o o o .
             o . o . o o o
            . o o o o o . o
           o . . o o o o o o
          o o o o o o o o o .
         o o . o o o o . o o o
        o o o o o o . o . o o o
       o . o o . o o o o o o o o
      o o o . o o o o o . o o o o
     o o o . o o o o o o o o . . o
    o o o o o o o o o o o . o . . o
   o o o o . o o o o . o o o o o . o
  o o o o o o o o . o o . . o o o o .
 o o o o . o o . o . o o o o o o . o o
o o . o o . o o o o . o o o . o o o o o
a(20) >= 42

这是扩展程序:

import itertools
from ortools.sat.python import cp_model

class ReportPrinter(cp_model.CpSolverSolutionCallback):

    def __init__(self, n, total):
        cp_model.CpSolverSolutionCallback.__init__(self)
        self.__n = n
        self.__total = total

    def on_solution_callback(self):
        print('a(%d) >= %d' %
              (self.__n, self.__total-self.ObjectiveValue()) )

def solve(n):
    model = cp_model.CpModel()
    solver = cp_model.CpSolver()
    cells = {
        (y, x): model.NewBoolVar(str((y, x)))
        for y in range(n) for x in range(y + 1)}
    triangles = [
            {cells[v] for v in ((y1, x1), (y2, x1), (y2, x1 + y2 - y1))}
            for (y1, x1) in cells.keys() for y2 in range(y1 + 1, n)]
    for t1, t2 in itertools.combinations(triangles, 2):
        model.AddBoolOr(t1.symmetric_difference(t2))
    model.Minimize(sum(cells.values()))
    print('Solving n = %d:' % n)
    status = solver.SolveWithSolutionCallback(model, ReportPrinter(n,len(cells)))
    if status == cp_model.OPTIMAL:
        rel = '='
    elif status == cp_model.FEASIBLE:
        rel = '>='
    else:
        print('No result for a(%d)\n' % n)
        return
    for y in range(n):
        print(' '*(n-y-1), end='')
        for x in range(y+1):
            print('.o'[solver.Value(cells[(y,x)])],end=' ')
        print()
    print('a(%d) %s %d' % (n, rel, len(cells) - solver.ObjectiveValue()))
    print()

for n in itertools.count(2):
    solve(n)

1

Python 3

强烈基于Delfad0r的答案,大多数情况下都遵循相同的逻辑过程,方法是检查三角形对,并在不包含未通过此验证的三角形对的情况下验证配置。由于除了itertools和copy之外,我没有使用任何库,因此可以完全控制保存程序中遇到的示例。

examples = dict() # stores examples by key pair of n to a tuple with the triangle and number of lights turned off

for n in range(3, 8):
    tri = [] # model of the triangle, to be filled with booleans representing lights
    tri_points = [] # list of tuples representing points of the triangle
    for i in range(n):
        tri.append([True]*(i + 1))
        for j in range(i+1):
            tri_points.append((i, j))

    t_set = [] # list of all possible triangles from tri, represented by lists of points
    for i in range(n):
        for j in range(len(tri[i])):
            for k in range(1, n - i):
                t_set.append([(i, j), (i + k, j), (i + k, j + k)])

    from itertools import combinations
    import copy

    # validates whether or not a triangle of n lights can have i lights turned off, and saves an example to examples if validated
    def tri_validate(x):
        candidate_list = list(combinations(tri_points, x))
        tri_pairs = list(combinations(t_set, 2))
        for candidate in candidate_list:
            temp_tri = copy.deepcopy(tri)
            valid = False
            for point in candidate:
                (row, col) = point
                temp_tri[row][col] = False
            for pair in tri_pairs:
                valid = False
                (tri1, tri2) = pair
                for point in tri1:
                    if not valid:
                        if point not in tri2:
                            (row, col) = point
                            if temp_tri[row][col]:
                                valid = True
                for point in tri2:
                    if not valid:
                        if point not in tri1:
                            (row, col) = point
                            if temp_tri[row][col]:
                                valid = True
                if not valid:
                    break
            if valid:
                examples[n] = (temp_tri, x)
                return True
        return False

    # iterates up to the point that validation fails, then moves on to the next n
    for i in range(len(tri_points)):
        if tri_validate(i + 1):
            continue
        break

问题是,它不是很有效。到时n=5,它的运行速度确实非常快,但超过该时间点时,其运行速度将显着降低。在n=6,它大约需要一分钟才能运行,而在时要慢得多n=7。我想可以使用此程序进行很多效率修复,但这是一个好的解决方案的快速草稿,具有更大的灵活性来检查此方法的内部工作原理。随着时间的推移,我将逐步进行此工作。

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.