【问题标题】:dart generating all possible combinations list of lists飞镖生成列表的所有可能组合列表
【发布时间】:2021-08-18 17:57:46
【问题描述】:

我有一个列表列表,类似于:

a = [ [1,2,3], [4,5,6], [7,8,9,10]]

我想创建所有可能的组合,如下所示:

[(1, 4, 7), (1, 4, 8), (1, 4, 9), (1, 4, 10), (1, 5, 7), (1, 5, 8), (1, 5, 9), (1, 5, 10), (1, 6, 7), (1, 6, 8), (1, 6, 9), (1, 6, 10), (2, 4, 7), (2, 4, 8), (2, 4, 9), (2, 4, 10), (2, 5, 7), (2, 5, 8), (2, 5, 9), (2, 5, 10), (2, 6, 7), (2, 6, 8), (2, 6, 9), (2, 6, 10), (3, 4, 7), (3, 4, 8), (3, 4, 9), (3, 4, 10), (3, 5, 7), (3, 5, 8), (3, 5, 9), (3, 5, 10), (3, 6, 7), (3, 6, 8), (3, 6, 9), (3, 6, 10)]

对于 python,有一个库 does exactly this.
Dart 有类似的解决方案吗?
如果没有,我将不胜感激一个简单的代码来实现这一点

【问题讨论】:

    标签: dart combinations


    【解决方案1】:

    一种方法可能是:

    Iterable<List<T>> allCombinations<T>(List<List<T>> sources) sync* {
      if (sources.isEmpty || sources.any((l) => l.isEmpty)) {
        yield [];
        return;
      }
      var indices = List<int>.filled(sources.length, 0);
      var next = 0;
      while (true) {
       yield [for (var i = 0; i < indices.length; i++) sources[i][indices[i]]];
       while (true) {
          var nextIndex = indices[next] + 1;
          if (nextIndex < sources[next].length) {
            indices[next] = nextIndex;
            break;
          }
          next += 1;
          if (next == sources.length) return;
        }
        indices.fillRange(0, next, 0);
        next = 0;
      }
    }
    

    这通过有效地将indices 视为基于源列表长度的可变基数中的数字,然后将其递增并创建相应的列表。

    时间复杂度仍然是?(Πi(source[<em>i</em>].length) * source.length)。

    【讨论】:

      【解决方案2】:

      找不到完全符合您要求的软件包,但如果您想引入自己的方法,我想您可以这样做:

      void main() {
        print(combinations([
          [1, 2, 3],
          [4, 5, 6],
          [7, 8, 9, 10]
        ]));
        // ([1, 4, 7], [1, 4, 8], [1, 4, 9], [1, 4, 10], ..., [3, 6, 9], [3, 6, 10])
      }
      
      Iterable<List<T>> combinations<T>(
          List<List<T>> lists, [
            int index = 0,
            List<T>? prefix,
          ]) sync* {
        prefix ??= <T>[];
      
        if (lists.length == index) {
          yield prefix.toList();
        } else {
          for (final value in lists[index]) {
            yield* combinations(lists, index + 1, prefix..add(value));
            prefix.removeLast();
          }
        }
      }
      

      更有效的解决方案但使用起来也有更大的风险,因为它确实需要combinations 的用户在使用输出时要小心,并确保不要保留内部Iterable 的任何实例:

      void main() {
        print(combinations([
          [1, 2, 3],
          [4, 5, 6],
          [7, 8, 9, 10]
        ]).map((e) => e.toList()));
        // ([1, 4, 7], [1, 4, 8], [1, 4, 9], [1, 4, 10], ..., [3, 6, 9], [3, 6, 10])
      }
      
      Iterable<Iterable<T>> combinations<T>(
          List<List<T>> lists, [
            int index = 0,
            List<T>? prefix,
          ]) sync* {
        prefix ??= <T>[];
      
        if (lists.length == index) {
          yield prefix;
        } else {
          for (final value in lists[index]) {
            yield* combinations(lists, index + 1, prefix..add(value));
            prefix.removeLast();
          }
        }
      }
      

      此解决方案的问题是存在误用的风险,如下例所示:

        final listOfCombinations = combinations([
          [1, 2, 3],
          [4, 5, 6],
          [7, 8, 9, 10]
        ]).toList();
        print(listOfCombinations);
        // [[], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], [], []]
      

      应该是:

        final listOfCombinations = combinations([
          [1, 2, 3],
          [4, 5, 6],
          [7, 8, 9, 10]
        ]).map((e) => e.toList()).toList();
        print(listOfCombinations);
        // [[1, 4, 7], [1, 4, 8], [1, 4, 9], [1, 4, 10], [1, 5, 7], [1, 5, 8], [1, 5, 9], [1, 5, 10], [1, 6, 7], [1, 6, 8], [1, 6, 9], [1, 6, 10], [2, 4, 7], [2, 4, 8], [2, 4, 9], [2, 4, 10], [2, 5, 7], [2, 5, 8], [2, 5, 9], [2, 5, 10], [2, 6, 7], [2, 6, 8], [2, 6, 9], [2, 6, 10], [3, 4, 7], [3, 4, 8], [3, 4, 9], [3, 4, 10], [3, 5, 7], [3, 5, 8], [3, 5, 9], [3, 5, 10], [3, 6, 7], [3, 6, 8], [3, 6, 9], [3, 6, 10]]
      

      因此,如果您不想冒此类问题的风险,请使用第一个建议的解决方案。 :)

      【讨论】:

      • 在每个递归调用上创建子列表效率非常低,但是 :)
      • @lrn 我同意这个解决方案有点糟糕,所以我要再次删除它...... :) - 好的......解决方案在我删除它之前就被接受了......我有点感觉现在有义务做出更好的解决方案,所以我可以替换这个答案......:D
      • 由于T 不是常量(取决于调用),它不能用于常量表达式。 const 表达式每次计算时总是计算出完全相同的值。
      • 另一种方法是使用列表作为“前缀”,然后执行prefix ??= &lt;T&gt;[]yield prefix.toList();prefix.add(value);yield* combinations(lists, index + 1, prefix);prefix.removeLast();。这避免了prefix.followedBy([value]) 的分配开销,这是一个额外的列表和一个Iterable.followedBy(每个嵌套也将有自己的迭代器)。如果这样记录,您甚至可以直接yield prefix;,并且只要求接收者在再次调用moveNext 后不要修改列表或重用它。更危险,但也更有效。
      • @lrn 是的,但是如果我们想表明我们不想修改,我们可以正确地将combinations 的返回值更改为Iterable&lt;Iterable&lt;T&gt;&gt; 并产生prefix。但是正如您所说,这确实要求消费内容而不是重复使用它。我认为在这里只产生prefix.toList() 是最安全的,但实施你建议的使用removeLast() 的改进。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-08-13
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 2018-10-15
      相关资源
      最近更新 更多