如何按从最少尝试到最多尝试的顺序获得一系列布尔组合?

Posted

技术标签:

【中文标题】如何按从最少尝试到最多尝试的顺序获得一系列布尔组合?【英文标题】:How can I get an array of bool combinations in order from least trues to most trues? 【发布时间】:2016-03-17 18:16:15 【问题描述】:

我正在尝试创建一个 bool 数组。我想拥有除false, false, false, false 之外的所有布尔数组组合。我希望这个数组的顺序来保存它的子数组,这样它就可以按照最少正确到最多正确的顺序上升。 (倒序也可以,但还是得按序。)

如果数组的每个子集具有相同数量的真值,它们应该是随机顺序的。

我可以这样硬编码:

private bool[][] GetBoolArrays()

    var fourDoorList = new List<bool[]>();
    fourDoorList.Add(new bool[4]  true, true, true, true );
    fourDoorList = fourDoorList.OrderBy(c => Random.Range(float.MinValue, float.MaxValue)).ToList();
    var threeDoorList = new List<bool[]>();
    threeDoorList.Add(new bool[4]  true, true, true, false );
    threeDoorList.Add(new bool[4]  true, true, false, true );
    threeDoorList.Add(new bool[4]  true, false, true, true );
    threeDoorList.Add(new bool[4]  false, true, true, true );
    threeDoorList = threeDoorList.OrderBy(c => Random.Range(float.MinValue, float.MaxValue)).ToList();
    var twoDoorList = new List<bool[]>();
    twoDoorList.Add(new bool[4]  true, true, false, false );
    twoDoorList.Add(new bool[4]  true, false, true, false );
    twoDoorList.Add(new bool[4]  true, false, false, true );
    twoDoorList.Add(new bool[4]  false, true, true, false );
    twoDoorList.Add(new bool[4]  false, true, false, true );
    twoDoorList.Add(new bool[4]  false, false, true, true );
    twoDoorList = twoDoorList.OrderBy(c => Random.Range(float.MinValue, float.MaxValue)).ToList();
    var oneDoorList = new List<bool[]>();
    oneDoorList.Add(new bool[4]  true, false, false, false );
    oneDoorList.Add(new bool[4]  false, true, false, false );
    oneDoorList.Add(new bool[4]  false, false, true, false );
    oneDoorList.Add(new bool[4]  false, false, false, true );
    oneDoorList = oneDoorList.OrderBy(c => Random.Range(float.MinValue, float.MaxValue)).ToList();
    var boolArrayList = new List<bool[]>();
    boolArrayList.AddRange(fourDoorList);
    boolArrayList.AddRange(threeDoorList);
    boolArrayList.AddRange(twoDoorList);
    boolArrayList.AddRange(oneDoorList);
    return boolArrayList.ToArray();

但那太脏了!

我可以创建这样的列表,但这些列表按照我想要的方式无序:

private bool[][] GetBoolArrays()

    const int subArraySize = 4;
    bool[][] combinations = new bool[(int)Mathf.Pow(2, subArraySize) - 1][];
    for (int i = 1; i < Mathf.Pow(2, subArraySize); i++)
    
        string binary = System.Convert.ToString(i, 2);
        while (binary.Length < subArraySize)
        
            binary = 0 + binary;
        
        bool[] singleCombination = binary.Select(c => c == '1').ToArray();
        combinations[i - 1] = singleCombination;
    
    return combinations;

为了澄清,我正在尝试创建一个数组数组。每个子数组有 4 个布尔值。主数组具有子数组的所有组合,除了全部为假。子数组应按 true 数排序,但每个具有一定数量 true 的部分应随机化。

如果这对我所追求的东西的解释不佳,我深表歉意......这有点难以解释。我可以澄清任何需要的东西。关于如何清理硬编码版本的任何想法?

【问题讨论】:

【参考方案1】:

让我们进行一系列小的重构。我们开始:

