Susun Segitiga Pythagoras

23

Latar Belakang

Segitiga Pythagoras adalah segitiga siku-siku di mana setiap panjang sisi adalah bilangan bulat (yaitu, panjang sisi membentuk tripel Pythagoras ):

Segitiga Pythagoras

Dengan menggunakan sisi-sisi segitiga ini, kita dapat melampirkan dua segitiga Pythagoras yang tidak kongruen sebagai berikut:

Tumpukan Segitiga 1

Kita dapat melanjutkan dengan pola ini sesuai keinginan, asalkan tidak ada dua segitiga yang bertumpang tindih, dan sisi penghubung memiliki panjang yang sama:

masukkan deskripsi gambar di sini

Pertanyaannya adalah, berapa banyak segitiga Pythagoras non-kongruen yang dapat kita muat di ruang tertentu?

Input

Anda akan menerima dua bilangan bulat sebagai input, Wdan H, melalui argumen fungsi, STDIN, string, atau apa pun yang Anda suka. Bilangan bulat dapat diterima sebagai desimal, heksadesimal, biner, unary (semoga sukses, Retina ), atau basis integer lainnya. Anda mungkin menganggap itu max(W, H) <= 2^15 - 1.

Hasil

Program atau fungsi Anda harus menghitung daftar segitiga Pythagoras terkoneksi yang tidak tumpang tindih dan menghasilkan daftar set tiga koordinat masing-masing, di mana koordinat dalam himpunan membentuk salah satu segitiga Pythagoras saat dihubungkan oleh garis. Koordinat harus bilangan real di ruang kita ( xharus dalam interval [0, W]dan yharus dalam interval)[0, H] ), dan jarak harus akurat untuk mesin presisi. Urutan segitiga dan format yang tepat dari masing-masing koordinat tidak penting.

Harus mungkin untuk "berjalan" dari satu segitiga ke segitiga lainnya hanya melangkahi batas yang terhubung.

Menggunakan diagram di atas sebagai contoh, mari kita masukan kami menjadi W = 60, H = 60.

Output kami kemudian bisa menjadi daftar koordinat berikut:

(0, 15), (0, 21), (8, 15)
(0, 21), (14.4, 40.2), (8, 15)
(0, 15), (8, 0), (8, 15)
(8, 0), (8, 15), (28, 15)
(8, 15), (28, 15), (28, 36)
(28, 15), (28, 36), (56, 36)

Sekarang, 6 segitiga pastinya bukan yang terbaik yang bisa kita lakukan mengingat ruang kita. Bisakah kamu berbuat lebih baik?

Aturan dan Penilaian

  • Skor Anda untuk tantangan ini adalah jumlah segitiga yang dihasilkan oleh program Anda atas masukan dari W = 1000, H = 1000. Saya berhak mengubah input ini jika saya mencurigai seseorang melakukan hardcoding pada kasus ini.

  • Anda tidak boleh menggunakan builtin yang menghitung tripel Pythagoras, dan Anda tidak boleh membuat hardcode daftar lebih dari 2 tripel Pythagoras (jika Anda hardcode program Anda untuk selalu memulai dengan segitiga (3, 4, 5), atau keadaan awal yang serupa, yang baik-baik saja).

  • Anda dapat menulis kiriman Anda dalam bahasa apa pun. Keterbacaan dan komentar sangat dianjurkan.

  • Anda dapat menemukan daftar tiga kali lipat Pythagoras di sini .

  • Celah Standar tidak diizinkan.

BrainSteel
sumber
Bisakah kita menggunakan lebih dari satu instance dari segitiga yang sama di dalam ruang?
DavidC
1
@DavidCarraher Tidak ada dua segitiga yang dihasilkan oleh program Anda yang mungkin saling kongruen.
BrainSteel
3
ini mungkin menarik: en.wikipedia.org/wiki/Tree_of_primitive_Pythagorean_triples
Level River St
Masalah ini membutuhkan banyak perhitungan, bukan? Terutama karena ini adalah masalah pengepakan.
Renae Lider
1
@ KeithRandall Mereka serupa, tidak kongruen.
Geobits

Jawaban:

16

Python 3, 109

Ini tentu saja merupakan tantangan yang sulit. Ada banyak kali menulis kode yang saya temukan mempertanyakan kemampuan geometri dasar saya. Yang sedang berkata, saya cukup senang dengan hasilnya. Saya tidak berusaha untuk menghasilkan algoritma yang kompleks untuk menempatkan segitiga, dan alih-alih kode saya hanya blunder karena selalu menempatkan yang terkecil yang dapat ditemukan. Saya harap saya dapat meningkatkan ini nanti, atau jawaban saya akan menolak orang lain untuk menemukan algoritma yang lebih baik! Secara keseluruhan, masalah yang sangat menyenangkan, dan menghasilkan beberapa gambar yang menarik.

Ini kodenya:

import time
import math

W = int(input("Enter W: "))
H = int(input("Enter H: "))

middle_x = math.floor(W/2)
middle_y = math.floor(H/2)

sides = [ # each side is in the format [length, [x0, y0], [x1, y1]]
    [3,[middle_x,middle_y],[middle_x+3,middle_y]],
    [4,[middle_x,middle_y],[middle_x,middle_y+4]],
    [5,[middle_x+3,middle_y],[middle_x,middle_y+4]]
    ]

triangles = [[0,1,2]] # each triangle is in the format [a, b, c] where a, b and c are the indexes of sides

used_triangles = [[3,4,5]] # a list of used Pythagorean triples, where lengths are ordered (a < b < c)

max_bounds_length = math.sqrt(W**2 + H**2)

def check_if_pyth_triple(a,b): # accepts two lists of the form [l, [x0,y0], [x1,y1]] defining two line segments
    # returns 0 if there are no triples, 1 if there is a triple with a right angle on a,
    # and 2 if there is a triple with the right angle opposite a
    c = math.sqrt(a[0]**2 + b[0]**2)
    if c.is_integer():
        if not sorted([a[0], b[0], c]) in used_triangles:
            return 1
        return 0
    else:
        if a[0] > b[0]:
            c = math.sqrt(a[0]**2 - b[0]**2)
            if c.is_integer() and not sorted([a[0], b[0], c]) in used_triangles:
                return 2
        return 0

def check_if_out_of_bounds(p):
    out = False
    if p[0] < 0 or p[0] > W:
        out = True
    if p[1] < 0 or p[1] > H:
        out = True
    return out

def in_between(a,b,c):
    maxi = max(a,c)
    mini = min(a,c)
    return mini < b < maxi

