首先要论证:)
切换:在位置0和1切换弹珠。
旋转:将大理石从0位置移至位置N-1,并将所有其他大理石向左移动一个位置(降低一个索引)。
如果有数字列表(1,3,0,2) 开关-旋转-开关将对数字进行排序 3,1,0,2-1,0,2,3-0,1,2,3
但是如果我们有(3,1,0,2),则它永远不会以switch -rotate-switch-rotate ...方法结束。
是否有更好的方法同时使用开关和旋转来有效地获得排序结果?
答案 0 :(得分:2)
我现在无法想到对任何给定列表进行排序的最有效方法(即使用最少数量的旋转和切换的方法)。但我可以想到一种找到列表的最有效方法。
将您的问题视为图形数据结构中的breadth-first search problem。如果可以通过单次交换或单次旋转从当前列表中获取列表,则考虑将其直接指向另一个列表。进行广度优先搜索,直到获得排序列表。那么从原始列表到排序列表的路径就是“最有效的方法”。您实际上不需要设置图形数据结构,这只是给出了算法的思想。
我将尽力在这里获得一些特定的代码,但这只是概述。从仅包含原始列表(将是一个元组,因此我将开始将它们称为元组)的字典作为键,并将None
作为值。该词典包含“已经看到的元组”作为键,并且对于每个键,值都是导致该键的元组。还从仅包含原始元组的队列(可能是Python的deque
)开始。这是“已看到但尚未处理”的队列。然后运行一个循环:从队列中弹出一个元组,检查它是否为排序后的元组,然后通过单个开关或旋转检查每个元组是否已被看到,将其添加到字典和队列中。最终,您将到达已排序的元组(如果正确定义了原始元组)。使用“已经看过”的字典将已排序的元组的路径打印回原始元组。
这是基于该算法的代码。可以进行进一步的优化,例如内联switched_or_rotated
例程或在第一次看到目标元组时检查目标元组,而不是在处理它时等待。
from collections import deque
# Constant strings: ensure they are the same length for pretty printing
START = 'Start: '
SWITCH = 'Switch:'
ROTATE = 'Rotate:'
def switched_or_rotated(atuple):
"""Generate the tuples reachable from the given tuple by one switch
or rotation, with the action that created each tuple.
"""
yield (atuple[1::-1] + atuple[2:], SWITCH) # swap first two items
yield (atuple[1:] + atuple[:1], ROTATE) # rotate first item to the end
def sort_by_switch_and_rotate(iter):
"""Sort a finite, sortable iterable by repeatedly switching the
first two items and/or rotating it left (position 0 to the end, all
others to one index lower). Print a way to do this with the
smallest number of switches and/or rotations then return the number
of steps needed.
Based on <https://stackoverflow.com/questions/54840758/
sorting-numbers-with-mix-of-switch-and-rotate-in-python>
"""
# Initialize variables
original = tuple(iter)
targettuple = tuple(sorted(original))
alreadyseen = {original: None} # tuples already seen w/ previous tuple
actions = {original: START} # actions that got each tuple
notprocessed = deque() # tuples seen but not yet processed
# Do a breadth-first search for the target tuple
thistuple = original
while thistuple!= targettuple:
for nexttuple, nextaction in switched_or_rotated(thistuple):
if nexttuple not in alreadyseen:
alreadyseen[nexttuple] = thistuple
actions[nexttuple] = nextaction
notprocessed.append(nexttuple)
thistuple = notprocessed.popleft()
# Print the path from the original to the target
path = []
while thistuple:
path.append(thistuple)
thistuple = alreadyseen[thistuple]
print('\nHow to sort a list in {} steps:'.format(len(path)-1))
for thistuple in reversed(path):
print(actions[thistuple], thistuple)
# Return the minimal number of steps
return len(path) - 1
这是您的两个示例和一些其他示例的测试代码。
# Example tuples from the questioner
assert sort_by_switch_and_rotate((1, 3, 0, 2)) == 3
assert sort_by_switch_and_rotate((3, 1, 0, 2)) == 2
# Test tuples
assert sort_by_switch_and_rotate((0, 1, 2, 3)) == 0 # identity
assert sort_by_switch_and_rotate((1, 0, 2, 3)) == 1 # one switch
assert sort_by_switch_and_rotate((3, 0, 1, 2)) == 1 # one rotation
assert sort_by_switch_and_rotate((1, 2, 3, 0)) == 3 # max rotations
assert sort_by_switch_and_rotate((1, 0, 3, 2)) == 6 # from @MattTimmermans
打印出来的是
How to sort a list in 3 steps:
Start: (1, 3, 0, 2)
Switch: (3, 1, 0, 2)
Rotate: (1, 0, 2, 3)
Switch: (0, 1, 2, 3)
How to sort a list in 2 steps:
Start: (3, 1, 0, 2)
Rotate: (1, 0, 2, 3)
Switch: (0, 1, 2, 3)
How to sort a list in 0 steps:
Start: (0, 1, 2, 3)
How to sort a list in 1 steps:
Start: (1, 0, 2, 3)
Switch: (0, 1, 2, 3)
How to sort a list in 1 steps:
Start: (3, 0, 1, 2)
Rotate: (0, 1, 2, 3)
How to sort a list in 3 steps:
Start: (1, 2, 3, 0)
Rotate: (2, 3, 0, 1)
Rotate: (3, 0, 1, 2)
Rotate: (0, 1, 2, 3)
How to sort a list in 6 steps:
Start: (1, 0, 3, 2)
Switch: (0, 1, 3, 2)
Rotate: (1, 3, 2, 0)
Rotate: (3, 2, 0, 1)
Switch: (2, 3, 0, 1)
Rotate: (3, 0, 1, 2)
Rotate: (0, 1, 2, 3)
答案 1 :(得分:0)
我不知道这是否能回答您的问题,我觉得这很棘手。
class Marbles:
def __init__(self, marbles):
self.marbles = marbles
self.len = len(marbles)
def switch(self):
self.marbles[0], self.marbles[1] = self.marbles[1], self.marbles[0]
if self.is_sorted(): raise StopIteration
return self
def rotate(self):
self.marbles = self.marbles[1:] + [self.marbles[0]]
if self.is_sorted(): raise StopIteration
return self
def is_sorted(self):
return all(self.marbles[i] <= self.marbles[i+1] for i in range(self.len-1))
def show(self):
print(self.marbles)
在对大理石进行分类后,它会抛出异常StopIteration
,因此循环可能会中断。
因此,对于您的示例(1,3,0,2)
:
marbles = Marbles([1,3,0,2])
marbles.switch().show() #=> [3, 1, 0, 2]
marbles.rotate().show() #=> [1, 0, 2, 3]
marbles.switch().show() #=> StopIteration because it is sorted
现在您可以使用蛮力编写几个循环,交换动作顺序(在这种情况下,我认为该规则是切换和旋转的替代顺序):
tested = []
original = [3,1,0,2]
marbles = Marbles(original)
while True:
try:
marbles.switch().show()
marbles.rotate().show()
except: break
if original in tested: break
tested.append(marbles.marbles)
print(marbles.is_sorted())
marbles.show()
print("-"*20)
tested = []
original = [3,1,0,2]
marbles = Marbles(original)
while True:
try:
marbles.rotate().show()
marbles.switch().show()
except: break
if original in tested: break
tested.append(marbles.marbles)
print(marbles.is_sorted())
marbles.show()
这将返回
# [1, 3, 0, 2]
# [3, 0, 2, 1]
# [0, 3, 2, 1]
# [3, 2, 1, 0]
# [2, 3, 1, 0]
# [3, 1, 0, 2]
# [1, 3, 0, 2]
# [3, 0, 2, 1]
# False
# [3, 0, 2, 1]
# --------------------
# [1, 0, 2, 3]
# True
# [0, 1, 2, 3]
答案 2 :(得分:0)
在开头选择一个数字,您将从不切换为代表列表中无法移动的开头/结尾。无论您选择哪个数字,都可以使用简单的顺序切换乱序元素和旋转算法。
请注意,如果您不选择最小或最大元素,则“乱序”会变得有些复杂,因为正确的顺序是循环的。小于所选元素的元素将排在较大元素之后。
尝试所有选择,看看哪一个给出最快的结果。
例如:
请勿切换0:
3,1,0,2-1,3,0,2-3,0,2,1-0,2,1,3-2,1,3,0-1,2,3,0 -2,3,0,1-3,0,1,2-0,1,2,3
请勿切换1:
3,1,0,2-1,0,2,3-0,2,3,1-2,0,3,1-0,3,1,2-3,0,1,2 -0,1,2,3
请勿切换2:
3,1,0,2-1,0,2,3-0,1,2,3
请勿切换3:
3,1,0,2-1,0,2,3-0,1,2,3
编辑:当所有最佳解决方案都要求所有元素都参与交换时,这并不是最佳选择。但是,它总是可以找到一个解决方案,而且它是多项式时间。
答案 3 :(得分:-1)
Python提供了使用列表sort
内置函数对列表进行排序的最佳方法。例如:
my_list=[3,1,0,2]
my_list.sort()
print(my_list)
输出:[0,1,2,3]