private bool[][] GetBoolArrays()

    var fourDoorList = new List<bool[]>();
    fourDoorList.Add(new bool[4]  true, true, true, true );
    fourDoorList = fourDoorList.OrderBy(c => Random.Range(float.MinValue, float.MaxValue)).ToList();
    var threeDoorList = new List<bool[]>();
    threeDoorList.Add(new bool[4]  true, true, true, false );
    threeDoorList.Add(new bool[4]  true, true, false, true );
    threeDoorList.Add(new bool[4]  true, false, true, true );
    threeDoorList.Add(new bool[4]  false, true, true, true );
    threeDoorList = threeDoorList.OrderBy(c => Random.Range(float.MinValue, float.MaxValue)).ToList();
    var twoDoorList = new List<bool[]>();
    twoDoorList.Add(new bool[4]  true, true, false, false );
    twoDoorList.Add(new bool[4]  true, false, true, false );
    twoDoorList.Add(new bool[4]  true, false, false, true );
    twoDoorList.Add(new bool[4]  false, true, true, false );
    twoDoorList.Add(new bool[4]  false, true, false, true );
    twoDoorList.Add(new bool[4]  false, false, true, true );
    twoDoorList = twoDoorList.OrderBy(c => Random.Range(float.MinValue, float.MaxValue)).ToList();
    var oneDoorList = new List<bool[]>();
    oneDoorList.Add(new bool[4]  true, false, false, false );
    oneDoorList.Add(new bool[4]  false, true, false, false );
    oneDoorList.Add(new bool[4]  false, false, true, false );
    oneDoorList.Add(new bool[4]  false, false, false, true );
    oneDoorList = oneDoorList.OrderBy(c => Random.Range(float.MinValue, float.MaxValue)).ToList();
    var boolArrayList = new List<bool[]>();
    boolArrayList.AddRange(fourDoorList);
    boolArrayList.AddRange(threeDoorList);
    boolArrayList.AddRange(twoDoorList);
    boolArrayList.AddRange(oneDoorList);
    return boolArrayList.ToArray();

我们注意到的第一件事是随机播放代码是重复的。将其提取到辅助扩展中。另外,为什么我们需要把它变成一个列表?稍后我们将把它传递给 AddRange。保持顺序。

static IEnumerable<T> Shuffle<T>(this IEnumerable<T> items)

  return items.OrderBy(c => Random.Range(float.MinValue, float.MaxValue));

此外,我们现在有一个打乱的序列和一个未打乱的列表。让它们分开变量。

此外,我们注意到没有必要对只有一件事的列表进行洗牌!

好的,现在我们得到了什么?

private bool[][] GetBoolArrays()

    var fourDoorList = new List<bool[]>();
    fourDoorList.Add(new bool[4]  true, true, true, true );
    var fourDoorListShuffle = fourDoorList; // No point shuffling!
    var threeDoorList = new List<bool[]>();
    threeDoorList.Add(new bool[4]  true, true, true, false );
    threeDoorList.Add(new bool[4]  true, true, false, true );
    threeDoorList.Add(new bool[4]  true, false, true, true );
    threeDoorList.Add(new bool[4]  false, true, true, true );
    var threeDoorListShuffle = threeDoorList.Shuffle();
    var twoDoorList = new List<bool[]>();
    twoDoorList.Add(new bool[4]  true, true, false, false );
    twoDoorList.Add(new bool[4]  true, false, true, false );
    twoDoorList.Add(new bool[4]  true, false, false, true );
    twoDoorList.Add(new bool[4]  false, true, true, false );
    twoDoorList.Add(new bool[4]  false, true, false, true );
    twoDoorList.Add(new bool[4]  false, false, true, true );
    var twoDoorListShuffle = twoDoorList.Shuffle();
    var oneDoorList = new List<bool[]>();
    oneDoorList.Add(new bool[4]  true, false, false, false );
    oneDoorList.Add(new bool[4]  false, true, false, false );
    oneDoorList.Add(new bool[4]  false, false, true, false );
    oneDoorList.Add(new bool[4]  false, false, false, true );
    var oneDoorListShuffle = oneDoorList.Shuffle();
    var boolArrayList = new List<bool[]>();
    boolArrayList.AddRange(fourDoorListShuffle);
    boolArrayList.AddRange(threeDoorListShuffle);
    boolArrayList.AddRange(twoDoorListShuffle);
    boolArrayList.AddRange(oneDoorListShuffle);
    return boolArrayList.ToArray();