def sides_intersect(AB,CD): # accepts two lists of the form [l, [x0,y0], [x1,y1]] defining two line segments
    # doesn't count overlapping lines
    A = AB[1]
    B = AB[2]
    C = CD[1]
    D = CD[2]

    if A[0] == B[0]: # AB is vertical
        if C[0] == D[0]: # CD is vertical
            return False
        else:
            m1 = (C[1] - D[1])/(C[0] - D[0]) # slope of CD
            y = m1*(A[0] - C[0]) + C[1] # the y value of CD at AB's x value
            return in_between(A[1], y, B[1]) and in_between(C[0], A[0], D[0])
    else:
        m0 = (A[1] - B[1])/(A[0] - B[0]) # slope of AB
        if C[0] == D[0]: # CD is vertical
            y = m0*(C[0] - A[0]) + A[1] # the y value of CD at AB's x value
            return in_between(C[1], y, D[1]) and in_between(A[0],C[0],B[0])
        else:
            m1 = (C[1] - D[1])/(C[0] - D[0]) # slope of CD
            if m0 == m1:
                return False
            else:
                x = (m0*A[0] - m1*C[0] - A[1] + C[1])/(m0 - m1)
                return in_between(A[0], x, B[0]) and in_between(C[0], x, D[0])

def check_all_sides(b,triangle):
    no_intersections = True
    for side in sides:
        if sides_intersect(side, b):
            no_intersections = False
            break

    return no_intersections

def check_point_still_has_room(A): # This function is needed for the weird case when all 2pi degrees
    # around a point are filled by triangles, but you could fit in a small triangle into another one
    # already built around the point. Doing this won't cause sides_intersect() to detect it because
    # the sides will all be parallel. Crazy stuff.
    connecting_sides = []
    for side in sides:
        if A in side:
            connecting_sides.append(side)

    match_count = 0
    slopes = []
    for side in connecting_sides:
        B = side[1]
        if A == B:
            B = side[2]
        if not A[0] == B[0]:
            slope = round((A[1]-B[1])/(A[0]-B[0]),4)
        else:
            if A[1] < B[1]:
                slope = "infinity"
            else:
                slope = "neg_infinity"
        if slope in slopes:
            match_count -= 1
        else:
            slopes.append(slope)
            match_count += 1

    return match_count != 0

def construct_b(a,b,pyth_triple_info,straight_b_direction,bent_b_direction):
    # this function finds the correct third point of the triangle given a and the length of b
    # pyth_triple_info determines if a is a leg or the hypotenuse
    # the b_directions determine on which side of a the triangle should be formed
    a_p = 2 # this is the index of the point in a that is not the shared point with b
    if a[1] != b[1]:
        a_p = 1

    vx = a[a_p][0] - b[1][0] # v is our vector, and these are the coordinates, adjusted so that
    vy = a[a_p][1] - b[1][1] # the shared point is the origin

    if pyth_triple_info == 1:
        # because the dot product of orthogonal vectors is zero, we can use that and the Pythagorean formula
        # to get this simple formula for generating the coordinates of b's second point
        if vy == 0:
            x = 0
            y = b[0]
        else:
            x = b[0]/math.sqrt(1+((-vx/vy)**2)) # b[0] is the desired length
            y = -vx*x/vy

        x = x*straight_b_direction # since the vector is orthagonal, if we want to reverse the direction,
        y = y*straight_b_direction # it just means finding the mirror point

    elif pyth_triple_info == 2: # this finds the intersection of the two circles of radii b[0] and c 
        # around a's endpoints, which is the third point of the triangle if a is the hypotenuse
        c = math.sqrt(a[0]**2 - b[0]**2)
        D = a[0]
        A = (b[0]**2 - c**2 + D**2 ) / (2*D)
        h = math.sqrt(b[0]**2 - A**2)
        x2 = vx*(A/D)
        y2 = vy*(A/D)        
        x = x2 + h*vy/D
        y = y2 - h*vx/D

        if bent_b_direction == -1: # this constitutes reflection of the vector (-x,-y) around the normal vector n,
            # which accounts for finding the triangle on the opposite side of a
            dx = -x
            dy = -y
            v_length = math.sqrt(vx**2 + vy**2)
            nx = vx/v_length
            ny = vy/v_length

            d_dot_n = dx*nx + dy*ny

            x = dx - 2*d_dot_n*nx
            y = dy - 2*d_dot_n*ny

    x = x + b[1][0] # adjust back to the original frame
    y = y + b[1][1]

    return [x,y]

def construct_triangle(side_index):
    a = sides[side_index] # a is the base of the triangle
    a_p = 1
    b = [1, a[a_p], []] # side b, c is hypotenuse

    for index, triangle in enumerate(triangles):
        if side_index in triangle:
            triangle_index = index
            break

    triangle = list(triangles[triangle_index])
    triangle.remove(side_index)

    add_tri = False

    straight_b = construct_b(a,b,1,1,1)

    bent_b = construct_b(a,b,2,1,1)

    A = sides[triangle[0]][1]
    if A in a:
        A = sides[triangle[0]][2]

    Ax = A[0] - b[1][0] # adjusting A so that it's a vector
    Ay = A[1] - b[1][1]

    # these are for determining if construct_b() is going to the correct side
    triangle_on_side = (a[2][0]-a[1][0])*(A[1]-a[1][1]) - (a[2][1]-a[1][1])*(A[0]-a[1][0])
    straight_b_on_side = (a[2][0]-a[1][0])*(straight_b[1]-a[1][1]) - (a[2][1]-a[1][1])*(straight_b[0]-a[1][0])
    bent_b_on_side = (a[2][0]-a[1][0])*(bent_b[1]-a[1][1]) - (a[2][1]-a[1][1])*(bent_b[0]-a[1][0])

    straight_b_direction = 1
    if (triangle_on_side > 0 and straight_b_on_side > 0) or (triangle_on_side < 0 and straight_b_on_side < 0):
        straight_b_direction = -1

    bent_b_direction = 1
    if (triangle_on_side > 0 and bent_b_on_side > 0) or (triangle_on_side < 0 and bent_b_on_side < 0):
        bent_b_direction = -1


    a_ps = []
    for x in [1,2]:
        if check_point_still_has_room(a[x]): # here we check for that weird exception
            a_ps.append(x)

    while True:
        out_of_bounds = False
        if b[0] > max_bounds_length:
            break

        pyth_triple_info = check_if_pyth_triple(a,b)

        for a_p in a_ps:
            if a_p == 1: # this accounts for the change in direction when switching a's points
                new_bent_b_direction = bent_b_direction
            else:
                new_bent_b_direction = -bent_b_direction

            b[1] = a[a_p]
            if pyth_triple_info > 0:
                b[2] = construct_b(a,b,pyth_triple_info,straight_b_direction,new_bent_b_direction)

                if check_if_out_of_bounds(b[2]): # here is the check to make sure we don't go out of bounds
                    out_of_bounds = True
                    break

                if check_all_sides(b,triangle):
                    if pyth_triple_info == 1:
                        c = [math.sqrt(a[0]**2 + b[0]**2), a[3-a_p], b[2]]
                    else:
                        c = [math.sqrt(a[0]**2 - b[0]**2), a[3-a_p], b[2]]

                    if check_all_sides(c,triangle):
                        add_tri = True
                        break

        if out_of_bounds or add_tri:
            break

        b[0] += 1 # increment the length of b every time the loop goes through

    if add_tri: # this adds a new triangle
        sides.append(b)
        sides.append(c)
        sides_len = len(sides)
        triangles.append([side_index, sides_len - 2, sides_len - 1])
        used_triangles.append(sorted([a[0], b[0], c[0]])) # so we don't use the same triangle again

