【问题标题】:Backtrack in any programming language [closed]任何编程语言的回溯[关闭]
【发布时间】:2013-10-07 08:34:22
【问题描述】:

我的任务是编写一个字符串置换程序。我了解逻辑,但不了解此程序中 Backtrack 的确切含义。请解释一下for循环的功能,什么时候调用swap,什么时候调用permutate(),以及回溯的确切含义。

# include <stdio.h>


void swap (char *x, char *y)
{
    char temp;
    temp = *x;
    *x = *y;
    *y = temp;
}


void permute(char *a, int i, int n) 
{
   int j; 
   if (i == n)
     printf("%s\n", a);
   else
   {
        for (j = i; j <= n; j++)
       {
          swap((a+i), (a+j));
          permute(a, i+1, n);
          swap((a+i), (a+j)); //backtrack
       }
   }
} 


int main()
{
   char a[] = "ABC";  
   permute(a, 0, 2);
   getchar();
   return 0;
}

【问题讨论】:

  • 这个问题似乎跑题了,因为它是关于算法理论而不是关于编程的。
  • 从哪里得到CODE,你就会明白所有的意思。 :)
  • @KerrekSB 有什么地方可以问这类问题吗?我想问一下 for 循环中的逻辑。它是如何工作的?
  • @Purisima 没有解释,有人只写了代码
  • 就我个人而言,我不会将其描述为回溯。回溯是您尝试一种解决方案的地方,意识到它不起作用,然后返回(回溯)到较早的点并尝试其他方法。标记为 backtrack 的所有行都是撤消之前两行所做的更改。

标签: c++ c recursion backtracking


【解决方案1】:

绘制调用堆栈可以帮助您了解算法的工作原理。示例字符串“ABC”是一个很好的起点。基本上,这就是 ABC 会发生的事情:

permute(ABC, 0, 2)
    i = 0
    j = 0
    permute(ABC, 1, 2)
        i = 1
        j = 1
        permute(ABC, 2, 2)
            print "ABC"
        j = 2
        string = ACB
        permute(ACB, 2, 2)
            print "ACB"
        string = ABC
    j = 1
    string = BAC
    permute(BAC, 1, 2)
        .... (everything starts over)

像往常一样,在上面的示例中,缩进定义了每个递归调用内部发生的情况。

for 循环背后的原因是字符串 ABC 的每个排列由 ABC、BAC 和 CBA 给出,加上子字符串 BC、AC 和 BA 的每个排列(从前面的每个中删除第一个字母)。对于任何字符串 S,可能的排列是通过将每个位置与第一个位置交换,加上每个字符串的所有排列来获得的。可以这样想:任何置换的字符串必须以原始字符串中的一个字母开头,因此您将每个可能的字母放在第一个位置,然后递归地将相同的方法应用于字符串的其余部分(没有第一个字母) .

这就是循环正在做的事情:我们从当前起始点(即 i)扫描字符串直到结束,并且在每一步中,我们将该位置与起始点交换,递归调用 permute() 以打印每个对这个新字符串进行排列,然后我们将字符串恢复到之前的状态,这样我们就可以让原始字符串在下一个位置重复相同的过程。

就我个人而言,我不喜欢那种说“回溯”的评论。一个更好的术语是“回绕”,因为此时递归回绕,您为下一次递归调用准备字符串。回溯通常用于您探索子树但没有找到解决方案的情况,因此您返回(回溯)并尝试不同的分支。摘自维基百科:

回溯是查找所有(或部分)的通用算法 一些计算问题的解决方案,逐步构建 解决方案的候选者,并放弃每个部分候选者c (“回溯”)一旦确定 c 不可能是 完成到一个有效的解决方案。

请注意,此算法不会生成排列集,因为当有重复的字母时,它可以多次打印相同的字符串。一个极端的情况是当你将它应用到字符串“aaaaa”,或者任何其他有一个唯一字母的字符串。

【讨论】:

    【解决方案2】:

    “回溯”意味着,您在解决方案空间中倒退了一步(将其视为决策树,您正在上升一级)。如果您可以排除决策空间中的某些子树,则通常使用它,并且与对决策树的全面探索相比,可以显着提升性能当且仅当您很可能可以排除较大部分解空间

    您可以在此处找到对类似算法的详尽说明:Using recursion and backtracking to generate all possible combinations

    【讨论】:

    • 你描述的是branch and bound不是回溯。 en.wikipedia.org/wiki/Branch_and_boundA branch-and-bound algorithm consists of a systematic enumeration of all candidate solutions, where large subsets of fruitless candidates are discarded **en masse**, by using upper and lower estimated bounds of the quantity being optimized.
    • 不,分支和界限是关于通过使用估计的上限和下限en masse 消除无用的候选人 - 这与我的回答中的描述不同。 (回溯中不需要估计界限)
    • 你说得对,我误解了你的部分答案。
    猜你喜欢
    • 1970-01-01
    • 2013-06-19
    • 2011-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-02
    • 2012-10-02
    相关资源
    最近更新 更多