【问题标题】:How to return all valid combinations of n-pairs of parentheses?如何返回 n 对括号的所有有效组合?
【发布时间】:2013-12-12 06:30:16
【问题描述】:
def paren(n):
    lst = ['(' for x in range(n)]
    current_string = ''.join(lst)
    solutions = list()
    for i in range(len(current_string)+1):
        close(current_string, n, i, solutions)
    return solutions

def close(current_string, num_close_parens, index, solutions):
    """close parentheses recursively"""
    if num_close_parens == 0:
        if current_string not in solutions:
            solutions.append(current_string)
        return
    new_str = current_string[:index] + ')' + current_string[index:]
    if num_close_parens and is_valid(new_str[:index+1]):
        return close(new_str, num_close_parens-1, index+1, solutions)
    else:
        return close(current_string, num_close_parens, index+1, solutions)

def is_valid(part):
    """True if number of open parens >= number of close parens in given part"""
    count_open = 0
    count_close = 0
    for paren in part:
        if paren == '(':
            count_open += 1
        else:
            count_close += 1
    if count_open >= count_close:
        return True
    else:
        return False

print paren(3)

上面的代码是我解决上述问题的尝试。它为n<3 提供了足够的解决方案,但除此之外,它并没有给出所有的解决方案。例如,当n=3 时,它输出['()()()', '(())()', '((()))'],而省略'()(())'。如何修改代码以正确输出所有可能的解决方案?

【问题讨论】:

  • 而且还得这样递归解决?
  • 这似乎你想使用深度/广度优先搜索和回溯。画出 n=3 的有效解,它形成一个明显的图形。 behold, the mspaint
  • @Smac89 不,其中大多数无效。
  • 你说得对,我刚刚意识到 OP 想要“有效”而不是所有组合
  • @roippi 请参阅 en.wikipedia.org/wiki/Catalan_number 中的插图 :)

标签: python recursion backtracking


【解决方案1】:

这是一个递归生成器,可以生成所有有效的解决方案。与其他答案不同,这个答案从不计算需要过滤掉的重复或无效字符串。这与this answer to a previous question 中的算法几乎相同,尽管它不需要非递归辅助函数:

def paren(left, right=None):
    if right is None:
        right = left  # allows calls with one argument

    if left == right == 0: # base case
        yield ""

    else:
        if left > 0:
            for p in paren(left-1, right): # first recursion
                yield "("+p

        if right > left:
            for p in paren(left, right-1): # second recursion
                yield ")"+p

【讨论】:

  • 快速通过加泰罗尼亚语检查。 :^)
【解决方案2】:

如果不必使用递归来完成,这似乎可行:

from itertools import permutations

def valid(test):
  open, close = 0, 0
  for c in test:
    if c == '(':
      open += 1
    elif c == ')':
      close += 1
      if close > open:
        return False
  return True

def paren(n):
  perms = set(''.join(p) for p in permutations('(' * n + ')' * n))
  return [s for s in perms if valid(s)]

【讨论】:

  • 谢谢!我赞成你的回答。我目前正在尝试学习回溯,并且更想知道如何在不使用 permuations 模块的情况下做到这一点。
  • 当然。如果我有空闲时间,我也可以坐下来尝试编写递归解决方案。祝你学习顺利! :)
【解决方案3】:

我是动态编程和递归的新手,但这是我没有递归的解决方案。请让我知道为什么它不起作用或者这是一个可以接受的解决方案:

class Parenthesis(object):
    def __init__(self, parens):
        self.parens = parens
        self.my_valid_parens = {
                                1: ['()'],
                                2: ['()()', '(())']
                               }

    def generate_valid_paren(self):
        if self.parens <= 2:
            return self.my_valid_parens[self.parens] 

        i = 3
        while i <= self.parens:
            new_set = []
            for each in self.my_valid_parens[i-1]:
                new_set += set([each + '()', '()' + each, '(' + each + ')'])
            self.my_valid_parens[i] = list(new_set)
            i += 1

if __name__ == '__main__':
    num = 4
    p = Parenthesis(num)
    p.generate_valid_paren()
    print p.my_valid_parens[num]

这是我分别在 num = 3 和 4 时的输出:

3: ['(()())', '()()()', '()(())', '(())()', '((()))']

