파이썬 : 1,688,293 1,579,182 1,524,054 1,450,842 1,093,195 이동
주요 방법은 main_to_help_best
선택된 일부 요소를 기본 스택에서 도우미 스택으로 이동하는 것입니다. 여기에는 everything
모든 것을 지정된 위치로 옮길 destination
것인지 또는 가장 큰 값만 유지할 것인지를 정의 하는 플래그 가 있습니다.destination
다른 도우미의 나머지 부분을 있습니다.
우리가 이사하고 있다고 가정 dst
helper 사용helper
기능은 대략 다음과 같이 설명 할 수 있습니다.
- 가장 큰 요소의 위치 찾기
- 가장 큰 요소의 상단에있는 모든 것을
helper
재귀 적으로 이동
- 가장 큰 요소를
dst
- 뒤로 밀기
helper
메인
- 가장 큰 요소가 나타날 때까지 2-4 반복
dst
- 에이. 경우에
everything
설정되고, 반복적으로 이동 주요 요소 dst
B를. 그렇지 않으면, 주에서 요소를 재귀 적으로helper
( sort2
내 코드에서) 주 정렬 알고리즘 은 main_to_help_best
로 everything
설정하여 호출 합니다.False
한 다음 가장 큰 요소를 다시 주로 이동 한 다음 모든 것을 도우미에서 주로 이동하여 정렬합니다.
코드에 주석으로 포함 된 추가 설명
기본적으로 내가 사용한 원리는 다음과 같습니다.
- 한 명의 도우미가 최대 요소를 포함하도록 유지
- 다른 도우미가 다른 요소를 포함하도록 유지
- 불필요한 움직임은 가능한 많이하지 마십시오
원칙 3은 소스가 이전 목적지 인 경우 이동을 계산하지 않고 구현됩니다 (즉, main을 help1로 이동 한 다음 help1에서 help2로 이동하려고 함). 원래 위치로 다시 이동하고 있습니다 (예 : main은 help1, help1은 main). 또한 이전 n
동작이 모두 같은 정수로 이동하면 실제로 순서를 바꿀 수 있습니다n
동작의 . 따라서 우리는 또한 그 수를 활용하여 더 많은 이동 수를 줄입니다.
이것은 메인 스택의 모든 요소를 알고 있기 때문에 유효하므로 나중에 요소를 뒤로 이동시킬 것으로 해석 될 수 있으므로이 이동을해서는 안됩니다.
샘플 런 (스택은 아래에서 위로 표시되므로 첫 번째 요소는 아래로)
길이 1
이동 : 0
작업 : 6
최대 : 0 ([1])
평균 : 0.000
길이 2
이동 : 60
작업 : 36
최대 : 4 ([1, 2])
평균 : 1.667
길이 3
이동 : 1030
작업 : 216
최대 : 9 ([2, 3, 1])
평균 : 4.769
길이 4
이동 : 11765
작업 : 1296
최대 : 19 ([3, 4, 2, 1])
평균 : 9.078
길이 5
이동 : 112325
작업 : 7776
최대 : 33 ([4, 5, 3, 2, 1])
평균 : 14.445
길이 6
이동 : 968015
작업 : 46656
최대 : 51 ([5, 6, 4, 3, 2, 1])
평균 : 20.748
--------------
사무용 겉옷
이동 : 1093195
작업 : 55986
평균 : 19.526
가장 큰 경우는 가장 큰 요소가 두 번째 바닥에 배치되고 나머지는 정렬되는 경우입니다. 최악의 경우 알고리즘이 O (n ^ 2)임을 알 수 있습니다.
이동의 수에 분명히 최소 n=1
와 n=2
우리가 결과에서 볼 수 있듯이, 나는이 또한 더 큰 값을 최소 믿고 n
내가 그것을 증명할 수는 없지만,.
자세한 설명은 코드에 있습니다.
from itertools import product
DEBUG = False
def sort_better(main, help1, help2):
# Offset denotes the bottom-most position which is incorrect
offset = len(main)
ref = list(reversed(sorted(main)))
for idx, ref_el, real_el in zip(range(len(main)), ref, main):
if ref_el != real_el:
offset = idx
break
num_moves = 0
# Move the largest to help1, the rest to help2
num_moves += main_to_help_best(main, help1, help2, offset, False)
# Move the largest back to main
num_moves += push_to_main(help1, main)
# Move everything (sorted in help2) back to main, keep it sorted
num_moves += move_to_main(help2, main, help1)
return num_moves
def main_to_help_best(main, dst, helper, offset, everything=True):
"""
Moves everything to dst if everything is true,
otherwise move only the largest to dst, and the rest to helper
"""
if offset >= len(main):
return 0
max_el = -10**10
max_idx = -1
# Find the location of the top-most largest element
for idx, el in enumerate(main[offset:]):
if el >= max_el:
max_idx = idx+offset
max_el = el
num_moves = 0
# Loop from that position downwards
for max_idx in range(max_idx, offset-1, -1):
# Processing only at positions with largest element
if main[max_idx] < max_el:
continue
# The number of elements above this largest element
top_count = len(main)-max_idx-1
# Move everything above this largest element to helper
num_moves += main_to_help_best(main, helper, dst, max_idx+1)
# Move the largest to dst
num_moves += move(main, dst)
# Move back the top elements
num_moves += push_to_main(helper, main, top_count)
# Here, the largest elements are in dst, the rest are in main, not sorted
if everything:
# Move everything to dst on top of the largest
num_moves += main_to_help_best(main, dst, helper, offset)
else:
# Move everything to helper, not with the largest
num_moves += main_to_help_best(main, helper, dst, offset)
return num_moves
def verify(lst, moves):
if len(moves) == 1:
return True
moves[1][0][:] = lst
for src, dst, el in moves[1:]:
move(src, dst)
return True
def equal(*args):
return len(set(str(arg.__init__) for arg in args))==1
def move(src, dst):
dst.append(src.pop())
el = dst[-1]
if not equal(dst, sort.lst) and list(reversed(sorted(dst))) != dst:
raise Exception('HELPER NOT SORTED: %s, %s' % (src, dst))
cur_len = len(move.history)
check_idx = -1
matched = False
prev_src, prev_dst, prev_el = move.history[check_idx]
# As long as the element is the same as previous elements,
# we can reorder the moves
while el == prev_el:
if equal(src, prev_dst) and equal(dst, prev_src):
del(move.history[check_idx])
matched = True
break
elif equal(src, prev_dst):
move.history[check_idx][1] = dst
matched = True
break
elif equal(dst, prev_src):
move.history[check_idx][0] = src
matched = True
break
check_idx -= 1
prev_src, prev_dst, prev_el = move.history[check_idx]
if not matched:
move.history.append([src, dst, el])
return len(move.history)-cur_len
def push_to_main(src, main, amount=-1):
num_moves = 0
if amount == -1:
amount = len(src)
if amount == 0:
return 0
for i in range(amount):
num_moves += move(src, main)
return num_moves
def push_to_help(main, dst, amount=-1):
num_moves = 0
if amount == -1:
amount = len(main)
if amount == 0:
return 0
for i in range(amount):
num_moves += move(main, dst)
return num_moves
def help_to_help(src, dst, main, amount=-1):
num_moves = 0
if amount == -1:
amount = len(src)
if amount == 0:
return 0
# Count the number of largest elements
src_len = len(src)
base_el = src[src_len-amount]
base_idx = src_len-amount+1
while base_idx < src_len and base_el == src[base_idx]:
base_idx += 1
# Move elements which are not the largest to main
num_moves += push_to_main(src, main, src_len-base_idx)
# Move the largest to destination
num_moves += push_to_help(src, dst, base_idx+amount-src_len)
# Move back from main
num_moves += push_to_help(main, dst, src_len-base_idx)
return num_moves
def move_to_main(src, main, helper, amount=-1):
num_moves = 0
if amount == -1:
amount = len(src)
if amount == 0:
return 0
# Count the number of largest elements
src_len = len(src)
base_el = src[src_len-amount]
base_idx = src_len-amount+1
while base_idx < src_len and base_el == src[base_idx]:
base_idx += 1
# Move elements which are not the largest to helper
num_moves += help_to_help(src, helper, main, src_len-base_idx)
# Move the largest to main
num_moves += push_to_main(src, main, base_idx+amount-src_len)
# Repeat for the rest of the elements now in the other helper
num_moves += move_to_main(helper, main, src, src_len-base_idx)
return num_moves
def main():
num_tasks = 0
num_moves = 0
for n in range(1, 7):
start_moves = num_moves
start_tasks = num_tasks
max_move = -1
max_main = []
for lst in map(list,product(*[[1,2,3,4,5,6]]*n)):
num_tasks += 1
if DEBUG: print lst, [], []
sort.lst = lst
cur_lst = lst[:]
move.history = [(None, None, None)]
help1 = []
help2 = []
moves = sort_better(lst, help1, help2)
if moves > max_move:
max_move = moves
max_main = cur_lst
num_moves += moves
if DEBUG: print '%s, %s, %s (moves: %d)' % (cur_lst, [], [], moves)
if list(reversed(sorted(lst))) != lst:
print 'NOT SORTED: %s' % lst
return
if DEBUG: print
# Verify that the modified list of moves is still valid
verify(cur_lst, move.history)
end_moves = num_moves - start_moves
end_tasks = num_tasks - start_tasks
print 'Length %d\nMoves: %d\nTasks: %d\nMax: %d (%s)\nAverage: %.3f\n' % (n, end_moves, end_tasks, max_move, max_main, 1.0*end_moves/end_tasks)
print '--------------'
print 'Overall\nMoves: %d\nTasks: %d\nAverage: %.3f' % (num_moves, num_tasks, 1.0*num_moves/num_tasks)
# Old sort method, which assumes we can only see the top of the stack
def sort(main, max_stack, a_stack):
height = len(main)
largest = -1
num_moves = 0
a_stack_second_el = 10**10
for i in range(height):
if len(main)==0:
break
el = main[-1]
if el > largest: # We found a new maximum element
if i < height-1: # Process only if it is not at the bottom of main stack
largest = el
if len(a_stack)>0 and a_stack[-1] < max_stack[-1] < a_stack_second_el:
a_stack_second_el = max_stack[-1]
# Move aux stack to max stack then reverse the role
num_moves += help_to_help(a_stack, max_stack, main)
max_stack, a_stack = a_stack, max_stack
if DEBUG: print 'Moved max_stack to a_stack: %s, %s, %s (moves: %d)' % (main, max_stack, a_stack, num_moves)
num_moves += move(main, max_stack)
if DEBUG: print 'Moved el to max_stack: %s, %s, %s (moves: %d)' % (main, max_stack, a_stack, num_moves)
elif el == largest:
# The maximum element is the same as in max stack, append
if i < height-1: # Only if the maximum element is not at the bottom
num_moves += move(main, max_stack)
elif len(a_stack)==0 or el <= a_stack[-1]:
# Current element is the same as in aux stack, append
if len(a_stack)>0 and el < a_stack[-1]:
a_stack_second_el = a_stack[-1]
num_moves += move(main, a_stack)
elif a_stack[-1] < el <= a_stack_second_el:
# Current element is larger, but smaller than the next largest element
# Step 1
# Move the smallest element(s) in aux stack into max stack
amount = 0
while len(a_stack)>0 and a_stack[-1] != a_stack_second_el:
num_moves += move(a_stack, max_stack)
amount += 1
# Step 2
# Move all elements in main stack that is between the smallest
# element in aux stack and current element
while len(main)>0 and max_stack[-1] <= main[-1] <= el:
if max_stack[-1] < main[-1] < a_stack_second_el:
a_stack_second_el = main[-1]
num_moves += move(main, a_stack)
el = a_stack[-1]
# Step 3
# Put the smallest element(s) back
for i in range(amount):
num_moves += move(max_stack, a_stack)
else: # Find a location in aux stack to put current element
# Step 1
# Move all elements into max stack as long as it will still
# fulfill the Hanoi condition on max stack, AND
# it should be greater than the smallest element in aux stack
# So that we won't duplicate work, because in Step 2 we want
# the main stack to contain the minimum element
while len(main)>0 and a_stack[-1] < main[-1] <= max_stack[-1]:
num_moves += move(main, max_stack)
# Step 2
# Pick the minimum between max stack and aux stack, move to main
# This will essentially sort (in reverse) the elements into main
# Don't move to main the element(s) found before Step 1, because
# we want to move them to aux stack
while True:
if len(a_stack)>0 and a_stack[-1] < max_stack[-1]:
num_moves += move(a_stack, main)
elif max_stack[-1] < el:
num_moves += move(max_stack, main)
else:
break
# Step 3
# Move all elements in main into aux stack, as long as it
# satisfies the Hanoi condition on aux stack
while max_stack[-1] == el:
num_moves += move(max_stack, a_stack)
while len(main)>0 and main[-1] <= a_stack[-1]:
if main[-1] < a_stack[-1] < a_stack_second_el:
a_stack_second_el = a_stack[-1]
num_moves += move(main, a_stack)
if DEBUG: print main, max_stack, a_stack
# Now max stack contains largest element(s), aux stack the rest
num_moves += push_to_main(max_stack, main)
num_moves += move_to_main(a_stack, main, max_stack)
return num_moves
if __name__ == '__main__':
main()