Python: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
设置为if ,则将main中的元素递归移动到dst
b。否则,递归地将main中的元素移动到helper
然后,主排序算法(sort2
在我的代码中)将main_to_help_best
使用everything
set设置为False
,然后将最大的元素移回main,然后将所有内容从帮助程序移回main,保持其排序。
更多的解释作为注释嵌入到代码中。
基本上,我使用的原则是:
- 保留一名助手,以容纳最多的元素
- 保留另一个助手以包含任何其他元素
- 尽量不要做不必要的动作
原则3是通过不计算移动源(如果将源移动到先前的目标)来实现的(即,我们只是将main移到了help1,然后我们希望从help1移到help2),此外,如果我们将移动数减少了1,正在将其移回原始位置(即,将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()