【问题标题】:Recursive Python function to produce a list of anagrams用于生成字谜列表的递归 Python 函数
【发布时间】:2014-04-03 14:01:51
【问题描述】:

经过大量的挠头和谷歌搜索后,我仍然无法弄清楚这一点。我对 Python 很陌生,我在语法上苦苦挣扎。从概念上讲,我认为我对我想要做什么以及如何递归地做这件事有一个相当不错的想法。然而,从技术上讲,将其编码成 Python 被证明是一场噩梦。

基本上我想将一个单词的所有排列添加到列表中(不允许重复的字符),然后可以由另一个程序或函数调用。

return 命令和如何处理空格真的让我很困惑。我希望递归函数在展开后“返回”一些东西,但我不希望它停止函数,直到所有字符都迭代并且所有排列都在这些迭代中递归生成。当我运行下面的代码时,似乎什么也没发生。

def permutations(A, B = ''):
    assert len(A) >= 0
    assert len(A) == len(set(A))
    res = []
    if len(A) == 0: res = res.extend(B)
    else:
        for i in range(len(A)):
            permutations(A[0:i] + A[i+1:], B + A[i])
    return res

permutations('word'))

如果我运行下面的代码,它会在我的显示窗格中打印出 OK,但我不知道如何将其转换为可供其他程序使用的输出格式,例如列表。

def permutations(A, B = ''):
    assert len(A) >= 0
    assert len(A) == len(set(A))
    if len(A) == 0: print(B)
    else:
        for i in range(len(A)):
            permutations(A[0:i] + A[i+1:], B + A[i])

permutations('word')

在我还有一些头发的时候,请有人给我建议!非常感谢收到。

谢谢

乔恩

【问题讨论】:

  • 首先,这是复制itertools.permutations。其次,您需要阅读yield关键字;它完全符合您的要求。

标签: python recursion return permutation


【解决方案1】:

这是解决此问题的另一种方法:

  • 兼容 Python 2.7 和 3.3(尚未与其他版本测试)

  • 它将接受包含重复项的输入,并且只返回唯一的输出 (即permutations("woozy") 只会返回一次“oowzy”)

  • 它以排序顺序返回输出(并允许您指定排序键和升序或降序)

  • 在字符串输入时返回字符串输出

  • 它作为生成器运行,即不会将所有组合存储在内存中。如果这是你想要的,你必须明确地说出来(如下所示)

  • 编辑:我突然想到我省略了一个长度参数,所以我添加了一个。您现在可以从 6 个字母的字符串中请求所有唯一的 4 个字母排列。

事不宜迟:

from collections import Counter
import sys

if sys.hexversion < 0x3000000:
    # Python 2.x
    dict_items_list = lambda d: d.items()
    is_string       = lambda s: isinstance(s, basestring)
    rng             = xrange
else:
    # Python 3.x
    dict_items_list = lambda d: list(d.items())
    is_string       = lambda s: isinstance(s, str)
    rng             = range

def permutations(lst, length=None, key=None, reverse=False):
    """
    Generate all unique permutations of lst in sorted order

        lst        list of items to permute
        length     number of items to pick for each permutation (defaults to all items)
        key        sort-key for items in lst
        reverse    sort in reverse order?
    """
    # this function is basically a shell, setting up the values
    # for _permutations, which actually does most of the work
    if length is None:
        length = len(lst)
    elif length < 1 or length > len(lst):
        return []     # no possible answers

    # 'woozy' => [('w', 1), ('o', 2), ('z', 1), ('y', 1)]  # unknown order
    items = dict_items_list(Counter(lst))
    # => [('o', 2), ('w', 1), ('y', 1), ('z', 1)]          # now in sorted order
    items.sort(key=key, reverse=reverse)

    if is_string(lst):
        # if input was string, return generator of string
        return (''.join(s) for s in _permutations(items, length))
    else:
        # return generator of list
        return _permutations(items, length)

def _permutations(items, length):
    if length == 1:
        for item,num in items:
            yield [item]
    else:
        for ndx in rng(len(items)):
            # pick an item to start with
            item, num = items[ndx]

            # make new list of remaining items
            if num == 1:
                remaining_items = items[:ndx] + items[ndx+1:]
            else:
                remaining_items = items[:ndx] + [(item, num-1)] + items[ndx+1:]

            # recurse against remaining items
            for perm in _permutations(remaining_items, length-1):
                yield [item]+perm

# test run!
words = list(permutations("woozy"))

结果

['oowyz',
 'oowzy',
 'ooywz',
 'ooyzw',
 'oozwy',
 'oozyw',
 'owoyz',
 # ...
 'zwooy',
 'zwoyo',
 'zwyoo',
 'zyoow',
 'zyowo',
 'zywoo']   # 60 items = 5!/2!, as expected

【讨论】:

    【解决方案2】:

    我会这样做:

    def permute_nondupe_letters_to_words(iterable):
        return (''.join(i) for i in itertools.permutations(set(iterable)))
    

    并使用它:

    word = 'word'
    permutation_generator = permute_nondupe_letters_to_words(word)
    
    bucket_1, bucket_2 = [], []
    for i in permutation_generator:
        bucket_1.append(i)
        if i == 'owdr':
            break
    
    for i in permutation_generator:
        bucket_2.append(i)
    

    还有

    print(len(bucket_1), len(bucket_2))
    

    打印:

    (10, 14)
    

    【讨论】:

      【解决方案3】:

      像这样?

      from itertools import permutations
      a = [x for x in permutations('word')]
      print a
      

      输出:

      >>[('w', 'o', 'r', 'd'), ('w', 'o', 'd', 'r'), ('w', 'r', 'o', 'd'),
      >>('w', 'r', 'd', 'o'), ('w', 'd', 'o', 'r'), ('w', 'd', 'r', 'o'), 
      >>('o', 'w', 'r', 'd'), ..............
      

      编辑: 我刚刚意识到你说不允许重复的字符。 'word' 并不重要,但假设你有 'wordwwwdd'。然后你可以这样做:

      [x for x in permutations(''.join(set('wordwwwdd')))] 
      

      但是会因为使用set而打乱了顺序,所以看起来像:

      >> [('r', 'o', 'w', 'd'), ('r', 'o', 'd', 'w'), ('r', 'w', 'o', 'd')....
      

      【讨论】:

      • 对不起,我应该说不使用 itertools。这是一个展示我可以实现递归解决方案的练习。不过还是谢谢。
      【解决方案4】:

      基本上你的错误在于

      res = res.extend(B)
      

      .extend() 不返回新列表,而是修改实例。

      另一个问题是您不使用递归调用的返回值。 这是修复代码的一种方法:

      def permutations(A, B = ''):
          assert len(A) >= 0
          assert len(A) == len(set(A))
          if len(A) == 0:
              return [B]
          else:
              res = []        
              for i in range(len(A)):
                  res.extend(permutations(A[0:i] + A[i+1:], B + A[i]))
      
              return res
      
      print permutations('word')
      

      【讨论】:

        猜你喜欢
        • 2019-02-06
        • 2020-09-17
        • 1970-01-01
        • 2018-05-18
        • 2021-06-25
        • 2016-04-03
        • 1970-01-01
        • 2016-02-16
        • 2021-04-22
        相关资源
        最近更新 更多