Menghapus poin dari array segitiga tanpa kehilangan segitiga

17

Saya memiliki masalah kombinatorik yang ingin saya gunakan pada OEIS — masalahnya adalah saya tidak memiliki persyaratan yang cukup. Tantangan kode ini adalah untuk membantu saya menghitung lebih banyak istilah, dan pemenangnya adalah pengguna dengan kiriman yang berisi jumlah syarat terbesar.


Masalah

Misalkan saya memberi Anda array segitiga bola lampu dengan panjang sisi 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

Saya akan menyalakan tiga bola lampu yang membentuk segitiga sama sisi "tegak" seperti dalam contoh berikut:

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

Sebelum saya menyalakan lampu, tugas Anda adalah menghilangkan sebanyak mungkin bola lampu dari array — tanpa kehilangan kemampuan untuk menyimpulkan segitiga bola lampu yang telah dinyalakan. Untuk menjadi jelas, jika bola lampu telah dilepas, itu tidak menyala ketika posisinya dihidupkan.

Misalnya, jika Anda melepas lampu berikut (ditandai oleh .), Anda hanya akan melihat dua lampu berikut menyala (ditandai oleh x), yang cukup unik untuk menyimpulkan posisi ketiga (tidak menyala):

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

Membiarkan a(n)menjadi jumlah maksimum umbi yang dapat dihapus tanpa memperkenalkan ambiguitas.


Contoh

Dengan algoritma naif, saya telah memeriksa nilai hingga segitiga dengan panjang sisi 7, seperti yang terlihat di bawah ini:

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

Mencetak gol

Kiriman yang menghitung urutan [a(2), a(3), ..., a(n)]untuk n terbesar menang. Jika dua pengiriman memiliki urutan yang sama, maka yang dikirim sebelumnya menang.

Meskipun tidak perlu untuk pengajuan, akan bermanfaat bagi saya jika Anda memposting konstruksi array triangluar yang dihasilkan, seperti pada contoh di atas.

Peter Kagey
sumber
1
Bukankah ini tantangan kode daripada kode tercepat?
Don Thousand
6
Saya pikir Anda harus memilih batas waktu (katakanlah, 60-an) sehingga kontes ini bukan tentang berapa lama seseorang menghabiskan waktu menjalankan kode mereka.
dylnan
Masalah bagus Apa yang Anda maksud dengan segitiga "tegak"?
Damien

Jawaban:

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

Menggunakan pemecah CP-SAT Google OR-Tools .

Setelah berjalan selama ~ 30 detik, ini menghasilkan yang berikut:

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

Bagaimana itu bekerja

T1T2T1T2

Dengan demikian pertanyaan tersebut dapat diulang sebagai masalah SAT, dengan satu kendala untuk setiap pasangan segitiga.

PS: Saya sangat ingin memasukkan contoh n=8, tapi saya mengalami masalah dengan pemecah SAT yang tampaknya ingin menyimpan semua solusi untuk dirinya sendiri.

Delfad0r
sumber
Saya memutuskan untuk port solusi ke Mathematica , yang sayangnya lebih lambat.
user202729
2

Mendapatkan solusi dari program @ Delfad0r

Saya memperluas program @ Delfad0r untuk menghasilkan solusi. Ini juga memberikan hasil menengah, sehingga Anda mendapatkan output seperti ini:

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

Perhitungan ini memakan waktu beberapa jam.

Jika Anda menjadi tidak sabar dan menekan Ctrl-Csetelah beberapa solusi yang mungkin tidak optimal ditemukan, program akan menunjukkan solusi itu. Jadi tidak butuh waktu lama untuk mendapatkan ini:

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

Berikut adalah program lanjutannya:

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)
Sievers Kristen
sumber
1

Python 3

Didasarkan kuat pada jawaban Delfad0r , sebagian besar mengikuti perkembangan logika yang sama dengan memeriksa pasangan segitiga dan memvalidasi konfigurasi jika tidak mengandung pasangan segitiga yang gagal validasi ini. Karena saya tidak menggunakan perpustakaan apa pun selain itertools dan salin, saya memiliki kontrol penuh untuk menyimpan contoh yang ditemukan di seluruh program.

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

Masalahnya, itu tidak terlalu efisien. Ini berjalan sangat cepat hingga n=5, tetapi mulai melambat secara signifikan melewati titik itu. Pada n=6, dibutuhkan sekitar satu menit untuk berlari, dan itu jauh lebih lambat n=7. Saya membayangkan ada banyak perbaikan efisiensi yang dapat dilakukan dengan program ini, tetapi ini merupakan rancangan kasar dari solusi yang baik dengan lebih banyak fleksibilitas untuk memeriksa cara kerja metode ini. Saya akan secara bertahap mengerjakan ini dari waktu ke waktu.

TCFP
sumber