Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/335.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 查找“;“最佳”;完全子图_Python_Graph - Fatal编程技术网

Python 查找“;“最佳”;完全子图

Python 查找“;“最佳”;完全子图,python,graph,Python,Graph,在优化我的一个应用程序的性能时,我在几行(Python)代码中遇到了巨大的性能瓶颈 我有N个代币。每个令牌都有一个分配给它的值。一些标记相互矛盾(例如,标记8和标记12不能“共存”)。我的工作是找到k-best令牌组。一组令牌的值只是其中令牌值的总和 天真算法(我已经实现了…): 查找令牌的所有2^N令牌组置换 消除其中存在矛盾的标记组 计算所有剩余令牌组的值 按值对令牌组排序 选择前K个令牌组 真实世界的数字-我需要从一组20个令牌中选出前10个令牌组(我计算了1000000个排列(!),缩小

在优化我的一个应用程序的性能时,我在几行(Python)代码中遇到了巨大的性能瓶颈

我有N个代币。每个令牌都有一个分配给它的值。一些标记相互矛盾(例如,标记8和标记12不能“共存”)。我的工作是找到k-best令牌组。一组令牌的值只是其中令牌值的总和

天真算法(我已经实现了…):

  • 查找令牌的所有2^N令牌组置换
  • 消除其中存在矛盾的标记组
  • 计算所有剩余令牌组的值
  • 按值对令牌组排序
  • 选择前K个令牌组
  • 真实世界的数字-我需要从一组20个令牌中选出前10个令牌组(我计算了1000000个排列(!),缩小到3500个不矛盾的令牌组。在我的笔记本电脑上花了5秒钟

    我确信我可以通过生成非冲突的令牌组以某种方式优化步骤1+2

    我也非常确定,我可以在一次搜索中神奇地找到最好的令牌组,并找到一种方法,通过减小值遍历令牌组,从而找到我正在寻找的10个最好的令牌组

    我的实际代码:

    all_possibilities = sum((list(itertools.combinations(token_list, i)) for i in xrange(len(token_list)+1)), [])
    all_possibilities = [list(option) for option in all_possibilities if self._no_contradiction(option)] 
    all_possibilities = [(option, self._probability(option)) for option in all_possibilities]
    all_possibilities.sort(key = lambda result: -result[1]) # sort by descending probability
    
    请帮忙


    好的。

    步骤1+2的简单方法如下:首先,定义一个标记列表和一个矛盾字典(每个键都是一个标记,每个值都是一组标记)。然后,对每个令牌采取两个操作:

    • 如果尚未冲突,则将其添加到
      结果
      ,并使用与当前添加的令牌冲突的令牌增加
      冲突
    • 不要将其添加到
      结果
      (选择忽略它)并移动到下一个标记
    下面是一个示例代码:

    token_list = ['a', 'b', 'c']
    
    contradictions = {
        'a': set(['b']),
        'b': set(['a']),
        'c': set()
    }
    
    class Generator(object):
        def __init__(self, token_list, contradictions):
            self.list = token_list
            self.contradictions = contradictions
            self.max_start = len(self.list) - 1
    
        def add_no(self, start, result, conflicting):
            if start < self.max_start:
                for g in self.gen(start + 1, result, conflicting):
                    yield g
            else:
                yield result[:]
    
        def add_yes(self, token, start, result, conflicting):
            result.append(token)
            new_conflicting = conflicting | self.contradictions[token]
            for g in self.add_no(start, result, new_conflicting):
                yield g
            result.pop()
    
        def gen(self, start, result, conflicting):
            token = self.list[start]
            if token not in conflicting:
                for g in self.add_yes(token, start, result, conflicting):
                    yield g
            for g in self.add_no(start, result, conflicting):
                yield g
    
        def go(self):
            return self.gen(0, [], set())
    
    这是一个递归算法,因此它对几千个令牌不起作用(因为Python的堆栈限制),但您可以轻松创建一个非递归算法。

    这里有一个可能的“启发式优化”方法和一个小示例:

    import itertools
    
    # tokens in decreasing order of value (must all be > 0)
    toks = 12, 11, 8, 7, 6, 2, 1
    
    # contradictions (dict highestvaltok -> set of incompatible ones)
    cont = {12: set([11, 8, 7, 2]),
        11: set([8, 7, 6]),
             7: set([2]),
         2: set([1]),
           }
    
    rec_calls = 0
    
    def bestgroup(toks, contdict, arein=(), contset=()):
      """Recursively compute the highest-valued non-contradictory subset of toks."""
      global rec_calls
      toks = list(toks)
      while toks:
        # find the top token compatible w/the ones in `arein`
        toptok = toks.pop(0)
        if toptok in contset:
          continue
        # try to extend with and without this toptok
        without_top = bestgroup(toks, contdict, arein, contset)
        contset = set(contset).union(c for c in contdict.get(toptok, ()))
        newarein = arein + (toptok,)
        with_top = bestgroup(toks, contdict, newarein, contset)
        rec_calls += 1
        if sum(with_top) > sum(without_top):
          return with_top
        else:
          return without_top
      return arein
    
    def noncongroups(toks, contdict):
      """Count possible, non-contradictory subsets of toks."""
      tot = 0
      for l in range(1, len(toks) + 1):
        for c in itertools.combinations(toks, l):
          if any(cont[k].intersection(c) for k in c if k in contdict): continue
          tot += 1
      return tot
    
    
    print bestgroup(toks, cont)
    print 'calls: %d (vs %d of %d)' % (rec_calls, noncongroups(toks, cont), 2**len(toks))
    
    我相信只要存在可行的(非矛盾的)子集,就会产生尽可能多的递归调用,但还没有证明它(所以我只是计算两者-
    非组
    当然与解决方案无关,它只是用来检查行为属性;-)

    如果这在您的“实际用例”基准测试上产生了可接受的加速,那么进一步的优化可能会引入alpha剪枝(这样您就可以停止沿着您知道的非生产性路径的递归——这就是标记中降序的动机;-)和递归消除(改为在函数中使用显式堆栈)。但是我想保持第一个版本的简单,这样就可以很容易地理解和验证它(而且,我认为进一步的优化只会起到很小的作用,比如说,最好是将典型的运行时减少一半,即使有那么多)。

    an
    O(n(logn))
    O(n+m)
    令牌和字符串长度
    m
    你的问题与NP完全群体问题的区别在于,你的“冲突”图是有结构的——也就是说,它可以投影到一维上(可以排序)

    这意味着您可以分而治之;毕竟,不重叠的范围彼此没有影响,因此不需要探索完整的状态空间。特别是,动态规划解决方案将起作用

    算法概要
  • 假设一个令牌的位置表示为
    [start,end)
    (即inclusive start,exclusive end)。按令牌end对令牌列表进行排序,我们将迭代它们
  • 您将扩展这些令牌的子集。这些令牌集将有一个端点(如果在子集的端点之前开始,则不能向子集添加令牌)和一个累积值。令牌子集的端点是子集中所有令牌端点的最大值
  • 您将维护映射(例如,通过哈希表或数组)从索引到已排序的令牌数组,在该数组中,所有内容都已处理完毕,并最终生成非冲突令牌的最佳子集。这意味着存储在索引J映射中的最佳子集只能包含索引小于或等于J的令牌
  • 在每个步骤中,您将计算某个位置J的最佳子集,然后可能会发生以下三种情况之一:您可能已经在映射中缓存了此计算(简单),或最佳子集包含项J,或最佳子集排除项J。如果您尚未缓存它,则只能通过尝试这两个选项来找出最佳子集是否包含项J
  • 现在,诀窍就在缓存中——您需要尝试这两个选项,这看起来像是一个递归(指数)搜索,但不需要


    • 如果索引
      J
      的最佳子集包括
      token[J]
      ,则它不能包括与该标记重叠的任何标记-尤其是,因为我们按
      token.end
      排序,所以该列表中有最后一个标记
      K
      token[K].end下面的解决方案生成所有最大不矛盾子集,利用这样一个事实,即从解决方案中省略一个元素没有意义,除非它与解决方案中的另一个元素相矛盾

      在元素t不与任何剩余元素冲突的情况下,避免第二次递归的简单优化应该有助于在冲突数量较少的情况下使该解决方案有效

      def solve(tokens, contradictions):
         if not tokens:
            yield set()
         else:
            tokens = set(tokens)
            t = tokens.pop()
            for solution in solve(tokens - contradictions[t], contradictions):
               yield solution | set([t])
            if contradictions[t] & tokens:
               for solution in solve(tokens, contradictions):
                  if contradictions[t] & solution:
                     yield solution
      

      此解决方案还表明,对于某些类型的输入,动态规划(也称为记忆)可能有助于进一步提高解决方案的性能。

      获取所有非冲突令牌组的一种非常简单的方法:

      #!/usr/bin/env python
      
      token_list = ['a', 'b', 'c']
      
      contradictions = {
          'a': set(['b']),
          'b': set(['a']),
          'c': set()
      }
      
      result = []
      
      while token_list:
          token = token_list.pop()
          new = [set([token])]
          for r in result:
              if token not in contradictions or not r & contradictions[token]:
                  new.append(r | set([token]))
          result.extend(new)
      
      print result
      

      帮助理解问题a
      #!/usr/bin/env python
      
      token_list = ['a', 'b', 'c']
      
      contradictions = {
          'a': set(['b']),
          'b': set(['a']),
          'c': set()
      }
      
      result = []
      
      while token_list:
          token = token_list.pop()
          new = [set([token])]
          for r in result:
              if token not in contradictions or not r & contradictions[token]:
                  new.append(r | set([token]))
          result.extend(new)
      
      print result