我们还注意到什么?我们说“new bool[4]”,但编译器可以推断类型和数字。

private bool[][] GetBoolArrays()

    var fourDoorList = new List<bool[]>();
    fourDoorList.Add(new[]  true, true, true, true );
    var fourDoorListShuffle = fourDoorList; // No point shuffling!
    var threeDoorList = new List<bool[]>();
    threeDoorList.Add(new[]  true, true, true, false );
    threeDoorList.Add(new[]  true, true, false, true );
    threeDoorList.Add(new[]  true, false, true, true );
    threeDoorList.Add(new[]  false, true, true, true );
    var threeDoorListShuffle = threeDoorList.Shuffle();
    var twoDoorList = new List<bool[]>();
    twoDoorList.Add(new[]  true, true, false, false );
    twoDoorList.Add(new[]  true, false, true, false );
    twoDoorList.Add(new[]  true, false, false, true );
    twoDoorList.Add(new[]  false, true, true, false );
    twoDoorList.Add(new[]  false, true, false, true );
    twoDoorList.Add(new[]  false, false, true, true );
    var twoDoorListShuffle = twoDoorList.Shuffle();
    var oneDoorList = new List<bool[]>();
    oneDoorList.Add(new[]  true, false, false, false );
    oneDoorList.Add(new[]  false, true, false, false );
    oneDoorList.Add(new[]  false, false, true, false );
    oneDoorList.Add(new[]  false, false, false, true );
    var oneDoorListShuffle = oneDoorList.Shuffle();
    var boolArrayList = new List<bool[]>();
    boolArrayList.AddRange(fourDoorListShuffle);
    boolArrayList.AddRange(threeDoorListShuffle);
    boolArrayList.AddRange(twoDoorListShuffle);
    boolArrayList.AddRange(oneDoorListShuffle);
    return boolArrayList.ToArray();

更好。如果我们使用集合初始化器而不是所有对 Add 的调用会怎样?

private bool[][] GetBoolArrays()

    var fourDoorList = new List<bool[]>() 
      new[]  true, true, true, true ;
    var fourDoorListShuffle = fourDoorList; // No point shuffling!
    var threeDoorList = new List<bool[]>() 
      new[]  true, true, true, false ,
      new[]  true, true, false, true ,
      new[]  true, false, true, true ,
      new[]  false, true, true, true ;
    var threeDoorListShuffle = threeDoorList.Shuffle();
    var twoDoorList = new List<bool[]>() 
      new[]  true, true, false, false ,
      new[]  true, false, true, false ,
      new[]  true, false, false, true ,
      new[]  false, true, true, false ,
      new[]  false, true, false, true ,
      new[]  false, false, true, true ;
    var twoDoorListShuffle = twoDoorList.Shuffle();
    var oneDoorList = new List<bool[]>() 
      new[]  true, false, false, false ,
      new[]  false, true, false, false ,
      new[]  false, false, true, false ,
      new[]  false, false, false, true ;
    var oneDoorListShuffle = oneDoorList.Shuffle();
    var boolArrayList = new List<bool[]>();
    boolArrayList.AddRange(fourDoorListShuffle);
    boolArrayList.AddRange(threeDoorListShuffle);
    boolArrayList.AddRange(twoDoorListShuffle);
    boolArrayList.AddRange(oneDoorListShuffle);
    return boolArrayList.ToArray();

更好。我们需要解释变量做什么?

