Số lượng định hướng rắn có thể tiếp cận


11

Thử thách này không phải là về trò chơi Snake.

Hãy tưởng tượng một con rắn 2d được hình thành bằng cách vẽ một đường ngang có chiều dài n. Tại các điểm nguyên dọc theo cơ thể của nó, con rắn này có thể xoay cơ thể 90 độ. Nếu chúng ta xác định phía trước của con rắn ở phía bên trái để bắt đầu, vòng quay sẽ di chuyển phần phía sau của con rắn và phần phía trước sẽ ở lại. Bằng cách thực hiện các vòng quay lặp đi lặp lại, nó có thể tạo ra rất nhiều hình dạng cơ thể rắn khác nhau.

Quy tắc

  1. Một phần của cơ thể rắn không thể chồng lên nhau.
  2. Nó có thể đạt được hướng cuối cùng mà không có bất kỳ bộ phận nào của cơ thể con rắn chồng lên nhau ở giữa. Hai điểm chạm được tính là chồng chéo trong vấn đề này.
  3. Tôi coi một con rắn và ngược lại là cùng một hình dạng.

Bài tập

Cho đến xoay, dịch và đối xứng gương, tổng số hình dạng cơ thể rắn khác nhau có thể được thực hiện là gì?

Ví dụ về một vòng quay của một phần của cơ thể rắn. Hãy tưởng tượng n=10và con rắn đang ở hướng bắt đầu của một đường thẳng. Bây giờ xoay ở điểm 490 độ ngược chiều kim đồng hồ. Chúng tôi nhận được con rắn từ 4đến 10(đuôi của con rắn) nằm theo chiều dọc và con rắn từ 0để 4nằm ngang. Con rắn bây giờ có một góc phải trong cơ thể của nó.

Dưới đây là một số ví dụ nhờ Martin Büttner.

Chúng tôi bắt đầu với con rắn ngang.

nhập mô tả hình ảnh ở đây

Bây giờ chúng tôi xoay từ vị trí 4.

nhập mô tả hình ảnh ở đây

Chúng tôi kết thúc sau khi xoay theo hướng này.

nhập mô tả hình ảnh ở đây

Bây giờ chúng ta hãy xem xét định hướng này của một con rắn khác.

nhập mô tả hình ảnh ở đây

Bây giờ chúng ta có thể thấy một động thái bất hợp pháp, nơi sẽ có sự chồng chéo gây ra trong quá trình luân chuyển.

ví dụ về va chạm

Ghi bàn

Điểm của bạn là lớn nhất nmà mã của bạn có thể giải quyết vấn đề trong chưa đầy một phút trên máy tính của tôi.

Khi một vòng quay xảy ra, nó sẽ di chuyển một nửa con rắn với nó. Chúng tôi phải lo lắng về việc bất kỳ phần nào được xoay có thể chồng lên một phần của con rắn trong quá trình quay. Để đơn giản, chúng ta có thể giả sử con rắn có chiều rộng bằng không. Bạn chỉ có thể xoay tại một điểm cụ thể ở rắn lên tới 90 độ theo chiều kim đồng hồ ngược chiều kim đồng hồ. Đối với, bạn không bao giờ có thể gấp hoàn toàn con rắn thành hai vì điều đó sẽ liên quan đến hai lần quay tại cùng một điểm theo cùng một hướng.

Hình dạng không thể được thực hiện

Một ví dụ đơn giản về hình dạng không thể tạo ra là vốn T. Một phiên bản tinh vi hơn là

nhập mô tả hình ảnh ở đây

(Cảm ơn bạn vì Harald Hanche-Olsen vì ví dụ này)

Trong ví dụ này, tất cả các đường ngang liền kề cách nhau 1 chiều. Do đó, không có động thái pháp lý từ vị trí này và vì vấn đề là có thể đảo ngược, do đó không có cách nào để đạt được điều đó từ vị trí bắt đầu.

Ngôn ngữ và thư viện

Bạn có thể sử dụng bất kỳ ngôn ngữ nào có trình biên dịch / trình thông dịch / có sẵn miễn phí. cho Linux và bất kỳ thư viện nào cũng có sẵn miễn phí cho Linux.

Máy của tôi Thời gian sẽ được chạy trên máy của tôi. Đây là bản cài đặt Ubuntu tiêu chuẩn trên Bộ xử lý tám lõi AMD FX-8350. Điều này cũng có nghĩa là tôi cần để có thể chạy mã của bạn. Do đó, chỉ sử dụng phần mềm miễn phí có sẵn dễ dàng và vui lòng bao gồm các hướng dẫn đầy đủ về cách biên dịch và chạy mã của bạn.


1
@TApicella Cảm ơn các câu hỏi. Khi tôi nói "Khi một vòng quay xảy ra, nó sẽ di chuyển một nửa con rắn với nó" Tôi không có nghĩa là 50 phần trăm. Tôi chỉ đề cập đến phần trước điểm xoay và phần sau nó. Nếu bạn xoay từ 0 dọc theo con rắn, bạn xoay toàn bộ!

2
@TApicella Về câu hỏi thứ hai của bạn. Vấn đề là không có sự luân chuyển hợp pháp từ vị trí mà tôi đã đưa ra. Nếu có thể tiếp cận được, bạn phải quay trở lại hướng bắt đầu theo chiều ngang bằng một chuỗi các phép quay (đảo ngược các hướng bạn sẽ thực hiện để đi đến hướng kết thúc.) Bạn có thể mô tả một vòng quay hợp pháp mà bạn nghĩ bạn có thể thực hiện từ vị trí này? Để rõ ràng, con rắn không phát triển. Nó luôn luôn giữ nguyên chiều dài trong suốt.

3
@TApicella Nghe có vẻ như bạn mong đợi con rắn sẽ lớn lên. Kích thước của nó là cố định mặc dù. Bạn bắt đầu với một con rắn dài và tất cả những gì bạn được phép làm là gấp các phần của nó 90 độ. Từ vị trí hiện tại, bạn không thể áp dụng bất kỳ nếp gấp nào sẽ dẫn đến giai đoạn trước của con rắn.
Martin Ender

1
Bạn có thể gấp tại một điểm nhiều lần (qua lại) không? Nếu bạn có thể làm cho nó khá phức tạp.
ngẫu nhiên

1
@randomra Bạn thực sự có thể miễn là bạn không bao giờ đi xa hơn chín mươi độ từ thẳng.

Câu trả lời:


5

Python 3 - điểm tạm thời: n = 11 (n = 13 với PyPy *)

