Xóa các điểm khỏi một mảng tam giác mà không mất các tam giác


17

Tôi có một vấn đề về tổ hợp mà tôi muốn đặt ra cho vấn đề OEIS , đó là tôi không có đủ điều khoản. Thử thách mã này là để giúp tôi tính toán nhiều thuật ngữ hơn và người chiến thắng sẽ là người dùng có bài nộp chứa số lượng điều khoản lớn nhất.


Vấn đề

Giả sử tôi cung cấp cho bạn một mảng hình tam giác của bóng đèn có chiều dài cạnh 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

Tôi sẽ bật ba bóng đèn tạo thành một tam giác đều "thẳng đứng" như trong ví dụ sau:

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

Trước khi tôi bật đèn, công việc của bạn là loại bỏ càng nhiều bóng đèn càng tốt khỏi mảng mà không làm mất khả năng suy ra tam giác của bóng đèn đã được bật. Để rõ ràng, nếu một bóng đèn đã được gỡ bỏ, nó sẽ không sáng khi vị trí của nó được bật.

Ví dụ: nếu bạn loại bỏ các bóng đèn sau (được đánh dấu bởi .), bạn sẽ chỉ thấy hai đèn sau bật (được đánh dấu bởi x), đủ để suy ra vị trí thứ ba (không sáng):

     .              .
    . 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

Hãy a(n)là số lượng bóng đèn tối đa có thể được loại bỏ mà không đưa ra bất kỳ sự mơ hồ nào.


Thí dụ

Với thuật toán ngây thơ, tôi đã kiểm tra các giá trị lên đến một hình tam giác có cạnh dài 7, như được thấy dưới đây:

                                                                      .
                                                      .              . 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

Chấm điểm

Bài nộp tính toán chuỗi [a(2), a(3), ..., a(n)]cho n lớn nhất sẽ thắng. Nếu hai bài nộp có trình tự giống hệt nhau, thì bài được đăng trước đó sẽ thắng.

Mặc dù không cần thiết cho việc đệ trình, nhưng nó sẽ mang tính hướng dẫn cho tôi nếu bạn đăng một cấu trúc của các mảng tam giác kết quả, như trong ví dụ trên.


1
Đây không phải là một thách thức mã hơn là mã nhanh nhất?
Don Ngàn

6
Tôi nghĩ bạn nên chọn giới hạn thời gian (giả sử là 60 giây) để cuộc thi không nói về việc ai đó đã dành thời gian để chạy mã của họ.
dylnan

Vấn đề tốt đẹp. Bạn có ý nghĩa gì bởi tam giác "thẳng đứng"?
Damien

Câu trả lời:


10

Python 3 ,n=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)))

Sử dụng bộ giải CP-SAT của Google OR-Tools .

Sau khi chạy trong ~ 30 giây, nó xuất ra như sau:

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

Làm thế nào nó hoạt động

T1T2T1T2

Do đó, câu hỏi có thể được nhắc lại như một bài toán SAT, với một ràng buộc cho mỗi cặp tam giác.

Tái bút: Tôi rất muốn đưa ra một ví dụ cho n=8, nhưng tôi gặp vấn đề với người giải SAT mà dường như muốn giữ tất cả các giải pháp cho chính nó.


Tôi quyết định chuyển giải pháp cho Mathematica , không may là chậm hơn.
dùng202729

2

Nhận các giải pháp từ chương trình của @ Delfad0r

Tôi đã mở rộng chương trình của @ Delfad0r cho các giải pháp đầu ra. Nó cũng cho kết quả trung gian, vì vậy bạn nhận được đầu ra như thế này:

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

Tính toán này mất vài giờ.

Nếu bạn không kiên nhẫn và nhấn Ctrl-Csau khi tìm thấy một số giải pháp không tối ưu, chương trình sẽ hiển thị giải pháp đó. Vì vậy, không mất nhiều thời gian để có được điều này:

                   .
                  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

Đây là chương trình mở rộng:

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

Con trăn 3

Dựa mạnh vào câu trả lời của Delfad0r , chủ yếu tuân theo tiến trình logic tương tự bằng cách kiểm tra các cặp tam giác và xác thực cấu hình nếu nó không chứa cặp tam giác nào không xác thực. Vì tôi không sử dụng bất kỳ thư viện nào ngoài itertools và sao chép, tôi có toàn quyền kiểm soát các ví dụ gặp phải trong suốt chương trình.

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

Vấn đề là, nó không hiệu quả lắm. Nó chạy rất nhanh n=5, nhưng bắt đầu chậm lại đáng kể qua điểm đó. Tại n=6, phải mất khoảng một phút để chạy, và nó chậm hơn nhiều n=7. Tôi tưởng tượng có rất nhiều bản sửa lỗi hiệu quả có thể được thực hiện với chương trình này, nhưng đó là bản phác thảo nhanh chóng về một giải pháp tốt với sự linh hoạt hơn rất nhiều để kiểm tra hoạt động bên trong của phương pháp này. Tôi sẽ dần dần làm việc này theo thời gian.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.