def build_all_triangles(): # this iterates through every side to see if a new triangle can be constructed
    # this is probably where real optimization would take place so more optimal triangles are placed first
    t0 = time.clock()

    index = 0
    while index < len(sides):
        construct_triangle(index)
        index += 1

    t1 = time.clock()

    triangles_points = [] # this is all for printing points
    for triangle in triangles:
        point_list = []
        for x in [1,2]:
            for side_index in triangle:
                point = sides[side_index][x]
                if not point in point_list:
                    point_list.append(point)
        triangles_points.append(point_list)

    for triangle in triangles_points:
        print(triangle)

    print(len(triangles), "triangles placed in", round(t1-t0,3), "seconds.")

def matplotlib_graph(): # this displays the triangles with matplotlib
    import pylab as pl
    import matplotlib.pyplot as plt
    from matplotlib import collections as mc

    lines = []
    for side in sides:
        lines.append([side[1],side[2]])

    lc = mc.LineCollection(lines)
    fig, ax = pl.subplots()
    ax.add_collection(lc)
    ax.autoscale()
    ax.margins(0.1)
    plt.show()

build_all_triangles()

Berikut grafik output untuk W = 1000dan H = 1000dengan 109 segitiga: Grafik segitiga diplot dengan matplotlib

Inilah W = 10000dan H = 10000dengan 724 segitiga: Grafik segitiga diplot dengan matplotlib

Panggil matplotlib_graph()fungsi setelah build_all_triangles()membuat grafik segitiga.

Saya pikir kodenya berjalan cukup cepat: pada W = 1000dan H = 1000butuh 0,66 detik, dan pada W = 10000dan H = 10000butuh 45 detik menggunakan laptop jelek saya.

Berirama
sumber
Saya benar-benar perlu menyelesaikan solusi saya. Saya cukup jauh beberapa minggu yang lalu, tetapi tidak pernah sempat untuk menyelesaikannya. Ini memang pekerjaan yang cukup banyak! Terutama dengan tes persimpangan, dan membuatnya berfungsi dengan baik untuk kasus yang rusak. Saya rasa saya tahu pendekatan apa yang ingin saya gunakan untuk itu, tapi itu bagian yang belum saya selesaikan.
Reto Koradi
1
Wow, ini solusi pertama yang bagus! Saya terutama suka grafiknya. Saya senang Anda menikmati tantangan ini, dan saya harap Anda bertahan di PPCG!
BrainSteel
Itu mungkin gambar paling kacau yang pernah saya lihat
Beta Decay
16

C ++, 146 segitiga (bagian 1/2)

Hasil sebagai Gambar

Hasil

Deskripsi Algoritma

Ini menggunakan pencarian luas pertama dari ruang solusi. Di setiap langkah, itu dimulai dengan semua konfigurasi unik ksegitiga yang sesuai di dalam kotak, dan membangun semua konfigurasi unikk + 1 segitiga dengan menyebutkan semua opsi menambahkan segitiga yang tidak digunakan ke salah satu konfigurasi.

Algoritma pada dasarnya diatur untuk menemukan maksimum absolut dengan BFS lengkap. Dan itu berhasil untuk ukuran yang lebih kecil. Misalnya, untuk kotak 50x50, ia menemukan maksimum dalam sekitar 1 menit. Tetapi untuk 1000x1000, ruang solusi terlalu besar. Untuk menghentikannya, saya memotong daftar solusi setelah setiap langkah. Jumlah solusi yang disimpan diberikan oleh argumen baris perintah. Untuk solusi di atas, nilai 50 digunakan. Ini menghasilkan runtime sekitar 10 menit.

Garis besar langkah-langkah utama terlihat seperti ini:

  1. Hasilkan semua segitiga Pythagoras yang berpotensi muat di dalam kotak.
  2. Hasilkan set solusi awal yang terdiri dari solusi dengan masing-masing 1 segitiga.
  3. Lingkaran dari generasi ke generasi (hitungan segitiga).
    1. Hilangkan solusi yang tidak valid dari set solusi. Ini adalah solusi yang tidak cocok di dalam kotak, atau tumpang tindih.
    2. Jika set solusi kosong, kita selesai. Solusi yang ditetapkan dari generasi sebelumnya berisi maksimal.
    3. Solusi trim diatur ke ukuran yang diberikan jika opsi trim diaktifkan.
    4. Ulangi semua solusi dalam generasi saat ini.
      1. Lingkari semua sisi dalam perimeter solusi.
        1. Temukan semua segitiga yang memiliki panjang sisi yang cocok dengan sisi perimeter, dan yang belum ada dalam solusinya.
        2. Hasilkan solusi baru yang dihasilkan dari menambahkan segitiga, dan tambahkan solusi ke set solusi generasi baru.
  4. Solusi cetak.

Salah satu aspek penting dalam keseluruhan skema adalah bahwa konfigurasi umumnya akan dihasilkan beberapa kali, dan kami hanya tertarik pada konfigurasi unik. Jadi kita membutuhkan kunci unik yang mendefinisikan solusi, yang harus independen dari urutan segitiga yang digunakan saat menghasilkan solusi. Misalnya, menggunakan koordinat untuk kunci tidak akan berfungsi sama sekali, karena mereka bisa sangat berbeda jika kami tiba pada solusi yang sama dalam beberapa pesanan. Apa yang saya gunakan adalah kumpulan indeks segitiga dalam daftar global, ditambah satu set objek "konektor" yang menentukan bagaimana segitiga terhubung. Jadi kuncinya hanya mengkodekan topologi, terlepas dari urutan dan posisi konstruksi dalam ruang 2D.

Sementara lebih merupakan aspek implementasi, bagian lain yang tidak sepenuhnya sepele adalah memutuskan apakah dan bagaimana semuanya cocok ke dalam kotak yang diberikan. Jika Anda benar-benar ingin mendorong batas, jelas perlu untuk memungkinkan rotasi agar sesuai di dalam kotak.

Saya akan mencoba dan menambahkan beberapa komentar ke kode di bagian 2 nanti, kalau-kalau ada orang yang ingin merinci bagaimana ini semua bekerja.

Hasil dalam Format Teks Resmi