Vì không có câu trả lời trong tuần đầu tiên, đây là một ví dụ trong Python để khuyến khích cạnh tranh. Tôi đã cố gắng làm cho nó dễ đọc một cách hợp lý để có thể dễ dàng xác định được sự thiếu hiệu quả để đưa ra ý tưởng cho các câu trả lời khác.

Tiếp cận

  • Bắt đầu với con rắn thẳng và tìm tất cả các vị trí có thể đạt được một cách hợp pháp trong một lần di chuyển.
  • Tìm tất cả các vị trí có thể đạt được một cách hợp pháp từ các vị trí đó, chưa được xác định.
  • Lặp lại cho đến khi không thể tìm thấy nữa và trả lại số lượng vị trí được tìm thấy hoàn toàn.

(bây giờ với một số tài liệu và khẳng định sau lần thử đầu tiên không chính xác của tôi)

'''
Snake combinations

A snake is represented by a tuple giving the relative orientation at each joint.
A length n snake has n-1 joints.
Each relative orientation is one of the following:

0: Clockwise 90 degrees
1: Straight
2: Anticlockwise 90 degrees

So a straight snake of length 4 has 3 joints all set to 1:

(1, 1, 1)

x increases to the right
y increases upwards

'''


import turtle


def all_coords(state):
    '''Return list of coords starting from (0,0) heading right.'''
    current = (1, 0)
    heading = 0
    coords = [(0,0), (1,0)]
    for item in state:
        heading += item + 3
        heading %= 4
        offset = ((1,0), (0,1), (-1,0), (0,-1))[heading]
        current = tuple(current[i]+offset[i] for i in (0,1))
        coords.append(current)
    return coords


def line_segments(coords, pivot):
    '''Return list of line segments joining consecutive coords up to pivot-1.'''
    return [(coords[i], coords[i+1]) for i in range(pivot+1)]


def rotation_direction(coords, pivot, coords_in_final_after_pivot):
    '''Return -1 if turning clockwise, 1 if turning anticlockwise.'''
    pivot_coord = coords[pivot + 1]
    initial_coord = coords[pivot + 2]
    final_coord = coords_in_final_after_pivot[0]
    initial_direction = tuple(initial_coord[i] - pivot_coord[i] for i in (0,1))
    final_direction = tuple(final_coord[i] - pivot_coord[i] for i in (0,1))
    return (initial_direction[0] * final_direction[1] -
            initial_direction[1] * final_direction[0]
            )


def intersects(arc, line):
    '''Return True if the arc intersects the line segment.'''
    if line_segment_cuts_circle(arc, line):
        cut_points = points_cutting_circle(arc, line)
        if cut_points and cut_point_is_on_arc(arc, cut_points):
            return True


def line_segment_cuts_circle(arc, line):
    '''Return True if the line endpoints are not both inside or outside.'''
    centre, point, direction = arc
    start, finish = line
    point_distance_squared = distance_squared(centre, point)
    start_distance_squared = distance_squared(centre, start)
    finish_distance_squared = distance_squared(centre, finish)
    start_sign = start_distance_squared - point_distance_squared
    finish_sign = finish_distance_squared - point_distance_squared
    if start_sign * finish_sign <= 0:
        return True


def distance_squared(centre, point):
    '''Return the square of the distance between centre and point.'''
    return sum((point[i] - centre[i]) ** 2 for i in (0,1))


def cut_point_is_on_arc(arc, cut_points):
    '''Return True if any intersection point with circle is on arc.'''
    centre, arc_start, direction = arc
    relative_start = tuple(arc_start[i] - centre[i] for i in (0,1))
    relative_midpoint = ((relative_start[0] - direction*relative_start[1])/2,
                         (relative_start[1] + direction*relative_start[0])/2
                         )
    span_squared = distance_squared(relative_start, relative_midpoint)
    for cut_point in cut_points:
        relative_cut_point = tuple(cut_point[i] - centre[i] for i in (0,1))
        spacing_squared = distance_squared(relative_cut_point,
                                           relative_midpoint
                                           )
        if spacing_squared <= span_squared:
            return True


def points_cutting_circle(arc, line):
    '''Return list of points where line segment cuts circle.'''
    points = []
    start, finish = line
    centre, arc_start, direction = arc
    radius_squared = distance_squared(centre, arc_start)
    length_squared = distance_squared(start, finish)
    relative_start = tuple(start[i] - centre[i] for i in (0,1))
    relative_finish = tuple(finish[i] - centre[i] for i in (0,1))
    relative_midpoint = tuple((relative_start[i] +
                               relative_finish[i]
                               )*0.5 for i in (0,1))
    determinant = (relative_start[0]*relative_finish[1] -
                   relative_finish[0]*relative_start[1]
                   )
    determinant_squared = determinant ** 2
    discriminant = radius_squared * length_squared - determinant_squared
    offset = tuple(finish[i] - start[i] for i in (0,1))
    sgn = (1, -1)[offset[1] < 0]
    root_discriminant = discriminant ** 0.5
    one_over_length_squared = 1 / length_squared
    for sign in (-1, 1):
        x = (determinant * offset[1] +
             sign * sgn * offset[0] * root_discriminant
             ) * one_over_length_squared
        y = (-determinant * offset[0] +
             sign * abs(offset[1]) * root_discriminant
             ) * one_over_length_squared
        check = distance_squared(relative_midpoint, (x,y))
        if check <= length_squared * 0.25:
            points.append((centre[0] + x, centre[1] + y))
    return points


def potential_neighbours(candidate):
    '''Return list of states one turn away from candidate.'''
    states = []
    for i in range(len(candidate)):
        for orientation in range(3):
            if abs(candidate[i] - orientation) == 1:
                state = list(candidate)
                state[i] = orientation
                states.append(tuple(state))
    return states