private bool[][] GetBoolArrays()

    var fourDoorList = new List<bool[]>() 
      new[]  true, true, true, true ;
    var threeDoorList = new List<bool[]>() 
      new[]  true, true, true, false ,
      new[]  true, true, false, true ,
      new[]  true, false, true, true ,
      new[]  false, true, true, true ;
    var twoDoorList = new List<bool[]>() 
      new[]  true, true, false, false ,
      new[]  true, false, true, false ,
      new[]  true, false, false, true ,
      new[]  false, true, true, false ,
      new[]  false, true, false, true ,
      new[]  false, false, true, true ;
    var oneDoorList = new List<bool[]>() 
      new[]  true, false, false, false ,
      new[]  false, true, false, false ,
      new[]  false, false, true, false ,
      new[]  false, false, false, true ;
    var boolArrayList = new List<bool[]>();
    boolArrayList.AddRange(fourDoorList);
    boolArrayList.AddRange(threeDoorList.Shuffle());
    boolArrayList.AddRange(twoDoorList.Shuffle());
    boolArrayList.AddRange(oneDoorList.Shuffle());
    return boolArrayList.ToArray();

嗯,为什么任何都必须是列表?

private bool[][] GetBoolArrays()

    var fourDoorList = new[] 
      new[]  true, true, true, true ;
    var threeDoorList = new[] 
      new[]  true, true, true, false ,
      new[]  true, true, false, true ,
      new[]  true, false, true, true ,
      new[]  false, true, true, true ;
    var twoDoorList = new[] 
      new[]  true, true, false, false ,
      new[]  true, false, true, false ,
      new[]  true, false, false, true ,
      new[]  false, true, true, false ,
      new[]  false, true, false, true ,
      new[]  false, false, true, true ;
    var oneDoorList = new[] 
      new[]  true, false, false, false ,
      new[]  false, true, false, false ,
      new[]  false, false, true, false ,
      new[]  false, false, false, true ;
    var boolArrayList = new List<bool[]>();
    boolArrayList.AddRange(fourDoorList);
    boolArrayList.AddRange(threeDoorList.Shuffle());
    boolArrayList.AddRange(twoDoorList.Shuffle());
    boolArrayList.AddRange(oneDoorList.Shuffle());
    return boolArrayList.ToArray();

add-ranges 序列与 concats 序列相同:

private bool[][] GetBoolArrays()

    var fourDoorList = new[] 
      new[]  true, true, true, true ;
    var threeDoorList = new[] 
      new[]  true, true, true, false ,
      new[]  true, true, false, true ,
      new[]  true, false, true, true ,
      new[]  false, true, true, true ;
    var twoDoorList = new[] 
      new[]  true, true, false, false ,
      new[]  true, false, true, false ,
      new[]  true, false, false, true ,
      new[]  false, true, true, false ,
      new[]  false, true, false, true ,
      new[]  false, false, true, true ;
    var oneDoorList = new[] 
      new[]  true, false, false, false ,
      new[]  false, true, false, false ,
      new[]  false, false, true, false ,
      new[]  false, false, false, true ;
    return fourDoorList.
      Concat(threeDoorList.Shuffle()).
      Concat(twoDoorList.Shuffle()).
      Concat(oneDoorList.Shuffle()).
      ToArray();

这比原来的代码好看多了。请注意我们是如何简单地进行了一系列清晰、正确的重构,使每个版本都变得更好。

现在,你能不能创建一个方法来获取你想要的总布尔数和你想要的真数?

static IEnumerable<bool[]> Combinations(int totalCount, int trueCount) 
 
    You implement this

假设我们有这样一个方法,留作练习。 (我博客上的组合学文章可能会有所帮助。)

现在我们可以写了:

private bool[][] GetBoolArrays()

    var fourDoorList = Combinations(4, 4);
    var threeDoorList = Combinations(4, 3);
    var twoDoorList = Combinations(4, 2);
    var oneDoorList = Combinations(4, 1);
    return fourDoorList.
      Concat(threeDoorList.Shuffle()).
      Concat(twoDoorList.Shuffle()).
      Concat(oneDoorList.Shuffle()).
      ToArray();

现在,你能不能写一个有这个签名的方法:

static IEnumerable<T> MultiConcat(IEnumerable<IEnumerable<T>> sequences) 

   ... you implement this ...

