การลบจุดออกจากอาร์เรย์รูปสามเหลี่ยมโดยไม่สูญเสียรูปสามเหลี่ยม


17

ฉันมีปัญหาเกี่ยวกับ combinatoricsที่ฉันต้องการนำมาใช้กับ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 ที่ใหญ่ที่สุดชนะ หากการส่งสองครั้งมีลำดับเหมือนกันดังนั้นการส่งสองครั้งที่ชนะก่อนหน้านี้

แม้ว่าจะไม่จำเป็นสำหรับการส่งมันก็จะเป็นประโยชน์กับฉันถ้าคุณโพสต์การก่อสร้างของอาร์เรย์ triangluar ที่เกิดขึ้นตามตัวอย่างข้างต้น


1
นี่ไม่ใช่การท้าทายรหัสแทนที่จะเป็นรหัสที่เร็วที่สุดใช่ไหม
Don Thousand

6
ฉันคิดว่าคุณควรเลือกเวลา จำกัด (เช่นอายุ 60 ปี) ดังนั้นการแข่งขันจึงไม่เกี่ยวกับระยะเวลาที่มีคนใช้รหัสของพวกเขา
dylnan

เป็นปัญหาที่ดี คุณหมายถึงอะไรโดยสามเหลี่ยม "ตรง"
ดาเมียน

คำตอบ:


10

งูหลาม 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)))

ใช้เครื่องมือแก้ CP-SAT ของ Google OR-Tools

หลังจากทำงานเป็นเวลา ~ 30 วินาทีผลลัพธ์จะดังต่อไปนี้:

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

ผมไม่ได้ลองเหตุการณ์ที่จะรอn=9ในขณะที่มันอาจจะใช้เวลาเป็นชั่วโมง (จำนวน จำกัด ที่เติบโตขึ้นเช่นn6 ) หลังจากที่น้อยกว่า 30 a(9)=15นาทีของการคำนวณผมพบว่า ฉันปล่อยให้คะแนนของฉันn=8เพราะในขณะนี้ข้อ จำกัด เวลาไม่ชัดเจน แต่ครึ่งชั่วโมงอาจยาวเกินไป

มันทำงานอย่างไร

ใช้เวลาสองแตกต่างกันสามเหลี่ยมด้านเท่าสามเหลี่ยมและT_2เพื่อหลีกเลี่ยงความคลุมเครือควรจะมีอย่างน้อยหนึ่งหลอดไฟบนจุดสุดยอดอยู่ตรงหนึ่งและT_2T 2 T 1 T 2T1T2T1T2

ดังนั้นคำถามอาจถูกใช้ใหม่เป็นปัญหา SAT โดยมีข้อ จำกัด หนึ่งข้อสำหรับสามเหลี่ยมทุกคู่

PS: ฉันอยากจะเป็นตัวอย่างให้กับn=8แต่ฉันมีปัญหากับตัวแก้ SAT ซึ่งเห็นได้ชัดว่าต้องการให้แก้ปัญหาทั้งหมดด้วยตัวเอง


ฉันตัดสินใจที่จะพอร์ตโซลูชันให้กับ Mathematicaซึ่งน่าเสียดายที่ช้ากว่า
user202729

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มาก ฉันคิดว่ามีการแก้ไขประสิทธิภาพจำนวนมากที่สามารถทำกับโปรแกรมนี้ได้ แต่มันเป็นร่างคร่าวๆที่รวดเร็วของโซลูชันที่ดีและมีความยืดหยุ่นมากขึ้นในการตรวจสอบการทำงานภายในของวิธีนี้ ฉันจะทำสิ่งนี้เพิ่มขึ้นเมื่อเวลาผ่านไป

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.