簡體   English   中英

在python中混合開關和旋轉對數字進行排序

[英]sorting numbers with mix of switch and rotate in python

首先證明:)

切換:切換位置 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 ... 方法結束。

有沒有更好的方法同時使用 switch 和 rotate 來有效地獲得排序結果?

我現在想不出對任何給定列表進行排序的最有效方法(意味着使用最少的旋轉和切換次數的方法)。 但是我可以想出一種方法,給定一個列表,找到最有效的方法。

將您的問題視為圖形數據結構中的廣度優先搜索問題 如果列表可以通過單次交換或單次旋轉從當前列表中獲得,請考慮直接指向另一個列表。 進行廣度優先搜索,直到獲得排序列表。 那么從原始列表到排序列表的路徑是“最有效的方式”。 您實際上不需要設置圖形數據結構——這只是給出了算法的想法。

我會盡快嘗試在此處獲取一些特定代碼,但這里有一個大綱。 從一個只包含原始列表(它需要是一個元組,所以我將開始稱它們為元組)作為鍵和None作為值的字典開始。 這個字典包含“已經看到的元組”作為鍵,對於每個鍵,值是導致該鍵的元組。 也從只包含原始元組的隊列(可能是 Python 的deque )開始。 這是“已看到但尚未處理”的隊列。 然后運行一個循環:從隊列中彈出一個元組,檢查它是否是已排序的元組,然后對於單個開關或旋轉可到達的每個元組,檢查它是否已經被看到,將它添加到字典和隊列中。 最終您將到達已排序的元組(如果原始元組定義正確)。 使用“already seen”字典將排序后的元組打印回原始元組的路徑。

這是基於該算法的代碼。 可以進行進一步的優化,例如內聯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)

我不知道這是否能回答你的問題,我覺得這很棘手。


我寫了一個在循環中使用的類:

 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)

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]

這返回

# [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]

在開始時選擇一個您永遠不會切換到代表列表中不可移動的開始/結束的數字。 無論您選擇哪個數字,您切換無序元素和旋轉的簡單算法將始終有效。

請注意,如果您不選擇最小或最大元素,“亂序”會變得有點復雜,因為正確的順序是循環的。 小於您選擇的元素的元素會跟隨較大的元素。

嘗試所有的選擇,看看哪一個給出最快的結果。

例如:

不要切換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

編輯:當所有最佳解決方案都需要所有元素參與交換時,這並沒有找到最好的。 不過,它確實總能找到解決方案,而且是多項式時間。

Python 提供了使用 list sort內置函數對列表sort排序的最佳方式。 例如:

my_list=[3,1,0,2]
my_list.sort()
print(my_list)

輸出:[0,1,2,3]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM