Üçgenleri kaybetmeden üçgen bir diziden noktaları kaldırma


17

OEIS'e koymak istediğim bir kombinatorik problemim var - sorun, yeterli terimlerim olmaması. Bu kod zorluğu, daha fazla terim hesaplamama yardımcı olmaktır ve kazanan, gönderimi en fazla sayıda terim içeren kullanıcı olacaktır.


Sorun

Size yan uzunluğu olan üçgen bir ampul dizisi verdiğimi varsayalım :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

Aşağıdaki örnekte olduğu gibi "dik" bir eşkenar üçgen oluşturan üç ampulü açacağım:

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

Işıkları açmadan önce işiniz, açılmış ampullerin üçgenini çıkarma yeteneğini kaybetmeden diziden olabildiğince fazla ampulü çıkarmaktır. Açıkça söylemek gerekirse, bir ampul çıkarılmışsa, konumu açıldığında yanmaz.

Örneğin, aşağıdaki ampulleri (ile işaretlenmiş .) çıkardıysanız, yalnızca aşağıdaki iki ışığın yandığını (işaretlendiğini x) görürsünüz;

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

Izin vermek a(n)herhangi bir belirti vermeden kaldırılabilir maksimum ampul sayısı olsun .


Misal

Saf bir algoritma ile, aşağıda görüldüğü gibi kenar uzunluğu 7 olan bir üçgene kadar değerleri kontrol ettim:

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

puanlama

En [a(2), a(3), ..., a(n)]büyük n dizisini hesaplayan gönderim kazanır. İki gönderinin özdeş dizileri varsa, daha önce gönderilenler kazanır.

Sunum için gerekli olmamakla birlikte, yukarıdaki örnekte olduğu gibi, sonuçtaki triangluar dizilerinin bir yapısını yayınlarsanız bana öğretici olacaktır.


1
Bu en hızlı koddan ziyade bir kod zorluğu değil mi?
Don Bin

6
Sanırım bir zaman sınırı seçmelisiniz (diyelim ki 60'lar), yarışma birisinin kodunu çalıştırmak için ne kadar zaman harcadığıyla ilgili değil.
dylnan

Güzel sorun. "Dik" üçgen ile ne demek istiyorsun?
Damien

Yanıtlar:


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

Kullanımları Google YA-Araçlar 'CP-SAT çözücüsü.

~ 30 saniye koştuktan sonra aşağıdakileri verir:

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

n=9Muhtemelen saat alacak gibi beklemeye çalışmadım (kısıtlamaların sayısı gibi büyür )n6 . 30 dakikadan kısa bir hesaplamadan sonra bunu öğrendim a(9)=15. n=8Şu anda zaman kısıtlamaları belirsiz olduğu için skorumu bırakıyorum , ancak yarım saat muhtemelen çok uzun.

Nasıl çalışır

İki farklı eşkenar üçgen ve . Belirsizliği önlemek için, tepe noktasında ve tam olarak birine ait en az bir ampul .T 2 T 1 T 2T1T2T1T2

Böylece soru her bir üçgen çifti için bir kısıtlama ile bir SAT problemi olarak yeniden ifade edilebilir.

PS: Ben çok bir örnek eklemek istiyorum n=8, ama görünüşe göre çözümleri kendisi için tutmak isteyen SAT çözücü ile sorunlar yaşıyorum.


Çözümü ne yazık ki daha yavaş olan Mathematica'ya taşımaya karar verdim .
user202729

2

@ Delfad0r programından çözüm alma

@ Delfad0r programını çözüm üretmek için genişlettim. Ayrıca ara sonuçlar verir, böylece şöyle çıktı alırsınız:

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

Bu hesaplama birkaç saat sürdü.

Sabırsız kalırsanız ve Ctrl-Cmuhtemelen uygun olmayan bir çözüm bulunduktan sonra baskı yaparsanız , program bu çözümü gösterecektir. Yani bunu elde etmek uzun sürmüyor:

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

İşte genişletilmiş program:

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'ın cevabına dayanarak , çoğunlukla üçgen çiftlerini kontrol ederek ve bu doğrulamada başarısız olan üçgen çiftleri içermiyorsa yapılandırmayı doğrulayarak aynı mantık ilerlemesini takip eder. Itertools ve copy dışında herhangi bir kütüphane kullanmadığım için, program boyunca karşılaşılan örnekleri kaydetme üzerinde tam kontrole sahibim.

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

Sorun şu ki, çok verimli değil. Çok hızlı çalışır n=5, ancak bu noktadan sonra önemli ölçüde yavaşlamaya başlar. At n=6, koşmak yaklaşık bir dakika sürer ve at çok daha yavaştır n=7. Bu programla yapılabilecek çok fazla verimlilik düzeltmesi olduğunu hayal ediyorum, ancak bu yöntemin iç işleyişini kontrol etmek için çok daha fazla esnekliğe sahip iyi bir çözümün hızlı bir şekilde hazırlanmış kaba taslağı. Zamanla bunun üzerinde kademeli olarak çalışacağım.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.