Python 3-잠정 점수 : n = 11 (PyPy *의 경우 n = 13)
첫 주에 답변이 없었으므로, 여기에 경쟁을 장려하는 파이썬의 예가 있습니다. 다른 답변에 대한 아이디어를 제공하기 위해 비 효율성을 쉽게 식별 할 수 있도록 합리적으로 읽을 수 있도록 노력했습니다.
접근하다
- 똑 바른 뱀으로 시작하여 한 번에 합법적으로 도달 할 수있는 모든 위치를 찾으십시오.
- 해당 위치에서 합법적으로 도달 할 수 있고 아직 식별되지 않은 모든 위치를 찾으십시오.
- 더 이상 찾을 수 없을 때까지 반복하고 찾은 위치 수를 모두 반환하십시오.
암호
(현재 잘못된 첫 시도 후 일부 doctest 및 주장과 함께)
'''
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)))
결과
내 컴퓨터에서 1 분 안에 계산할 수있는 가장 긴 뱀은 길이 11 (또는 PyPy *의 경우 길이 13)입니다. Lembik의 기계에서 공식 점수가 무엇인지 알 때까지 이것은 단지 잠정적 인 점수입니다.
비교를 위해 다음은 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)
이 중 잘못된 것이 있으면 알려주십시오.
전개 할 수없는 배열의 예가있는 경우 함수 neighbours(snake)
를 사용 하여 코드 테스트로 한 단계로 도달 할 수있는 배열을 찾을 수 있습니다. snake
관절 방향의 튜플입니다 (시계 방향으로 0, 직선으로 1, 시계 반대 방향으로 2). 예를 들어 (1,1,1)은 길이가 4 (관절 3 개) 인 직선 뱀입니다.
심상
염두에 둔 뱀 또는에서 반환 한 뱀을 시각화하려면 neighbours
함수를 사용할 수 있습니다 display(snake)
. 이것은 다른 기능과 마찬가지로 튜플을 허용하지만 기본 프로그램에서 사용하지 않으므로 빠를 필요가 없으므로 편의를 위해 문자열도 허용합니다.
display((1,1,2,0))
에 해당 display("1120")
Lembik이 의견에서 언급했듯이 내 결과는 회전하는 동안 교차를 고려하지 않은 OEIS A037245 와 동일합니다 . 이것은 n의 처음 몇 값에 차이가 없기 때문입니다. 직선 교차 뱀을 접 으면 자체 교차하지 않는 모든 모양에 도달 할 수 있습니다. neighbours()
교차없이 펼칠 수없는 뱀을 호출하여 코드의 정확성을 테스트 할 수 있습니다 . 이웃이 없기 때문에 OEIS 시퀀스에만 기여하고이 시퀀스에는 기여하지 않습니다. 내가 아는 가장 작은 예는 David K 덕분에 Lembik이 언급 한이 길이의 31 뱀입니다 .
(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')
다음과 같은 출력을 제공합니다.
팁 : 디스플레이 창의 크기를 조정 한 다음 다시 디스플레이를 호출하면 뱀이 새 창 크기에 맞춰집니다.
이웃이없는 짧은 예를 가진 사람의 의견을 듣고 싶습니다. 그러한 예제가 가장 짧은 것이 두 시퀀스가 다른 가장 작은 n을 표시한다고 생각합니다.
* n 씩 증가 할 때마다 3 배 정도의 시간이 걸리므로 n = 11에서 n = 13으로 늘리려면 거의 10 배가 걸립니다. 이것이 PyPy가 표준 Python 인터프리터보다 훨씬 빠르게 실행 되더라도 n을 2 씩만 늘릴 수있는 이유입니다.