(322.085, 641.587) (318.105, 641.979) (321.791, 638.602)
(318.105, 641.979) (309.998, 633.131) (321.791, 638.602)
(318.105, 641.979) (303.362, 639.211) (309.998, 633.131)
(318.105, 641.979) (301.886, 647.073) (303.362, 639.211)
(301.886, 647.073) (297.465, 638.103) (303.362, 639.211)
(301.886, 647.073) (280.358, 657.682) (297.465, 638.103)
(301.886, 647.073) (283.452, 663.961) (280.358, 657.682)
(301.886, 647.073) (298.195, 666.730) (283.452, 663.961)
(301.886, 647.073) (308.959, 661.425) (298.195, 666.730)
(301.886, 647.073) (335.868, 648.164) (308.959, 661.425)
(335.868, 648.164) (325.012, 669.568) (308.959, 661.425)
(308.959, 661.425) (313.666, 698.124) (298.195, 666.730)
(313.666, 698.124) (293.027, 694.249) (298.195, 666.730)
(313.666, 698.124) (289.336, 713.905) (293.027, 694.249)
(298.195, 666.730) (276.808, 699.343) (283.452, 663.961)
(335.868, 648.164) (353.550, 684.043) (325.012, 669.568)
(303.362, 639.211) (276.341, 609.717) (309.998, 633.131)
(276.808, 699.343) (250.272, 694.360) (283.452, 663.961)
(335.868, 648.164) (362.778, 634.902) (353.550, 684.043)
(362.778, 634.902) (367.483, 682.671) (353.550, 684.043)
(250.272, 694.360) (234.060, 676.664) (283.452, 663.961)
(362.778, 634.902) (382.682, 632.942) (367.483, 682.671)
(382.682, 632.942) (419.979, 644.341) (367.483, 682.671)
(419.979, 644.341) (379.809, 692.873) (367.483, 682.671)
(353.550, 684.043) (326.409, 737.553) (325.012, 669.568)
(353.550, 684.043) (361.864, 731.318) (326.409, 737.553)
(353.550, 684.043) (416.033, 721.791) (361.864, 731.318)
(416.033, 721.791) (385.938, 753.889) (361.864, 731.318)
(385.938, 753.889) (323.561, 772.170) (361.864, 731.318)
(385.938, 753.889) (383.201, 778.739) (323.561, 772.170)
(383.201, 778.739) (381.996, 789.673) (323.561, 772.170)
(323.561, 772.170) (292.922, 743.443) (361.864, 731.318)
(323.561, 772.170) (296.202, 801.350) (292.922, 743.443)
(250.272, 694.360) (182.446, 723.951) (234.060, 676.664)
(335.868, 648.164) (330.951, 570.319) (362.778, 634.902)
(330.951, 570.319) (381.615, 625.619) (362.778, 634.902)
(330.951, 570.319) (375.734, 565.908) (381.615, 625.619)
(330.951, 570.319) (372.989, 538.043) (375.734, 565.908)
(323.561, 772.170) (350.914, 852.648) (296.202, 801.350)
(323.561, 772.170) (362.438, 846.632) (350.914, 852.648)
(234.060, 676.664) (217.123, 610.807) (283.452, 663.961)
(217.123, 610.807) (249.415, 594.893) (283.452, 663.961)
(375.734, 565.908) (438.431, 559.733) (381.615, 625.619)
(382.682, 632.942) (443.362, 567.835) (419.979, 644.341)
(443.362, 567.835) (471.667, 606.601) (419.979, 644.341)
(323.561, 772.170) (393.464, 830.433) (362.438, 846.632)
(372.989, 538.043) (471.272, 556.499) (375.734, 565.908)
(372.989, 538.043) (444.749, 502.679) (471.272, 556.499)
(372.989, 538.043) (365.033, 521.897) (444.749, 502.679)
(443.362, 567.835) (544.353, 553.528) (471.667, 606.601)
(544.353, 553.528) (523.309, 622.384) (471.667, 606.601)
(544.353, 553.528) (606.515, 572.527) (523.309, 622.384)
(419.979, 644.341) (484.688, 697.901) (379.809, 692.873)
(444.749, 502.679) (552.898, 516.272) (471.272, 556.499)
(217.123, 610.807) (170.708, 516.623) (249.415, 594.893)
(484.688, 697.901) (482.006, 753.837) (379.809, 692.873)
(484.688, 697.901) (571.903, 758.147) (482.006, 753.837)
(419.979, 644.341) (535.698, 636.273) (484.688, 697.901)
(276.808, 699.343) (228.126, 812.299) (250.272, 694.360)
(228.126, 812.299) (185.689, 726.188) (250.272, 694.360)
(228.126, 812.299) (192.246, 829.981) (185.689, 726.188)
(393.464, 830.433) (449.003, 936.807) (362.438, 846.632)
(393.464, 830.433) (468.505, 926.625) (449.003, 936.807)
(416.033, 721.791) (471.289, 833.915) (385.938, 753.889)
(471.289, 833.915) (430.252, 852.379) (385.938, 753.889)
(350.914, 852.648) (227.804, 874.300) (296.202, 801.350)
(192.246, 829.981) (114.401, 834.898) (185.689, 726.188)
(114.401, 834.898) (155.433, 715.767) (185.689, 726.188)
(217.123, 610.807) (91.773, 555.523) (170.708, 516.623)
(91.773, 555.523) (141.533, 457.421) (170.708, 516.623)
(141.533, 457.421) (241.996, 407.912) (170.708, 516.623)
(141.533, 457.421) (235.365, 394.457) (241.996, 407.912)
(241.996, 407.912) (219.849, 525.851) (170.708, 516.623)
(241.996, 407.912) (304.896, 419.724) (219.849, 525.851)
(91.773, 555.523) (55.917, 413.995) (141.533, 457.421)
(571.903, 758.147) (476.260, 873.699) (482.006, 753.837)
(571.903, 758.147) (514.819, 890.349) (476.260, 873.699)
(571.903, 758.147) (587.510, 764.886) (514.819, 890.349)
(587.510, 764.886) (537.290, 898.778) (514.819, 890.349)
(587.510, 764.886) (592.254, 896.801) (537.290, 898.778)
(587.510, 764.886) (672.455, 761.831) (592.254, 896.801)
(55.917, 413.995) (113.819, 299.840) (141.533, 457.421)
(113.819, 299.840) (149.275, 293.604) (141.533, 457.421)
(544.353, 553.528) (652.112, 423.339) (606.515, 572.527)
(652.112, 423.339) (698.333, 461.597) (606.515, 572.527)
(535.698, 636.273) (651.250, 731.917) (484.688, 697.901)
(651.250, 731.917) (642.213, 756.296) (484.688, 697.901)
(304.896, 419.724) (299.444, 589.636) (219.849, 525.851)
(304.896, 419.724) (369.108, 452.294) (299.444, 589.636)
(304.896, 419.724) (365.965, 299.326) (369.108, 452.294)
(304.896, 419.724) (269.090, 347.067) (365.965, 299.326)
(114.401, 834.898) (0.942, 795.820) (155.433, 715.767)
(114.401, 834.898) (75.649, 947.412) (0.942, 795.820)
(192.246, 829.981) (124.489, 994.580) (114.401, 834.898)
(269.090, 347.067) (205.435, 217.901) (365.965, 299.326)
(205.435, 217.901) (214.030, 200.956) (365.965, 299.326)
(182.446, 723.951) (68.958, 600.078) (234.060, 676.664)
(182.446, 723.951) (32.828, 633.179) (68.958, 600.078)
(652.112, 423.339) (763.695, 288.528) (698.333, 461.597)
(763.695, 288.528) (808.220, 324.117) (698.333, 461.597)
(763.695, 288.528) (811.147, 229.162) (808.220, 324.117)
(652.112, 423.339) (627.572, 321.247) (763.695, 288.528)
(627.572, 321.247) (660.872, 244.129) (763.695, 288.528)
(652.112, 423.339) (530.342, 344.618) (627.572, 321.247)
(652.112, 423.339) (570.488, 453.449) (530.342, 344.618)
(627.572, 321.247) (503.633, 267.730) (660.872, 244.129)
(365.965, 299.326) (473.086, 450.157) (369.108, 452.294)
(365.965, 299.326) (506.922, 344.440) (473.086, 450.157)
(365.965, 299.326) (394.633, 260.827) (506.922, 344.440)
(394.633, 260.827) (537.381, 303.535) (506.922, 344.440)
(811.147, 229.162) (979.067, 234.338) (808.220, 324.117)
(698.333, 461.597) (706.660, 655.418) (606.515, 572.527)
(811.147, 229.162) (982.117, 135.385) (979.067, 234.338)
(982.117, 135.385) (999.058, 234.954) (979.067, 234.338)
(365.965, 299.326) (214.375, 186.448) (394.633, 260.827)
(811.147, 229.162) (803.145, 154.590) (982.117, 135.385)
(803.145, 154.590) (978.596, 102.573) (982.117, 135.385)
(214.375, 186.448) (314.969, 126.701) (394.633, 260.827)
(314.969, 126.701) (508.984, 192.909) (394.633, 260.827)
(314.969, 126.701) (338.497, 88.341) (508.984, 192.909)
(338.497, 88.341) (523.725, 138.884) (508.984, 192.909)
(338.497, 88.341) (359.556, 11.163) (523.725, 138.884)
(808.220, 324.117) (801.442, 544.012) (698.333, 461.597)
(801.442, 544.012) (739.631, 621.345) (698.333, 461.597)
(660.872, 244.129) (732.227, 78.877) (763.695, 288.528)
(660.872, 244.129) (644.092, 40.821) (732.227, 78.877)
(808.220, 324.117) (822.432, 544.659) (801.442, 544.012)
(660.872, 244.129) (559.380, 47.812) (644.092, 40.821)
(660.872, 244.129) (556.880, 242.796) (559.380, 47.812)
(556.880, 242.796) (528.882, 242.437) (559.380, 47.812)
(808.220, 324.117) (924.831, 449.189) (822.432, 544.659)
(924.831, 449.189) (922.677, 652.177) (822.432, 544.659)
(922.677, 652.177) (779.319, 785.836) (822.432, 544.659)
(779.319, 785.836) (696.630, 771.054) (822.432, 544.659)
(779.319, 785.836) (746.412, 969.918) (696.630, 771.054)
(779.319, 785.836) (848.467, 840.265) (746.412, 969.918)
(848.467, 840.265) (889.327, 872.428) (746.412, 969.918)
(746.412, 969.918) (619.097, 866.541) (696.630, 771.054)
(779.319, 785.836) (993.200, 656.395) (848.467, 840.265)
(993.200, 656.395) (935.157, 864.450) (848.467, 840.265)
(993.200, 656.395) (995.840, 881.379) (935.157, 864.450)
(338.497, 88.341) (34.607, 5.420) (359.556, 11.163)
(338.497, 88.341) (189.294, 204.357) (34.607, 5.420)
(189.294, 204.357) (158.507, 228.296) (34.607, 5.420)
(158.507, 228.296) (38.525, 230.386) (34.607, 5.420)
(158.507, 228.296) (41.694, 412.358) (38.525, 230.386)