如果可以,那么你可以写:

private bool[][] GetBoolArrays()

    var combinations = new[] 
      Combinations(4, 4).Shuffle(),
      Combinations(4, 3).Shuffle(),
      Combinations(4, 2).Shuffle(),
      Combinations(4, 1).Shuffle();
    return combinations.MultiConcat().ToArray();

我认为这确实比原始代码更容易阅读。事实上,我们可以把它归结为一个语句:

private bool[][] GetBoolArrays()

    return new[] 
    
      Combinations(4, 4).Shuffle(),
      Combinations(4, 3).Shuffle(),
      Combinations(4, 2).Shuffle(),
      Combinations(4, 1).Shuffle()
    .MultiConcat().ToArray();

但现在我们可能变得过于简洁了。

但我们现在不要停下来。里面有很多重复的代码!

private bool[][] GetBoolArrays()

  var q = from num in new[]  4, 3, 2, 1 
          select Combinations(4, num).Shuffle();
  return q.MultiConcat().ToArray();

哦,等等,我们已经在 LINQ 中内置了一个 multi concat!嘿,很抱歉让你做那个练习,但我敢打赌它塑造了性格。

private bool[][] GetBoolArrays()

  var q = from num in new[]  4, 3, 2, 1 
          select Combinations(4, num).Shuffle() into all
          from combinations in all
          from combination in combinations
          select combination;
  return q.ToArray();

这就像我将要做到的那样简洁。

注意这里的课程:

反复进行的小更改可能会产生大结果。 将操作提取到专门从事这些操作的方法中 使用编译器推断来减少冗余并提高可读性 使用表达式描述值的代码通常比使用语句描述操作的代码更紧凑。 拥抱抽象。请注意,当我们放弃将所有内容都放入列表的愿望时,一切都变得轻松多了。 如果您要将事物实现为列表或数组,请尽可能晚地进行。

【讨论】:

只是一个小缺陷:我找不到Random.Range 方法。似乎你的意思是Random.Sample(),它在0.01.0之间返回一个“随机”float @RenéVogt:我是从原始海报的代码中复制的。我认为他们有这样的方法。 @RenéVogt 这是一个团结的事情。为什么我添加了统一标签 不使用Enumerable.Range 来创建从4 到1 的数字数组? (我意识到字符数可能会增加,而不是减少,但它比硬编码更能代表操作的语义。)【参考方案2】:

我假设您找到了一种方法来创建一个包含您需要的所有组合的数组。我们就叫它allCombinations吧。

您可以使用以下方法创建有序数组:

bool[][] orderedCombinations = allCombinations.OrderBy(combination => combination.Count(b => b)).ToArray();

这将按包含的true 值的数量对组合进行排序。具有相同数量trues 的组合没有排序(但也没有明确随机化)。

希望这会有所帮助。


更新对于具有相同数量trues 的组合的随机化,您可以尝试以下操作:

Random rand = new Random();
bool[][] orderedCombinations = allCombinations.
    OrderBy(combination => combination.Count(b => b)).
    ThenBy(combination => rand.Next()).
    ToArray();

【讨论】:

这确实有帮助,谢谢!关于如何在保持主要顺序的同时随机化子集的任何想法? bool[][] orderedCombinations = allCombinations.select(sub=> sub.OrderBy(c => Guid.NewGuid())).OrderBy(combination => combination.Count(b => b )).ToArray(); @AwakeningByte thx,只是有同样的想法,只有ThenBy 不错的解决方案! LINQ 再次获胜。

以上是关于如何按从最少尝试到最多尝试的顺序获得一系列布尔组合?的主要内容,如果未能解决你的问题,请参考以下文章

无论它们的顺序如何,如何检查相同的值组合

获得列表中重复次数最多的名称,如果出现平局,则按字母顺序排列第一个

CodeForces 721B Passwords (水题)

Python plotly 折线图 y 轴按从 CSV 读取的顺序打印,而不是按顺序打印

具有最多和最少比较的堆排序输入

确定是不是可以通过组合单个分数来确定某个分数