4: ['(()())()', '()(()())', '((()()))', '()()()()', '(()()())', '()()(())', '(()(()))', '()(())()', '((())())', '(())()()', '()(())()', '()((()))', '(((())))', '((()))()']

【讨论】:

    【解决方案4】:

    似乎任务归结为生成具有 N+1 个节点的所有可能的树。让我们假设整个字符串周围有另一对括号,那么对于 N=3,所有可能的树都将是

    ○ | o ((())) | ○ | ○ ○ / \ (())() 哦哦 | ○ ○ / \ o o ()(()) | ○ o ()()() /|\ o o o

    我现在无法为您提供任何代码(因此是 CW),但请参阅 this paper - 它似乎正好解决了这个问题。

    【讨论】:

      【解决方案5】:

      使用递归,很多itertools.permutations高效:

      def paren(n):
          ps = set(['(' * n + ')' * n])
          for i in range(1, n):
              for a in paren(i):
                  for b in paren(n-i):
                      ps.add(a + b)
          return ps
      

      由于反复递归,使用functools.lru_cache也可以提高效率。

      或者,使用内置记忆,

      def paren(n, known={}):
          if n in known:
              return known[n]
          ps = set(['(' * n + ')' * n])
          for i in range(1, n):
              for f in paren(i, known):
                  for s in paren(n-i, known):
                      ps.add(f + s)
          known[n] = ps
          return ps
      

      【讨论】:

      • OP 只需要有效的序列,而不是任何括号序列。即您的解决方案返回 )))((( 作为结果之一,但这不是有效的括号序列
      • 啊,我看到问题已经更新了。我会更新我的答案,FWIW。
      • 这很接近。但它仍然没有得到所有有效的组合。例如,paren(3) 不会产生 "(())()"
      • 这个仍然不正确,它返回一些 2**(n-1) 个可能的组合,而正确的数字是 catalan(n) = (2n)!/(n!(n+1)!)
      • 我认为您可以通过在末尾添加 if not p.startswith("()"): yield '%s()' % p 来完成这项工作(缩进与其他 yields 一样多)。
      【解决方案6】:

      对于 N==3,有 5 种有效组合:()()()、((()))、(()())、(())() 和 ()(())。

      递归算法是这样工作的:

      • 从左到右通过添加任一左侧来构建有效字符串 或一次一个右括号。
      • 基本情况:所有左括号和右括号都已使用(左 == n && 对 == n)。只需返回一个空字符串。否则:
      • 如果不是所有的左括号都被使用,则打印一个左括号 (left
      • 仅在使用右括号的数量时打印右括号 括号小于使用的左括号的数量(右

      这里是 Java 代码:

      public static ArrayList<String> parentheses(int n, int left, int right) {
      
      ArrayList<String> results = new ArrayList<String>();
      
        if (left == n && right == n) {
          results.add("");
        }
      
        if (left < n) {
          ArrayList<String> subResults = parentheses(n, left + 1, right);
          for (String subResult : subResults) {
            String newResult = "(" + subResult;
            results.add(newResult);
          }
        }
      
        if (left > right) {
          ArrayList<String> oldResults = parentheses(n, left, right + 1);
          for (String oldResult : oldResults) {
            String newResult = ")" + oldResult;
            results.add(newResult);
          }
        }
      
        return results;
      }
      

      最后,调用递归函数:

      parentheses(n, 0, 0);
      

      【讨论】:

        【解决方案7】:

        这是我的解决方案

        from itertools import permutations
        n = 3
        input = ("( " * n) + (") " * n)
        in_list = [f for f in input.split(" ") if f]
        possible_combinations = list(permutations(in_list, n*2))
        valid_list = []
        
        def ret_valid(el):
            num_open = num_close = 0
            for val in el:
                if val == "(":
                    num_open += 1
                else:
                    num_close += 1
                if num_close > num_open:
                    return False
            return True
        
        for el in possible_combinations:
            if ret_valid(el):
                if "".join(el) not in valid_list:
                    valid_list.append("".join(el))
        print(", ".join(valid_list))
        

        【讨论】:

          猜你喜欢
          • 2017-05-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-25
          • 1970-01-01
          • 2021-10-06
          • 1970-01-01
          • 2017-11-09
          相关资源
          最近更新 更多