Kode

Lihat bagian 2 untuk kode. Ini dipecah menjadi 2 bagian untuk mengatasi batas ukuran posting.

Kode ini juga tersedia di PasteBin .

Reto Koradi
sumber
8

C ++, 146 segitiga (bagian 2/2)

Lanjutan dari bagian 1. Ini dibagi menjadi 2 bagian untuk mengatasi batas ukuran pos.

Kode

Komentar yang akan ditambahkan.

#include <cmath>
#include <vector>
#include <set>
#include <map>
#include <sstream>
#include <iostream>

class Vec2 {
public:
    Vec2()
      : m_x(0.0f), m_y(0.0f) {
    }

    Vec2(float x, float y)
      : m_x(x), m_y(y) {
    }

    float x() const {
        return m_x;
    }

    float y() const {
        return m_y;
    }

    void normalize() {
        float s = 1.0f / sqrt(m_x * m_x + m_y * m_y);
        m_x *= s;
        m_y *= s;
    }

    Vec2 operator+(const Vec2& rhs) const {
        return Vec2(m_x + rhs.m_x, m_y + rhs.m_y);
    }

    Vec2 operator-(const Vec2& rhs) const {
        return Vec2(m_x - rhs.m_x, m_y - rhs.m_y);
    }

    Vec2 operator*(float s) const {
        return Vec2(m_x * s, m_y * s);
    }

private:
    float m_x, m_y;
};

static float cross(const Vec2& v1, const Vec2& v2) {
    return v1.x() * v2.y() - v1.y() * v2.x();
}

class Triangle {
public:
    Triangle()
      : m_sideLenA(0), m_sideLenB(0), m_sideLenC(0) {
    }

    Triangle(int sideLenA, int sideLenB, int sideLenC)
      : m_sideLenA(sideLenA),
        m_sideLenB(sideLenB),
        m_sideLenC(sideLenC) {
    }

    int getSideLenA() const {
        return m_sideLenA;
    }

    int getSideLenB() const {
        return m_sideLenB;
    }

    int getSideLenC() const {
        return m_sideLenC;
    }

private:
    int m_sideLenA, m_sideLenB, m_sideLenC;
};

class Connector {
public:
    Connector(int sideLen, int triIdx1, int triIdx2, bool flipped);

    bool operator<(const Connector& rhs) const;