def reachable(initial, final):
    '''
    Return True if final state can be reached in one legal move.

    >>> reachable((1,0,0), (0,0,0))
    False

    >>> reachable((0,1,0), (0,0,0))
    False

    >>> reachable((0,0,1), (0,0,0))
    False

    >>> reachable((1,2,2), (2,2,2))
    False

    >>> reachable((2,1,2), (2,2,2))
    False

    >>> reachable((2,2,1), (2,2,2))
    False

    >>> reachable((1,2,1,2,1,1,2,2,1), (1,2,1,2,1,1,2,1,1))
    False

    '''
    pivot = -1
    for i in range(len(initial)):
        if initial[i] != final[i]:
            pivot = i
            break

    assert pivot > -1, '''
        No pivot between {} and {}'''.format(initial, final)
    assert initial[pivot + 1:] == final[pivot + 1:], '''
        More than one pivot between {} and {}'''.format(initial, final)

    coords_in_initial = all_coords(initial)
    coords_in_final_after_pivot = all_coords(final)[pivot+2:]
    coords_in_initial_after_pivot = coords_in_initial[pivot+2:]
    line_segments_up_to_pivot = line_segments(coords_in_initial, pivot)

    direction = rotation_direction(coords_in_initial,
                                   pivot,
                                   coords_in_final_after_pivot
                                   )

    pivot_point = coords_in_initial[pivot + 1]

    for point in coords_in_initial_after_pivot:
        arc = (pivot_point, point, direction)
        if any(intersects(arc, line) for line in line_segments_up_to_pivot):
            return False
    return True


def display(snake):
    '''Display a line diagram of the snake.

    Accepts a snake as either a tuple:

    (1, 1, 2, 0)

    or a string:

    "1120"

    '''
    snake = tuple(int(s) for s in snake)
    coords = all_coords(snake)

    turtle.clearscreen()
    t = turtle.Turtle()
    t.hideturtle()
    s = t.screen
    s.tracer(0)

    width, height = s.window_width(), s.window_height()

    x_min = min(coord[0] for coord in coords)
    x_max = max(coord[0] for coord in coords)
    y_min = min(coord[1] for coord in coords)
    y_max = max(coord[1] for coord in coords)
    unit_length = min(width // (x_max - x_min + 1),
                      height // (y_max - y_min + 1)
                      )

    origin_x = -(x_min + x_max) * unit_length // 2
    origin_y = -(y_min + y_max) * unit_length // 2

    pen_width = max(1, unit_length // 20)
    t.pensize(pen_width)
    dot_size = max(4, pen_width * 3)

    t.penup()
    t.setpos(origin_x, origin_y)
    t.pendown()

    t.forward(unit_length)
    for joint in snake:
        t.dot(dot_size)
        t.left((joint - 1) * 90)
        t.forward(unit_length)
    s.update()


def neighbours(origin, excluded=()):
    '''Return list of states reachable in one step.'''
    states = []
    for candidate in potential_neighbours(origin):
        if candidate not in excluded and reachable(origin, candidate):
            states.append(candidate)
    return states


def mirrored_or_backwards(candidates):
    '''Return set of states that are equivalent to a state in candidates.'''
    states = set()
    for candidate in candidates:
        mirrored = tuple(2 - joint for joint in candidate)
        backwards = candidate[::-1]
        mirrored_backwards = mirrored[::-1]
        states |= set((mirrored, backwards, mirrored_backwards))
    return states


def possible_snakes(snake):
    '''
    Return the set of possible arrangements of the given snake.

    >>> possible_snakes((1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1))
    {(1, 1, 1, 1, 2, 1, 2, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1)}

    '''
    reached = set()
    candidates = set((snake,))

    while candidates:
        candidate = candidates.pop()
        reached.add(candidate)
        new_candidates = neighbours(candidate, reached)
        reached |= mirrored_or_backwards(new_candidates)
        set_of_new_candidates = set(new_candidates)
        reached |= set_of_new_candidates
        candidates |= set_of_new_candidates

    excluded = set()
    final_answers = set()
    while reached:
        candidate = reached.pop()
        if candidate not in excluded:
            final_answers.add(candidate)
            excluded |= mirrored_or_backwards([candidate])

    return final_answers


def straight_derived_snakes(length):
    '''Return the set of possible arrangements of a snake of this length.'''
    straight_line = (1,) * max(length-1, 0)
    return possible_snakes(straight_line)


if __name__ == '__main__':
    import doctest
    doctest.testmod()
    import sys
    arguments = sys.argv[1:]
    if arguments:
        length = int(arguments[0])
    else:
        length = int(input('Enter the length of the snake:'))
    print(len(straight_derived_snakes(length)))

Các kết quả

Trên máy của tôi, con rắn dài nhất có thể tính được dưới 1 phút là chiều dài 11 (hoặc chiều dài 13 với PyPy *). Đây rõ ràng chỉ là một điểm tạm thời cho đến khi chúng tôi tìm ra điểm chính thức từ máy của Lembik.

Để so sánh, đây là kết quả cho một vài giá trị đầu tiên của n:

 n | reachable orientations
-----------------------------
 0 | 1
 1 | 1
 2 | 2
 3 | 4
 4 | 9
 5 | 22
 6 | 56
 7 | 147
 8 | 388
 9 | 1047
10 | 2806
11 | 7600
12 | 20437
13 | 55313
14 | 148752
15 | 401629
16 | 1078746
17 | MemoryError (on my machine)

Xin vui lòng cho tôi biết nếu bất kỳ trong số này hóa ra là không chính xác.

Nếu bạn có một ví dụ về một sự sắp xếp không thể mở ra được, bạn có thể sử dụng hàm neighbours(snake)để tìm bất kỳ sự sắp xếp nào có thể tiếp cận trong một bước, như một bài kiểm tra mã. snakelà một tuple của các hướng khớp (0 cho chiều kim đồng hồ, 1 cho thẳng, 2 cho ngược chiều kim đồng hồ). Ví dụ (1,1,1) là một con rắn thẳng dài 4 (có 3 khớp).

Hình dung

Để hình dung một con rắn bạn có trong tâm trí, hoặc bất kỳ con rắn nào được trả lại neighbours, bạn có thể sử dụng chức năng display(snake). Điều này chấp nhận một tuple như các chức năng khác, nhưng vì nó không được chương trình chính sử dụng và do đó không cần phải nhanh, nên nó cũng sẽ chấp nhận một chuỗi, để thuận tiện cho bạn.

display((1,1,2,0)) tương đương với display("1120")

Như Lembik đã đề cập trong các bình luận, kết quả của tôi giống hệt với OEIS A037245 không tính đến các giao lộ trong quá trình quay. Điều này là do đối với một vài giá trị đầu tiên của n không có sự khác biệt - tất cả các hình dạng không tự giao nhau có thể đạt được bằng cách gấp một con rắn thẳng. Tính chính xác của mã có thể được kiểm tra bằng cách gọi neighbours()với một con rắn không thể mở ra mà không có giao lộ. Vì nó không có hàng xóm nên nó sẽ chỉ đóng góp cho chuỗi OEIS chứ không đóng góp cho chuỗi này. Ví dụ nhỏ nhất mà tôi biết là con rắn dài 31 này mà Lembik đã đề cập, nhờ David K :

(1,1,1,1,2,1,2,1,1,1,1,1,1,2,1,1,1,2,1,1,2,2,1,0,1,0,1,1,1,1)

display('111121211111121112112210101111') đưa ra đầu ra sau:

Hình ảnh con rắn ngắn nhất không có hàng xóm

Mẹo: Nếu bạn thay đổi kích thước cửa sổ hiển thị và sau đó gọi lại màn hình, con rắn sẽ được gắn vào kích thước cửa sổ mới.

Tôi muốn nghe từ bất cứ ai có một ví dụ ngắn hơn không có hàng xóm. Tôi nghi ngờ rằng ví dụ ngắn nhất như vậy sẽ đánh dấu n nhỏ nhất mà hai chuỗi khác nhau.


* Lưu ý rằng mỗi lần tăng của n mất khoảng 3 lần thời gian, do đó, việc tăng từ n = 11 đến n = 13 đòi hỏi gần gấp 10 lần thời gian. Đây là lý do tại sao PyPy chỉ cho phép tăng n lên 2, mặc dù nó chạy nhanh hơn đáng kể so với trình thông dịch Python tiêu chuẩn.


6
Nếu nhận xét này nhận được 5 lượt ủng hộ, tôi sẽ xem xét thêm một tùy chọn để bao gồm trực quan hóa các sắp xếp có thể, trong trường hợp giúp phân tích.
trichoplax 3/2/2015


@Geobits Tôi nghĩ rằng tôi đã hiểu đúng vào lúc này ...
trichoplax 4/2/2015


1
@Jakube Điều này có thể mở được bằng nhiều cách, ví dụ như theo thứ tự khớp # 1 # 3 # 2 # 4 # 5 # 6.
Randomra 6/2/2015

1

C ++ 11 - gần như hoạt động :)

