混合使用switch和python进行旋转排序数字

时间:2019-02-23 10:46:42

标签: python python-3.x algorithm sorting

首先要论证:)

切换:在位置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 ...方法结束。

是否有更好的方法同时使用开关和旋转来有效地获得排序结果?

4 个答案:

答案 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]