    void print() const {
        std::cout << m_sideLen << "/" << m_triIdx1 << "/"
                  << m_triIdx2 << "/" << m_flipped << " ";
    }

private:
    int m_sideLen;
    int m_triIdx1, m_triIdx2;
    bool m_flipped;
};

typedef std::vector<Triangle> TriangleVec;
typedef std::multimap<int, int> SideMap;

typedef std::set<int> TriangleSet;
typedef std::set<Connector> ConnectorSet;

class SolutionKey {
public:
    SolutionKey() {
    }

    void init(int triIdx);
    void add(int triIdx, const Connector& conn);

    bool containsTriangle(int triIdx) const;
    int minTriangle() const;

    bool operator<(const SolutionKey& rhs) const;

    void print() const;

private:
    TriangleSet m_tris;
    ConnectorSet m_conns;
};

typedef std::map<SolutionKey, class SolutionData> SolutionMap;

class SolutionData {
public:
    SolutionData()
      : m_lastPeriIdx(0),
        m_rotAng(0.0f),
        m_xShift(0.0f), m_yShift(0.0f) {
    }

    void init(int triIdx);

    bool fitsInBox();
    bool selfOverlaps() const;

    void nextGeneration(
        const SolutionKey& key, bool useTrim, SolutionMap& rNewSols) const;

    void print() const;

private:
    void addTriangle(
        const SolutionKey& key, int periIdx, int newTriIdx,
        SolutionMap& rNewSols) const;

    std::vector<int> m_periTris;
    std::vector<int> m_periLens;
    std::vector<bool> m_periFlipped;
    std::vector<Vec2> m_periPoints;

    int m_lastPeriIdx;

    std::vector<Vec2> m_triPoints;

    float m_rotAng;
    float m_xShift, m_yShift;
};

static int BoxW  = 0;
static int BoxH  = 0;
static int BoxD2 = 0;

static TriangleVec AllTriangles;
static SideMap AllSides;

Connector::Connector(
    int sideLen, int triIdx1, int triIdx2, bool flipped)
  : m_sideLen(sideLen),
    m_flipped(flipped) {
    if (triIdx1 < triIdx2) {
        m_triIdx1 = triIdx1;
        m_triIdx2 = triIdx2;
    } else {
        m_triIdx1 = triIdx2;
        m_triIdx2 = triIdx1;
    }
}

bool Connector::operator<(const Connector& rhs) const {
    if (m_sideLen < rhs.m_sideLen) {
        return true;
    } else if (m_sideLen > rhs.m_sideLen) {
        return false;
    }

    if (m_triIdx1 < rhs.m_triIdx1) {
        return true;
    } else if (m_triIdx1 > rhs.m_triIdx1) {
        return false;
    }

    if (m_triIdx2 < rhs.m_triIdx2) {
        return true;
    } else if (m_triIdx2 > rhs.m_triIdx2) {
        return false;
    }

    return m_flipped < rhs.m_flipped;
}

void SolutionKey::init(int triIdx) {
    m_tris.insert(triIdx);
}

void SolutionKey::add(int triIdx, const Connector& conn) {
    m_tris.insert(triIdx);
    m_conns.insert(conn);
}

bool SolutionKey::containsTriangle(int triIdx) const {
    return m_tris.count(triIdx);
}

int SolutionKey::minTriangle() const {
    return *m_tris.begin();
}

bool SolutionKey::operator<(const SolutionKey& rhs) const {
    if (m_tris.size() < rhs.m_tris.size()) {
        return true;
    } else if (m_tris.size() > rhs.m_tris.size()) {
        return false;
    }

    TriangleSet::const_iterator triIt1 = m_tris.begin();
    TriangleSet::const_iterator triIt2 = rhs.m_tris.begin();
    while (triIt1 != m_tris.end()) {
        if (*triIt1 < *triIt2) {
           return true;
        } else if (*triIt2 < *triIt1) {
           return false;
        }
        ++triIt1;
        ++triIt2;
    }

    if (m_conns.size() < rhs.m_conns.size()) {
        return true;
    } else if (m_conns.size() > rhs.m_conns.size()) {
        return false;
    }

    ConnectorSet::const_iterator connIt1 = m_conns.begin();
    ConnectorSet::const_iterator connIt2 = rhs.m_conns.begin();
    while (connIt1 != m_conns.end()) {
        if (*connIt1 < *connIt2) {
           return true;
        } else if (*connIt2 < *connIt1) {
           return false;
        }
        ++connIt1;
        ++connIt2;
    }

    return false;
}

void SolutionKey::print() const {
    TriangleSet::const_iterator triIt = m_tris.begin();
    while (triIt != m_tris.end()) {
        std::cout << *triIt << " ";
        ++triIt;
    }
    std::cout << "\n";

    ConnectorSet::const_iterator connIt = m_conns.begin();
    while (connIt != m_conns.end()) {
        connIt->print();
        ++connIt;
    }
    std::cout << "\n";
}

void SolutionData::init(int triIdx) {
    const Triangle& tri = AllTriangles[triIdx];

    m_periTris.push_back(triIdx);
    m_periTris.push_back(triIdx);
    m_periTris.push_back(triIdx);

    m_periLens.push_back(tri.getSideLenB());
    m_periLens.push_back(tri.getSideLenC());
    m_periLens.push_back(tri.getSideLenA());

    m_periFlipped.push_back(false);
    m_periFlipped.push_back(false);
    m_periFlipped.push_back(false);

    m_periPoints.push_back(Vec2(0.0f, 0.0f));
    m_periPoints.push_back(Vec2(tri.getSideLenB(), 0.0f));
    m_periPoints.push_back(Vec2(0.0f, tri.getSideLenA()));

    m_triPoints = m_periPoints;

    m_periPoints.push_back(Vec2(0.0f, 0.0f));
}