Sau khi đọc bài viết này , tôi đã thu thập được một chút trí tuệ từ anh chàng đó, người dường như đã làm việc trong 25 năm về vấn đề ít phức tạp hơn là đếm các con đường tự tránh trên một mạng vuông.

#include <cassert>
#include <ctime>
#include <sstream>
#include <vector>
#include <algorithm> // sort

using namespace std;

// theroretical max snake lenght (the code would need a few decades to process that value)
#define MAX_LENGTH ((int)(1+8*sizeof(unsigned)))

#ifndef _MSC_VER
#ifndef QT_DEBUG // using Qt IDE for g++ builds
#define NDEBUG
#endif
#endif

#ifdef NDEBUG
inline void tprintf(const char *, ...){}
#else
#define tprintf printf
#endif

void panic(const char * msg)
{
    printf("PANIC: %s\n", msg);
    exit(-1);
}

// ============================================================================
// fast bit reversal
// ============================================================================
unsigned bit_reverse(register unsigned x, unsigned len)
{
    x = (((x & 0xaaaaaaaa) >> 1) | ((x & 0x55555555) << 1));
    x = (((x & 0xcccccccc) >> 2) | ((x & 0x33333333) << 2));
    x = (((x & 0xf0f0f0f0) >> 4) | ((x & 0x0f0f0f0f) << 4));
    x = (((x & 0xff00ff00) >> 8) | ((x & 0x00ff00ff) << 8));
    return((x >> 16) | (x << 16)) >> (32-len);
}

// ============================================================================
// 2D geometry (restricted to integer coordinates and right angle rotations)
// ============================================================================

// points using integer- or float-valued coordinates
template<typename T>struct tTypedPoint;

typedef int    tCoord;
typedef double tFloatCoord;

typedef tTypedPoint<tCoord> tPoint;
typedef tTypedPoint<tFloatCoord>  tFloatPoint;

template <typename T>
struct tTypedPoint {
    T x, y;

    template<typename U> tTypedPoint(const tTypedPoint<U>& from) : x((T)from.x), y((T)from.y) {} // conversion constructor

    tTypedPoint() {}
    tTypedPoint(T x, T y) : x(x), y(y) {}
    tTypedPoint(const tTypedPoint& p) { *this = p; }
    tTypedPoint operator+ (const tTypedPoint & p) const { return{ x + p.x, y + p.y }; }
    tTypedPoint operator- (const tTypedPoint & p) const { return{ x - p.x, y - p.y }; }
    tTypedPoint operator* (T scalar) const { return{ x * scalar, y * scalar }; }
    tTypedPoint operator/ (T scalar) const { return{ x / scalar, y / scalar }; }
    bool operator== (const tTypedPoint & p) const { return x == p.x && y == p.y; }
    bool operator!= (const tTypedPoint & p) const { return !operator==(p); }
    T dot(const tTypedPoint &p) const { return x*p.x + y * p.y; } // dot product  
    int cross(const tTypedPoint &p) const { return x*p.y - y * p.x; } // z component of cross product
    T norm2(void) const { return dot(*this); }

    // works only with direction = 1 (90° right) or -1 (90° left)
    tTypedPoint rotate(int direction) const { return{ direction * y, -direction * x }; }
    tTypedPoint rotate(int direction, const tTypedPoint & center) const { return (*this - center).rotate(direction) + center; }

    // used to compute length of a ragdoll snake segment
    unsigned manhattan_distance(const tPoint & p) const { return abs(x-p.x) + abs(y-p.y); }
};


struct tArc {
    tPoint c;                        // circle center
    tFloatPoint middle_vector;       // vector splitting the arc in half
    tCoord      middle_vector_norm2; // precomputed for speed
    tFloatCoord dp_limit;

    tArc() {}
    tArc(tPoint c, tPoint p, int direction) : c(c)
    {
        tPoint r = p - c;
        tPoint end = r.rotate(direction);
        middle_vector = ((tFloatPoint)(r+end)) / sqrt(2); // works only for +-90° rotations. The vector should be normalized to circle radius in the general case
        middle_vector_norm2 = r.norm2();
        dp_limit = ((tFloatPoint)r).dot(middle_vector);
        assert (middle_vector == tPoint(0, 0) || dp_limit != 0);
    }

    bool contains(tFloatPoint p) // p must be a point on the circle
    {
        if ((p-c).dot(middle_vector) >= dp_limit)
        {
            return true;
        }
        else return false;
    }
};

// returns the point of line (p1 p2) that is closest to c
// handles degenerate case p1 = p2
tPoint line_closest_point(tPoint p1, tPoint p2, tPoint c)
{
    if (p1 == p2) return{ p1.x, p1.y };
    tPoint p1p2 = p2 - p1;
    tPoint p1c =  c  - p1;
    tPoint disp = (p1p2 * p1c.dot(p1p2)) / p1p2.norm2();
    return p1 + disp;
}

