使用有限的堆栈操作进行排序

时间:2018-11-28 19:03:30

标签: algorithm sorting

我在分拣机上工作,为了使复杂性最小化,我想将移动部件的数量减至最少。我来了以下设计:

  • 1个输入堆栈
  • 2个以上的输出堆栈
  • 启动时,机器已经知道所有物品,它们的当前顺序和所需的顺序。
  • 机器可以将一项从输入堆栈的底部移至其选择的输出堆栈的 bottom
  • 机器可以将所有项从输出堆栈移动到输入堆栈的 top 。这称为“返回”。 (在我的机器上,我计划由用户完成此操作。)
  • 除了返回以外,机器仅访问堆栈的底部。当堆栈返回到输入时,“新”项将是输入中的最后一项。这也意味着,如果机器将一组项目从输入移动到一个输出,则这些项目的顺序将颠倒。

机器的目标是从输入堆栈中取出所有项目,并最终将它们全部按排序顺序移至输出堆栈。第二个目标是将“堆栈退货”的数量减少到最少,因为在我的机器中,这是需要用户干预的部分。理想情况下,机器应在没有用户帮助的情况下尽可能多地进行排序。

我遇到的问题是我似乎找不到合适的算法来进行实际排序。我能找到的几乎所有算法都依赖于能够交换任意元素。分发/外部排序似乎很有希望,但是我发现的所有算法似乎都依赖于一次访问多个输入。

由于机器已经知道所有项目,因此我可以利用这一优势,对所有项目进行“内存中”排序。我尝试了从未排序状态到已排序状态的“寻路”,但是我无法使它真正收敛于解决方案。 (通常只是卡在一个循环中,来回移动堆栈。)

最好,我希望有一个至少可以使用2个输出堆栈的解决方案,但是如果有可用的话,可以使用更多的堆栈。

有趣的是,这是可以使用标准扑克牌玩的“游戏”:

  • 获取尽可能多的卡片以进行排序。 (我通常会得到13套西服。)
  • 将它们洗净并放在手中。确定您获得多少输出堆栈。
  • 您有两个有效的举动:
    • 您可以将最前卡移到手中,并将其放在任何输出堆栈的顶部。
    • 您可以在输出堆栈中捡起所有卡,并将它们放在手边的卡的背面
  • 在输出堆栈中按顺序排列卡片时,您将获胜。您的分数是您捡起筹码的次数。分数越低越好。

1 个答案:

答案 0 :(得分:1)

这可以通过将输出返回到输入的O(log(n))来完成。更准确地说,如果2 ceil(log_2(n)) - 1返回1 < n

让我们调用输出堆栈A和B。

首先考虑最简单的算法。我们遍历它们,将最小的卡片放在B上,将其余的卡片放在A上。然后将A放在输入上并重复。 n通过后,您将获得按排序的顺序。效率不高,但是可以。

现在我们可以做到了,这样每张通行证可以抽出2张卡吗?好吧,如果我们在上半部有卡1、4、5、8、9、12,...,其余在下半部,那么第一遍将在卡2之前找到卡1,将它们反向,第二遍则找到卡3之前的卡3,将它们反转,依此类推。每张通行证2张。但是通过1次返还2次返回,我们可以将所需的所有卡放在堆栈A的上半部分,其余的放在堆栈B上,返回堆栈A,返回堆栈B,然后开始提取。这需要2 + n/2次通过。

每张通行证有4张卡片怎么样?好吧,我们希望将其分为几个部分。上四分之一有纸牌1、8、9、16...。第二个四分之一有纸牌2、7、10、15...。第三个四分之一有3、6、11、14,...。最后一个有4、5、12、13...。基本上,如果您要对它们进行交易,则按顺序对前四个进行交易,对第二个按相反顺序进行交易,对后一个按顺序进行交易。

我们可以将它们分为2个季度。我们能弄清楚如何到达那里吗?倒退,在第二遍之后,我们希望A具有2,1的四分之一。和乙有宿舍4,3。然后我们返回A,返回B,我们很成功。因此,在第一遍之后,我们希望A具有2,4的四分之一,而B具有1,3的四分之一,返回A返回B。

转过来工作,在第1遍中,将组2,4放在A上,在B上1,3。返回A,返回B。然后在第2遍中,我们将组1,2放在A,3,4上在B上,返回A,然后返回B。然后我们开始进行交易,每遍通过4张牌。因此,现在我们使用4 + n/4返回。

如果继续进行逻辑运算,则在3次通过(6次返回)中,您可以弄清楚如何在提取阶段每次获得8张卡。在4张通行证(8张回程)中,每张通行证可获得16张卡。等等。逻辑很复杂,但是您要做的就是记住要让它们按顺序结束... 5,4,3,2,1。从最后一遍倒退到第一遍,弄清楚您必须如何完成了。然后您有了正向算法。

如果您玩数字游戏,如果n是2的幂,则同样可以很好地获得log_2(n) - 22 log_2(n) - 4的通行证,然后获得4的提取通行证与3之间的退回,以获得2 log_2(n) - 1返回,或者如果您接受log_2(n) - 1返回的2 log_2(n) - 2传递,然后2返回的1抽取传递它们之间的2 log_2(n) - 1返回。 (当然,这是假设n足够大,可以将其进行除法。对于第二版算法,其含义为“非1”。)我们很快就会看到一个较小的理由偏爱使用如果是2 < n,则为该算法的旧版本。

好的,如果您获得2的幂的倍数,这很好。但是,如果您有10张卡怎么办?插入假想卡,直到我们达到最接近的2的幂为止。我们为此遵循算法,只是不实际执行虚卡上的操作,除了虚卡不存在外,我们会得到应有的准确结果。  因此,我们有一个不超过2 ceil(log_2(n)) - 1个退货的一般解决方案。

现在我们知道为什么更喜欢将其分成4个组而不是2个。如果我们分成4个组,则第4个组可能只是虚构的牌,而我们又跳过了另一组。如果我们分成两组,那么每个组中总是有真实的牌,并且我们就无法保存回报。  如果n为3, 5, 6, 9, 10, 11, 12, 17, 18, ...,这会使我们加快1。

计算确切的规则将变得很复杂,我不会尝试编写代码来做到这一点。但是您应该可以从这里弄清楚。

我无法证明这一点,但是就卡的排列而言,您可能无法做得更好,因此该算法可能是最优的。 (当然,可以使用此算法克服一些排列。例如,如果我将所有内容反向传递,则将它们全部提取都比该算法要好。)但是,我希望找到针对给定排列的最佳策略是一个不错的选择。 NP完全问题。