bool SolutionData::fitsInBox() {
    int nStep = 8;
    float angInc = 0.5f * M_PI / nStep;

    for (;;) {
        bool mayFit = false;
        float ang = 0.0f;

        for (int iStep = 0; iStep <= nStep; ++iStep) {
            float cosAng = cos(ang);
            float sinAng = sin(ang);

            float xMin = 0.0f;
            float xMax = 0.0f;
            float yMin = 0.0f;
            float yMax = 0.0f;
            bool isFirst = true;

            for (int iPeri = 0; iPeri < m_periLens.size(); ++iPeri) {
                const Vec2& pt = m_periPoints[iPeri];
                float x = cosAng * pt.x() - sinAng * pt.y();
                float y = sinAng * pt.x() + cosAng * pt.y();

                if (isFirst) {
                    xMin = x;
                    xMax = x;
                    yMin = y;
                    yMax = y;
                    isFirst = false;
                } else {
                    if (x < xMin) {
                        xMin = x;
                    } else if (x > xMax) {
                        xMax = x;
                    }
                    if (y < yMin) {
                        yMin = y;
                    } else if (y > yMax) {
                        yMax = y;
                    }
                }
            }

            float w = xMax - xMin;
            float h = yMax - yMin;

            bool fits = false;
            if ((BoxW >= BoxH) == (w >= h)) {
                if (w <= BoxW && h <= BoxH) {
                    m_rotAng = ang;
                    m_xShift = 0.5f * BoxW - 0.5f * (xMax + xMin);
                    m_yShift = 0.5f * BoxH - 0.5f * (yMax + yMin);
                    return true;
                }
            } else {
                if (h <= BoxW && w <= BoxH) {
                    m_rotAng = ang + 0.5f * M_PI;
                    m_xShift = 0.5f * BoxW + 0.5f * (yMax + yMin);
                    m_yShift = 0.5f * BoxH - 0.5f * (xMax + xMin);
                    return true;
                }
            }

            w -= 0.125f * w * angInc * angInc + 0.5f * h * angInc;
            h -= 0.125f * h * angInc * angInc + 0.5f * w * angInc;

            if ((BoxW < BoxH) == (w < h)) {
                if (w <= BoxW && h <= BoxH) {
                    mayFit = true;
                }
            } else {
                if (h <= BoxW && w <= BoxH) {
                    mayFit = true;
                }
            }

            ang += angInc;
        }

        if (!mayFit) {
            break;
        }

        nStep *= 4;
        angInc *= 0.25f;
    }

    return false;
}

static bool intersects(
    const Vec2& p1, const Vec2& p2,
    const Vec2& q1, const Vec2& q2) {

    if (cross(p2 - p1, q1 - p1) * cross(p2 - p1, q2 - p1) > 0.0f) {
        return false;
    }

    if (cross(q2 - q1, p1 - q1) * cross(q2 - q1, p2 - q1) > 0.0f) {
        return false;
    }

    return true;
}

bool SolutionData::selfOverlaps() const {
    int periSize = m_periPoints.size();

    int triIdx = m_periTris[m_lastPeriIdx];
    const Triangle& tri = AllTriangles[triIdx];
    float offsScale = 0.0001f / tri.getSideLenC();

    const Vec2& pt1 = m_periPoints[m_lastPeriIdx];
    const Vec2& pt3 = m_periPoints[m_lastPeriIdx + 1];
    const Vec2& pt2 = m_periPoints[m_lastPeriIdx + 2];

    Vec2 pt1o = pt1 + ((pt2 - pt1) + (pt3 - pt1)) * offsScale;
    Vec2 pt2o = pt2 + ((pt1 - pt2) + (pt3 - pt2)) * offsScale;
    Vec2 pt3o = pt3 + ((pt1 - pt3) + (pt2 - pt3)) * offsScale;

    float xMax = m_periPoints[0].x();
    float yMax = m_periPoints[0].y();
    for (int iPeri = 1; iPeri < m_periLens.size(); ++iPeri) {
        if (m_periPoints[iPeri].x() > xMax) {
            xMax = m_periPoints[iPeri].x();
        }
        if (m_periPoints[iPeri].y() > yMax) {
            yMax = m_periPoints[iPeri].y();
        }
    }

    Vec2 ptOut(xMax + 0.3f, yMax + 0.7f);
    int nOutInter = 0;

    for (int iPeri = 0; iPeri < m_periLens.size(); ++iPeri) {
        int iNextPeri = iPeri + 1;
        if (iPeri == m_lastPeriIdx) {
            ++iNextPeri;
        } else if (iPeri == m_lastPeriIdx + 1) {
            continue;
        }

        if (intersects(
            m_periPoints[iPeri], m_periPoints[iNextPeri], pt1o, pt3o)) {
            return true;
        }

        if (intersects(
            m_periPoints[iPeri], m_periPoints[iNextPeri], pt2o, pt3o)) {
            return true;
        }

        if (intersects(
            m_periPoints[iPeri], m_periPoints[iNextPeri], pt3o, ptOut)) {
            ++nOutInter;
        }
    }

    return nOutInter % 2;
}

void SolutionData::nextGeneration(
    const SolutionKey& key, bool useTrim, SolutionMap& rNewSols) const
{
    int nPeri = m_periLens.size();
    for (int iPeri = (useTrim ? 0 : m_lastPeriIdx); iPeri < nPeri; ++iPeri) {
        int len = m_periLens[iPeri];
        SideMap::const_iterator itCand = AllSides.lower_bound(len);
        SideMap::const_iterator itCandEnd = AllSides.upper_bound(len);
        while (itCand != itCandEnd) {
            int candTriIdx = itCand->second;
            if (!key.containsTriangle(candTriIdx) &&
                candTriIdx > key.minTriangle()) {
                addTriangle(key, iPeri, candTriIdx, rNewSols);
            }
            ++itCand;
        }
    }
}

void SolutionData::print() const {
    float cosAng = cos(m_rotAng);
    float sinAng = sin(m_rotAng);

    int nPoint = m_triPoints.size();

    for (int iPoint = 0; iPoint < nPoint; ++iPoint) {
        const Vec2& pt = m_triPoints[iPoint];
        float x = cosAng * pt.x() - sinAng * pt.y() + m_xShift;
        float y = sinAng * pt.x() + cosAng * pt.y() + m_yShift;
        std::cout << "(" << x << ", " << y << ")";

        if (iPoint % 3 == 2) {
            std::cout << std::endl;
        } else {
            std::cout << " ";
        }
    }
}