// variant of closest point computation that checks if the projection falls within the segment
bool closest_point_within(tPoint p1, tPoint p2, tPoint c, tPoint & res)
{
    tPoint p1p2 = p2 - p1;
    tPoint p1c = c - p1;
    tCoord nk = p1c.dot(p1p2);
    if (nk <= 0) return false;
    tCoord n = p1p2.norm2();
    if (nk >= n) return false;
    res = p1 + p1p2 * (nk / n);
    return true;
}

// tests intersection of line (p1 p2) with an arc
bool inter_seg_arc(tPoint p1, tPoint p2, tArc arc)
{
    tPoint m = line_closest_point(p1, p2, arc.c);
    tCoord r2 = arc.middle_vector_norm2;
    tPoint cm = m - arc.c;
    tCoord h2 = cm.norm2();
    if (r2 < h2) return false; // no circle intersection

    tPoint p1p2 = p2 - p1;
    tCoord n2p1p2 = p1p2.norm2();

    // works because by construction p is on (p1 p2)
    auto in_segment = [&](const tFloatPoint & p) -> bool
    {
        tFloatCoord nk = p1p2.dot(p - p1);
        return nk >= 0 && nk <= n2p1p2;
    };

    if (r2 == h2) return arc.contains(m) && in_segment(m); // tangent intersection

    //if (p1 == p2) return false; // degenerate segment located inside circle
    assert(p1 != p2);

    tFloatPoint u = (tFloatPoint)p1p2 * sqrt((r2-h2)/n2p1p2); // displacement on (p1 p2) from m to one intersection point

    tFloatPoint i1 = m + u;
    if    (arc.contains(i1) && in_segment(i1)) return true;
    tFloatPoint i2 = m - u;
    return arc.contains(i2) && in_segment(i2);
}

// ============================================================================
// compact storage of a configuration (64 bits)
// ============================================================================
struct sConfiguration {
    unsigned partition;
    unsigned folding;

    explicit sConfiguration() {}
    sConfiguration(unsigned partition, unsigned folding) : partition(partition), folding(folding) {}

    // add a bend
    sConfiguration bend(unsigned joint, int rotation) const
    {
        sConfiguration res;
        unsigned joint_mask = 1 << joint;
        res.partition = partition | joint_mask;
        res.folding = folding;
        if (rotation == -1) res.folding |= joint_mask;
        return res;
    }

    // textual representation
    string text(unsigned length) const
    {
        ostringstream res;

        unsigned f = folding;
        unsigned p = partition;

        int segment_len = 1;
        int direction = 1;
        for (size_t i = 1; i != length; i++)
        {
            if (p & 1)
            {
                res << segment_len * direction << ',';
                direction = (f & 1) ? -1 : 1;
                segment_len = 1;
            }
            else segment_len++;

            p >>= 1;
            f >>= 1;
        }
        res << segment_len * direction;
        return res.str();
    }

    // for final sorting
    bool operator< (const sConfiguration& c) const
    {
        return (partition == c.partition) ? folding < c.folding : partition < c.partition;
    }
};

// ============================================================================
// static snake geometry checking grid
// ============================================================================
typedef unsigned tConfId;

class tGrid {
    vector<tConfId>point;
    tConfId current;
    size_t snake_len;
    int min_x, max_x, min_y, max_y;
    size_t x_size, y_size;

    size_t raw_index(const tPoint& p) { bound_check(p);  return (p.x - min_x) + (p.y - min_y) * x_size; }
    void bound_check(const tPoint& p) const { assert(p.x >= min_x && p.x <= max_x && p.y >= min_y && p.y <= max_y); }

    void set(const tPoint& p)
    {
        point[raw_index(p)] = current;
    }
    bool check(const tPoint& p)
    {
        if (point[raw_index(p)] == current) return false;
        set(p);
        return true;
    }

public:
    tGrid(int len) : current(-1), snake_len(len)
    {
        min_x = -max(len - 3, 0);
        max_x = max(len - 0, 0);
        min_y = -max(len - 1, 0);
        max_y = max(len - 4, 0);
        x_size = max_x - min_x + 1;
        y_size = max_y - min_y + 1;
        point.assign(x_size * y_size, current);
    }

    bool check(sConfiguration c)
    {
        current++;
        tPoint d(1, 0);
        tPoint p(0, 0);
        set(p);
        for (size_t i = 1; i != snake_len; i++)
        {
            p = p + d;
            if (!check(p)) return false;
            if (c.partition & 1) d = d.rotate((c.folding & 1) ? -1 : 1);
            c.folding >>= 1;
            c.partition >>= 1;
        }
        return check(p + d);
    }

};

// ============================================================================
// snake ragdoll 
// ============================================================================
class tSnakeDoll {
    vector<tPoint>point; // snake geometry. Head at (0,0) pointing right

    // allows to check for collision with the area swept by a rotating segment
    struct rotatedSegment {
        struct segment { tPoint a, b; };
        tPoint  org;
        segment end;
        tArc    arc[3];
        bool extra_arc; // see if third arc is needed

        // empty constructor to avoid wasting time in vector initializations
        rotatedSegment(){}
        // copy constructor is mandatory for vectors *but* shall never be used, since we carefully pre-allocate vector memory
        rotatedSegment(const rotatedSegment &){ assert(!"rotatedSegment should never have been copy-constructed"); }

        // rotate a segment
        rotatedSegment(tPoint pivot, int rotation, tPoint o1, tPoint o2)
        {
            arc[0] = tArc(pivot, o1, rotation);
            arc[1] = tArc(pivot, o2, rotation);
            tPoint middle;
            extra_arc = closest_point_within(o1, o2, pivot, middle);
            if (extra_arc) arc[2] = tArc(pivot, middle, rotation);
            org = o1;
            end = { o1.rotate(rotation, pivot), o2.rotate(rotation, pivot) };
        }

        // check if a segment intersects the area swept during rotation
        bool intersects(tPoint p1, tPoint p2) const
        {
            auto print_arc = [&](int a) { tprintf("(%d,%d)(%d,%d) -> %d (%d,%d)[%f,%f]", p1.x, p1.y, p2.x, p2.y, a, arc[a].c.x, arc[a].c.y, arc[a].middle_vector.x, arc[a].middle_vector.y); };

            if (p1 == org) return false; // pivot is the only point allowed to intersect
            if (inter_seg_arc(p1, p2, arc[0])) 
            { 
                print_arc(0);  
                return true;
            }
            if (inter_seg_arc(p1, p2, arc[1]))
            { 
                print_arc(1); 
                return true;
            }
            if (extra_arc && inter_seg_arc(p1, p2, arc[2])) 
            { 
                print_arc(2);
                return true;
            }
            return false;
        }
    };

public:
    sConfiguration configuration;
    bool valid;