void SolutionData::addTriangle(
    const SolutionKey& key, int periIdx, int newTriIdx,
    SolutionMap& rNewSols) const {

    int triIdx = m_periTris[periIdx];
    bool flipped = m_periFlipped[periIdx];
    int len = m_periLens[periIdx];

    Connector conn1(len, triIdx, newTriIdx, flipped);
    SolutionKey newKey1(key);
    newKey1.add(newTriIdx, conn1);
    bool isNew1 = (rNewSols.find(newKey1) == rNewSols.end());

    Connector conn2(len, triIdx, newTriIdx, !flipped);
    SolutionKey newKey2(key);
    newKey2.add(newTriIdx, conn2);
    bool isNew2 = (rNewSols.find(newKey2) == rNewSols.end());

    if (!(isNew1 || isNew2)) {
        return;
    }

    SolutionData data;

    int periSize = m_periLens.size();
    data.m_periTris.resize(periSize + 1);
    data.m_periLens.resize(periSize + 1);
    data.m_periFlipped.resize(periSize + 1);
    data.m_periPoints.resize(periSize + 2);
    for (int k = 0; k <= periIdx; ++k) {
        data.m_periTris[k] = m_periTris[k];
        data.m_periLens[k] = m_periLens[k];
        data.m_periFlipped[k] = m_periFlipped[k];
        data.m_periPoints[k] = m_periPoints[k];
    }
    for (int k = periIdx + 1; k < periSize; ++k) {
        data.m_periTris[k + 1] = m_periTris[k];
        data.m_periLens[k + 1] = m_periLens[k];
        data.m_periFlipped[k + 1] = m_periFlipped[k];
        data.m_periPoints[k + 1] = m_periPoints[k];
    }
    data.m_periPoints[periSize + 1] = m_periPoints[periSize];

    data.m_lastPeriIdx = periIdx;

    data.m_periTris[periIdx] = newTriIdx;
    data.m_periTris[periIdx + 1] = newTriIdx;

    int triSize = m_triPoints.size();
    data.m_triPoints.resize(triSize + 3);
    for (int k = 0; k < triSize; ++k) {
        data.m_triPoints[k] = m_triPoints[k];
    }

    const Triangle& tri = AllTriangles[newTriIdx];
    int lenA = tri.getSideLenA();
    int lenB = tri.getSideLenB();
    int lenC = tri.getSideLenC();

    const Vec2& pt1 = m_periPoints[periIdx];
    const Vec2& pt2 = m_periPoints[periIdx + 1];

    Vec2 v = pt2 - pt1;
    v.normalize();
    Vec2 vn(v.y(), -v.x());

    float dA = lenA;
    float dB = lenB;
    float dC = lenC;

    int len1 = 0, len2 = 0;
    Vec2 pt31, pt32;

    if (len == lenA) {
        len1 = lenB;
        len2 = lenC;
        pt31 = pt1 + vn * dB;
        pt32 = pt2 + vn * dB;
    } else if (len == lenB) {
        len1 = lenC;
        len2 = lenA;
        pt31 = pt2 + vn * dA;
        pt32 = pt1 + vn * dA;
    } else {
        len1 = lenA;
        len2 = lenB;
        pt31 = pt1 + v * (dA * dA / dC) + vn * (dA * dB / dC);
        pt32 = pt1 + v * (dB * dB / dC) + vn * (dA * dB / dC);
    }

    if (isNew1) {
        data.m_periLens[periIdx] = len1;
        data.m_periLens[periIdx + 1] = len2;
        data.m_periFlipped[periIdx] = false;
        data.m_periFlipped[periIdx + 1] = false;
        data.m_periPoints[periIdx + 1] = pt31;

        data.m_triPoints[triSize] = pt1;
        data.m_triPoints[triSize + 1] = pt31;
        data.m_triPoints[triSize + 2] = pt2;

        rNewSols.insert(std::make_pair(newKey1, data));
    }

    if (isNew2) {
        data.m_periLens[periIdx] = len2;
        data.m_periLens[periIdx + 1] = len1;
        data.m_periFlipped[periIdx] = true;
        data.m_periFlipped[periIdx + 1] = true;
        data.m_periPoints[periIdx + 1] = pt32;

        data.m_triPoints[triSize] = pt1;
        data.m_triPoints[triSize + 1] = pt32;
        data.m_triPoints[triSize + 2] = pt2;

        rNewSols.insert(std::make_pair(newKey2, data));
    }
}

static void enumerateTriangles() {
    for (int c = 2; c * c <= BoxD2; ++c) {
        for (int a = 1; 2 * a * a < c * c; ++a) {
            int b = static_cast<int>(sqrt(c * c - a * a) + 0.5f);
            if (a * a + b * b == c * c) {
                Triangle tri(a, b, c);

                int triIdx = AllTriangles.size();
                AllTriangles.push_back(Triangle(a, b, c));

                AllSides.insert(std::make_pair(a, triIdx));
                AllSides.insert(std::make_pair(b, triIdx));
                AllSides.insert(std::make_pair(c, triIdx));
            }
        }
    }
}

static void eliminateInvalid(SolutionMap& rSols) {
    SolutionMap::iterator it = rSols.begin();
    while (it != rSols.end()) {
        SolutionMap::iterator itNext = it;
        ++itNext;

        SolutionData& rSolData = it->second;

        if (!rSolData.fitsInBox()) {
            rSols.erase(it);
        } else if (rSolData.selfOverlaps()) {
            rSols.erase(it);
        }

        it = itNext;
    }
}

static void trimSolutions(SolutionMap& rSols, int trimCount) {
    if (trimCount >= rSols.size()) {
        return;
    }

    SolutionMap::iterator it = rSols.begin();
    for (int iTrim = 0; iTrim < trimCount; ++iTrim) {
        ++it;
    }

    rSols.erase(it, rSols.end());
}

static void nextGeneration(
    const SolutionMap& srcSols, bool useTrim, SolutionMap& rNewSols) {
    SolutionMap::const_iterator it = srcSols.begin();
    while (it != srcSols.end()) {
        const SolutionKey& solKey = it->first;
        const SolutionData& solData = it->second;
        solData.nextGeneration(solKey, useTrim, rNewSols);
        ++it;
    }
}

static void printSolutions(const SolutionMap& sols) {
    std::cout << std::fixed;
    std::cout.precision(3);

    SolutionMap::const_iterator it = sols.begin();
    while (it != sols.end()) {
        const SolutionKey& solKey = it->first;
        solKey.print();
        const SolutionData& solData = it->second;
        solData.print();
        std::cout << std::endl;
        ++it;
    }
}

int main(int argc, char* argv[]) {
    if (argc < 3) {
        std::cerr << "usage: " << argv[0] << " width height [trimCount]"
                  << std::endl;
        return 1;
    }

    std::istringstream streamW(argv[1]);
    streamW >> BoxW;
    std::istringstream streamH(argv[2]);
    streamH >> BoxH;

    int trimCount = 0;
    if (argc > 3) {
        std::istringstream streamTrim(argv[3]);
        streamTrim >> trimCount;
    }

    BoxD2 = BoxW * BoxW + BoxH * BoxH;

    enumerateTriangles();
    int nTri = AllTriangles.size();

    SolutionMap solGen[2];
    int srcGen = 0;

    for (int iTri = 0; iTri < nTri; ++iTri) {
        const Triangle& tri = AllTriangles[iTri];

        SolutionKey solKey;
        solKey.init(iTri);

        SolutionData solData;
        solData.init(iTri);

        solGen[srcGen].insert(std::make_pair(solKey, solData));
    }

    int level = 1;

    for (;;) {
        eliminateInvalid(solGen[srcGen]);
        std::cout << "level: " << level
                  << " solutions: " << solGen[srcGen].size() << std::endl;
        if (solGen[srcGen].empty()) {
            break;
        }

        if (trimCount > 0) {
            trimSolutions(solGen[srcGen], trimCount);
        }

        solGen[1 - srcGen].clear();
        nextGeneration(solGen[srcGen], trimCount > 0, solGen[1 - srcGen]);

        srcGen = 1 - srcGen;
        ++level;
    }

    printSolutions(solGen[1 - srcGen]);

    return 0;
}
Reto Koradi
sumber