    // holds results of a folding attempt
    class snakeFolding {
        friend class tSnakeDoll;
        vector<rotatedSegment>segment; // rotated segments
        unsigned joint;
        int direction;
        size_t i_rotate;

        // pre-allocate rotated segments
        void reserve(size_t length)
        {
            segment.clear(); // this supposedly does not release vector storage memory
            segment.reserve(length);
        }

        // handle one segment rotation
        void rotate(tPoint pivot, int rotation, tPoint o1, tPoint o2)
        {
            segment.emplace_back(pivot, rotation, o1, o2);
        }
    public:
        // nothing done during construction
        snakeFolding(unsigned size)
        {
            segment.reserve (size);
        }
    };

    // empty default constructor to avoid wasting time in array/vector inits
    tSnakeDoll() {}

    // constructs ragdoll from compressed configuration
    tSnakeDoll(unsigned size, unsigned generator, unsigned folding) : point(size), configuration(generator,folding)
    {
        tPoint direction(1, 0);
        tPoint current = { 0, 0 };
        size_t p = 0;
        point[p++] = current;
        for (size_t i = 1; i != size; i++)
        {
            current = current + direction;
            if (generator & 1)
            {
                direction.rotate((folding & 1) ? -1 : 1);
                point[p++] = current;
            }
            folding >>= 1;
            generator >>= 1;
        }
        point[p++] = current;
        point.resize(p);
    }

    // constructs the initial flat snake
    tSnakeDoll(int size) : point(2), configuration(0,0), valid(true)
    {
        point[0] = { 0, 0 };
        point[1] = { size, 0 };
    }

    // constructs a new folding with one added rotation
    tSnakeDoll(const tSnakeDoll & parent, unsigned joint, int rotation, tGrid& grid)
    {
        // update configuration
        configuration = parent.configuration.bend(joint, rotation);

        // locate folding point
        unsigned p_joint = joint+1;
        tPoint pivot;
        size_t i_rotate = 0;
        for (size_t i = 1; i != parent.point.size(); i++)
        {
            unsigned len = parent.point[i].manhattan_distance(parent.point[i - 1]);
            if (len > p_joint)
            {
                pivot = parent.point[i - 1] + ((parent.point[i] - parent.point[i - 1]) / len) * p_joint;
                i_rotate = i;
                break;
            }
            else p_joint -= len;
        }

        // rotate around joint
        snakeFolding fold (parent.point.size() - i_rotate);
        fold.rotate(pivot, rotation, pivot, parent.point[i_rotate]);
        for (size_t i = i_rotate + 1; i != parent.point.size(); i++) fold.rotate(pivot, rotation, parent.point[i - 1], parent.point[i]);

        // copy unmoved points
        point.resize(parent.point.size()+1);
        size_t i;
        for (i = 0; i != i_rotate; i++) point[i] = parent.point[i];

        // copy rotated points
        for (; i != parent.point.size(); i++) point[i] = fold.segment[i - i_rotate].end.a;
        point[i] = fold.segment[i - 1 - i_rotate].end.b;

        // static configuration check
        valid = grid.check (configuration);

        // check collisions with swept arcs
        if (valid && parent.valid) // ;!; parent.valid test is temporary
        {
            for (const rotatedSegment & s : fold.segment)
            for (size_t i = 0; i != i_rotate; i++)
            {
                if (s.intersects(point[i+1], point[i]))
                {
                    //printf("! %s => %s\n", parent.trace().c_str(), trace().c_str());//;!;
                    valid = false;
                    break;
                }
            }
        }
    }

    // trace
    string trace(void) const
    {
        size_t len = 0;
        for (size_t i = 1; i != point.size(); i++) len += point[i - 1].manhattan_distance(point[i]);
        return configuration.text(len);
    }
};

// ============================================================================
// snake twisting engine
// ============================================================================
class cSnakeFolder {
    int length;
    unsigned num_joints;
    tGrid grid;

    // filter redundant configurations
    bool is_unique (sConfiguration c)
    {
        unsigned reverse_p = bit_reverse(c.partition, num_joints);
        if (reverse_p < c.partition)
        {
            tprintf("P cut %s\n", c.text(length).c_str());
            return false;
        }
        else if (reverse_p == c.partition) // filter redundant foldings
        {
            unsigned first_joint_mask = c.partition & (-c.partition); // insulates leftmost bit
            unsigned reverse_f = bit_reverse(c.folding, num_joints);
            if (reverse_f & first_joint_mask) reverse_f = ~reverse_f & c.partition;

            if (reverse_f > c.folding)
            {
                tprintf("F cut %s\n", c.text(length).c_str());
                return false;
            }
        }
        return true;
    }

    // recursive folding
    void fold(tSnakeDoll snake, unsigned first_joint)
    {
        // count unique configurations
        if (snake.valid && is_unique(snake.configuration)) num_configurations++;

        // try to bend remaining joints
        for (size_t joint = first_joint; joint != num_joints; joint++)
        {
            // right bend
            tprintf("%s -> %s\n", snake.configuration.text(length).c_str(), snake.configuration.bend(joint,1).text(length).c_str());
            fold(tSnakeDoll(snake, joint, 1, grid), joint + 1);

            // left bend, except for the first joint
            if (snake.configuration.partition != 0)
            {
                tprintf("%s -> %s\n", snake.configuration.text(length).c_str(), snake.configuration.bend(joint, -1).text(length).c_str());
                fold(tSnakeDoll(snake, joint, -1, grid), joint + 1);
            }
        }
    }

public:
    // count of found configurations
    unsigned num_configurations;

    // constructor does all the work :)
    cSnakeFolder(int n) : length(n), grid(n), num_configurations(0)
    {
        num_joints = length - 1;

        // launch recursive folding
        fold(tSnakeDoll(length), 0);
    }
};

// ============================================================================
// here we go
// ============================================================================
int main(int argc, char * argv[])
{
#ifdef NDEBUG
    if (argc != 2) panic("give me a snake length or else");
    int length = atoi(argv[1]);
#else
    (void)argc; (void)argv;
    int length = 12;
#endif // NDEBUG

    if (length <= 0 || length >= MAX_LENGTH) panic("a snake of that length is hardly foldable");

    time_t start = time(NULL);
    cSnakeFolder snakes(length);
    time_t duration = time(NULL) - start;

    printf ("Found %d configuration%c of length %d in %lds\n", snakes.num_configurations, (snakes.num_configurations == 1) ? '\0' : 's', length, duration);
    return 0;
}

Xây dựng thực thi

Biên dịch với tôi sử dụng MinGW trong Win7 với g ++ 4.8 cho các bản dựng "linux", vì vậy tính di động không được đảm bảo 100%.g++ -O3 -std=c++11

Nó cũng hoạt động (loại) với một dự án MSVC2013 tiêu chuẩn

Bằng cách xác định NDEBUG, bạn có được dấu vết thực hiện thuật toán và tóm tắt các cấu hình được tìm thấy.

Biểu diễn

có hoặc không có bảng băm, trình biên dịch Microsoft thực hiện một cách thảm hại: g ++ build nhanh hơn 3 lần .

Thuật toán sử dụng thực tế không có bộ nhớ.

Vì kiểm tra va chạm gần bằng O (n), thời gian tính toán phải ở O (nk n ), với k hơi thấp hơn 3.
Trên i3-2100@3.1GHz của tôi, n = 17 mất khoảng 1:30 (khoảng 2 triệu rắn / phút).

Tôi không thực hiện tối ưu hóa, nhưng tôi sẽ không mong đợi nhiều hơn mức tăng x3, vì vậy về cơ bản tôi có thể hy vọng đạt được có thể n = 20 dưới một giờ, hoặc n = 24 dưới một ngày.

Để đạt được hình dạng không thể chịu được đầu tiên (n = 31) sẽ mất từ ​​vài năm đến một thập kỷ, giả sử không có sự cố mất điện.

Đếm hình

Một N kích thước con rắn có N-1 khớp.
Mỗi khớp có thể được đặt thẳng hoặc uốn cong sang trái hoặc phải (3 khả năng).
Số lần gấp có thể là 3 N-1 .
Va chạm sẽ giảm số đó đi một chút, vì vậy con số thực tế gần bằng 2,7 N-1

Tuy nhiên, nhiều nếp gấp như vậy dẫn đến hình dạng giống hệt nhau.

hai hình giống hệt nhau nếu có hoặc là một vòng xoay hoặc một đối xứng mà có thể chuyển đổi một thành kia.

Hãy xác định một phân đoạn là bất kỳ phần thẳng nào của cơ thể gấp.
Ví dụ, một con rắn cỡ 5 được gấp ở khớp thứ 2 sẽ có 2 đoạn (một dài 2 đơn vị và 3 đơn vị thứ hai dài).
Đoạn đầu tiên sẽ được đặt tên là đầuđuôi cuối cùng .

Theo quy ước, chúng tôi định hướng đầu rắn theo chiều ngang với cơ thể hướng sang phải (như trong hình đầu tiên của OP).

Chúng tôi chỉ định một con số nhất định với một danh sách các độ dài phân khúc đã ký, với độ dài dương cho thấy một nếp gấp phải và âm một nếp gấp trái.
Chiều dài ban đầu là tích cực theo quy ước.

Tách các đoạn và uốn cong

Nếu chúng ta chỉ xem xét các cách khác nhau mà một con rắn có chiều dài N có thể được chia thành các phân đoạn, chúng ta sẽ có một phân vùng giống hệt với các chế phẩm của N.

Sử dụng cùng một thuật toán như được hiển thị trong trang wiki, thật dễ dàng để tạo ra tất cả 2 phân vùng N-1 có thể có của con rắn.

Mỗi phân vùng sẽ lần lượt tạo ra tất cả các nếp gấp có thể bằng cách áp dụng các uốn cong trái hoặc phải cho tất cả các khớp của nó. Một lần gấp như vậy sẽ được gọi là một cấu hình .

Tất cả các phân vùng có thể có thể được biểu diễn bằng một số nguyên của các bit N-1, trong đó mỗi bit đại diện cho sự hiện diện của khớp. Chúng tôi sẽ gọi số nguyên này là một máy phát điện .

Cắt tỉa phân vùng

Bằng cách nhận thấy rằng việc bẻ cong một phân vùng nhất định từ đầu trở xuống tương đương với việc bẻ cong phân vùng đối xứng từ đuôi lên, chúng ta có thể tìm thấy tất cả các cặp phân vùng đối xứng và loại bỏ một trong hai.
Trình tạo phân vùng đối xứng là trình tạo của phân vùng được viết theo thứ tự bit ngược, rất dễ phát hiện và rẻ tiền.

Điều này sẽ loại bỏ gần một nửa các phân vùng có thể, ngoại lệ là các phân vùng có bộ tạo "palindromic" được giữ nguyên bởi đảo ngược bit (ví dụ 00100100).

Chăm sóc các triệu chứng ngang

Với các quy ước của chúng tôi (một con rắn bắt đầu chỉ sang phải), lần uốn đầu tiên được áp dụng ở bên phải sẽ tạo ra một họ các nếp gấp sẽ là đối xứng ngang với các lần uốn khác nhau chỉ ở lần uốn đầu tiên.

Nếu chúng ta quyết định rằng khúc cua đầu tiên sẽ luôn ở bên phải, chúng ta sẽ loại bỏ tất cả các phép đối xứng ngang trong một cú trượt lớn.

Lau dọn các palindromes

Hai vết cắt này là hiệu quả, nhưng không đủ để chăm sóc các palindromes pesky này.
Kiểm tra kỹ lưỡng nhất trong trường hợp chung như sau:

xem xét một cấu hình C với một phân vùng palindromic.

  • nếu chúng ta đảo ngược mọi uốn cong trong C, chúng ta sẽ có một biểu tượng ngang của C.
  • Nếu chúng ta đảo ngược C (áp dụng các uốn cong từ đuôi lên), chúng ta sẽ có cùng một con số được quay đúng
  • nếu cả hai chúng ta đảo ngược và đảo ngược C, chúng ta sẽ có cùng một con số được quay trái.

Chúng tôi có thể kiểm tra mọi cấu hình mới so với 3 cấu hình khác. Tuy nhiên, vì chúng tôi đã chỉ tạo các cấu hình bắt đầu bằng một rẽ phải, chúng tôi chỉ có một biểu tượng có thể kiểm tra:

  • C đảo ngược sẽ bắt đầu rẽ trái, do xây dựng không thể nhân đôi
  • trong số các cấu hình đảo ngược và đảo ngược, chỉ có một cấu hình sẽ bắt đầu với một rẽ phải.
    Đó là cấu hình duy nhất chúng ta có thể nhân đôi.

Loại bỏ trùng lặp mà không có bất kỳ lưu trữ

Cách tiếp cận ban đầu của tôi là lưu trữ tất cả các cấu hình trong một bảng băm lớn, để loại bỏ các bản sao bằng cách kiểm tra sự hiện diện của cấu hình đối xứng được tính toán trước đó.

Nhờ vào bài viết đã nói ở trên, rõ ràng rằng, vì các phân vùng và nếp gấp được lưu trữ dưới dạng bitfield, chúng có thể được so sánh như bất kỳ giá trị số nào.
Vì vậy, để loại bỏ một thành viên của một cặp đối xứng, bạn chỉ cần so sánh cả hai yếu tố và giữ một cách có hệ thống cái nhỏ nhất (hoặc cái lớn nhất, như bạn muốn).

Do đó, kiểm tra một cấu hình để nhân đôi số tiền để tính toán phân vùng đối xứng và nếu cả hai đều giống hệt nhau, thì việc gấp lại. Không có bộ nhớ là cần thiết ở tất cả.

Trật tự thế hệ

Rõ ràng kiểm tra va chạm sẽ là phần tốn nhiều thời gian nhất, vì vậy việc giảm các tính toán này là một công cụ tiết kiệm thời gian chính.

Một giải pháp khả thi là có một "con rắn ragdoll" sẽ bắt đầu ở cấu hình phẳng và được uốn cong dần, để tránh tính toán lại toàn bộ hình dạng con rắn cho từng cấu hình có thể.

Bằng cách chọn thứ tự các cấu hình được kiểm tra, sao cho tối đa một ragdoll được lưu trữ cho mỗi tổng số khớp, chúng ta có thể giới hạn số lượng phiên bản thành N-1.

Tôi sử dụng quét đệ quy sake từ đuôi trở xuống, thêm một khớp duy nhất ở mỗi cấp. Do đó, một phiên bản ragdoll mới được xây dựng dựa trên cấu hình cha, với một uốn cong quảng cáo duy nhất.

Điều này có nghĩa là uốn cong được áp dụng theo thứ tự liên tục, dường như là đủ để tránh tự va chạm trong hầu hết các trường hợp.

Khi tự va chạm được phát hiện, các khúc cua dẫn đến di chuyển vi phạm được áp dụng trong tất cả các đơn đặt hàng có thể cho đến khi tìm thấy gấp hợp pháp hoặc tất cả các kết hợp đã hết.

Kiểm tra tĩnh

Trước cả khi nghĩ về các bộ phận chuyển động, tôi thấy hiệu quả hơn khi kiểm tra hình dạng tĩnh cuối cùng của một con rắn để tự giao nhau.

Điều này được thực hiện bằng cách vẽ con rắn trên lưới. Mỗi điểm có thể được vẽ từ đầu xuống. Nếu có giao điểm tự, ít nhất một cặp điểm sẽ rơi vào cùng một vị trí. Điều này đòi hỏi chính xác N lô cho bất kỳ cấu hình con rắn nào, trong thời gian O (N) không đổi.

Ưu điểm chính của phương pháp này là một mình thử nghiệm tĩnh sẽ chỉ đơn giản chọn các đường dẫn tự tránh hợp lệ trên một mạng vuông, cho phép kiểm tra toàn bộ thuật toán bằng cách ức chế phát hiện va chạm động và đảm bảo chúng tôi tìm thấy số lượng chính xác của các đường dẫn đó.

Kiểm tra động

Khi một con rắn gập quanh một khớp, mỗi đoạn xoay sẽ quét một khu vực có hình dạng là bất cứ thứ gì ngoài tầm thường.
Rõ ràng bạn có thể kiểm tra va chạm bằng cách kiểm tra bao gồm trong tất cả các khu vực quét như vậy riêng lẻ. Kiểm tra toàn cầu sẽ hiệu quả hơn, nhưng với sự phức tạp của các khu vực tôi không thể nghĩ đến (ngoại trừ việc sử dụng GPU để vẽ tất cả các khu vực và thực hiện kiểm tra lần truy cập toàn cầu).

Vì kiểm tra tĩnh đảm nhiệm vị trí bắt đầu và kết thúc của từng phân đoạn, chúng tôi chỉ cần kiểm tra các giao điểm với các cung được quét bởi mỗi phân đoạn xoay.

Sau một cuộc thảo luận thú vị với trichoplax và một chút JavaScript để có được vòng bi của tôi, tôi đã nghĩ ra phương pháp này:

Để thử đặt nó trong một vài từ, nếu bạn gọi

  • C tâm quay,
  • S một đoạn quay của chiều dài và hướng tùy ý không chứa C ,
  • L dòng kéo dài S
  • H đường thẳng trực giao với L đi qua C ,
  • Tôi là giao điểm của LH ,

toán học
(nguồn: free.fr )

Đối với bất kỳ phân đoạn nào không chứa I , khu vực quét bị ràng buộc bởi 2 cung (và 2 phân đoạn đã được kiểm tra tĩnh).

Nếu tôi nằm trong phân khúc, vòng cung bị quét bởi tôi cũng phải được tính đến.

Điều này có nghĩa là chúng ta có thể kiểm tra từng đoạn không di chuyển so với từng đoạn xoay với 2 hoặc 3 đoạn giao nhau với cung

Tôi đã sử dụng hình học vector để tránh các hàm lượng giác hoàn toàn.
Các hoạt động vector tạo ra mã nhỏ gọn và (tương đối) có thể đọc được.

Giao điểm giữa các phân đoạn đòi hỏi một vectơ dấu phẩy động, nhưng logic phải miễn nhiễm với các lỗi làm tròn.
Tôi tìm thấy giải pháp thanh lịch và hiệu quả này trong một bài đăng diễn đàn tối nghĩa. Tôi tự hỏi tại sao nó không được công bố rộng rãi hơn.

Nó có hoạt động không?

Ức chế phát hiện va chạm động tạo ra các đường dẫn tự tránh chính xác lên tới n = 19, vì vậy tôi khá tự tin rằng bố cục toàn cầu hoạt động.

Phát hiện va chạm động tạo ra kết quả nhất quán, mặc dù việc kiểm tra các khúc cua theo thứ tự khác nhau bị thiếu (hiện tại).
Kết quả là, chương trình đếm những con rắn có thể uốn cong từ đầu xuống (tức là với các khớp được xếp theo thứ tự tăng dần khoảng cách từ đầu).

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.