优雅地确定多个布尔值是否为“true”

时间:2008-12-18 14:27:48

标签: c# hammingweight

我有一组五个布尔值。如果其中不止一个是真的,我想要执行特定的功能。您能想到的最优雅的方法是什么,这样我才能在单个if()语句中检查这种情况?目标语言是C#,但我也对其他语言的解决方案感兴趣(只要我们不讨论特定的内置函数)。

一个有趣的选择是将布尔值存储在一个字节中,执行右移并与原始字节进行比较。像if(myByte && (myByte >> 1))之类的东西但是这需要将单独的布尔值转换成一个字节(通过bitArray?)而且看起来有点(双关语)笨拙...... [编辑]对不起,那应该是< / em> if(myByte & (myByte - 1)) [/ edit]

注意:这当然非常接近经典的“人口数量”,“横向增加”或“汉明重量”编程问题 - 但不完全相同。我不需要知道有多少位被设置,只要它不止一个。我希望有一种更简单的方法来实现这一目标。

22 个答案:

答案 0 :(得分:114)

我打算写Linq版本,但是有五个人打败了我。但我真的很喜欢params方法,以避免手动新建数组。所以我认为最好的混合体是基于rp的回答用身体代替明显的Linqness:

public static int Truth(params bool[] booleans)
{
    return booleans.Count(b => b);
}

阅读和使用非常清晰:

if (Truth(m, n, o, p, q) > 2)

答案 1 :(得分:84)

怎么样

  if ((bool1? 1:0) + (bool2? 1:0) + (bool3? 1:0) + 
      (bool4? 1:0) + (bool5? 1:0) > 1)
      // do something

或一般化的方法是...

   public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools)
    {
       int trueCnt = 0;
       foreach(bool b in bools)
          if (b && (++trueCnt > threshold)) 
              return true;
       return false;          
    } 

或使用其他答案建议的LINQ:

    public bool ExceedsThreshold(int threshold, IEnumerable<bool> bools)
    { return bools.Count(b => b) > threshold; }
编辑(添加Joel Coehoorn建议: (在.Net 2.x及更高版本中)

    public void ExceedsThreshold<T>(int threshold, 
                      Action<T> action, T parameter, 
                      IEnumerable<bool> bools)
    { if (ExceedsThreshold(threshold, bools)) action(parameter); }

或.Net 3.5及更高版本:

    public void ExceedsThreshold(int threshold, 
            Action action, IEnumerable<bool> bools)
    { if (ExceedsThreshold(threshold, bools)) action(); }

或作为IEnumerable<bool>

的扩展名
  public static class IEnumerableExtensions
  {
      public static bool ExceedsThreshold<T> 
         (this IEnumerable<bool> bools, int threshold)
      { return bools.Count(b => b) > threshold; }
  }

用法将是:

  var bools = new [] {true, true, false, false, false, false, true};
  if (bools.ExceedsThreshold(3))
      // code to execute  ...

答案 2 :(得分:18)

现在是强制性LINQ答案的时候了,在这种情况下实际上非常简洁。

var bools = new[] { true, true, false, false, false };

return bools.Count(b => b == true) > 1;

答案 3 :(得分:16)

我会把它们投入到整数和总和中。

除非你处于一个超级紧密的内循环中,否则这样做的好处是易于理解。

答案 4 :(得分:6)

我会编写一个函数来接收任意数量的布尔值。它将返回那些值为true的值。检查结果中您需要积极的值的数量。

更加努力地说清楚,而不是聪明!

private int CountTrues( params bool[] booleans )
{
    int result = 0;
    foreach ( bool b in booleans )
    {
        if ( b ) result++;
    }

    return result;
}

答案 5 :(得分:5)

如果您的标记被打包成一个单词,则Michael Burr's solution将起作用。但是,循环不是必需的:

int moreThanOneBitSet( unsigned int v)
{
    return (v & (v - 1)) != 0;
}

例如

 v (binary) | v - 1 | v&(v-1) | result
------------+-------+---------+--------
       0000 |  1111 |    0000 |  false
       0001 |  0000 |    0000 |  false
       0010 |  0001 |    0000 |  false
       0011 |  0010 |    0010 |   true
       .... |  .... |    .... |   ....
       1000 |  0111 |    0000 |  false
       1001 |  1000 |    1000 |   true
       1010 |  1001 |    1000 |   true
       1011 |  1010 |    1010 |   true
       1100 |  1011 |    1000 |   true
       1101 |  1100 |    1100 |   true
       1110 |  1101 |    1100 |   true
       1111 |  1110 |    1110 |   true

答案 6 :(得分:5)

如果有数百万而不是5,你可以避免使用Count()而不是......

public static bool MoreThanOne (IEnumerable<bool> booleans)
{
    return booleans.SkipWhile(b => !b).Skip(1).Any(b => b);
}

答案 7 :(得分:4)

如果你的意思是大于或等于一个布尔值等于true,你可以像

那样做
if (bool1 || bool2 || bool3 || bool4 || bool5)

如果您需要多于一个(2及以上)布尔值等于true,您可以尝试

int counter = 0;
if (bool1) counter++;
if (bool2) counter++;
if (bool3) counter++;
if (bool4) counter++;
if (bool5) counter++;
if (counter >= 2) //More than 1 boolean is true

答案 8 :(得分:4)

比Vilx-s版更短更丑:

if (((a||b||c)&&(d||e))||((a||d)&&(b||c||e))||(b&&c)) {}

答案 9 :(得分:2)

从我的头脑中,这个具体例子的快速方法;你可以将bool转换为int(0或1)。然后循环通过therm并将它们加起来。如果结果> = 2,那么你可以执行你的功能。

答案 10 :(得分:2)

虽然我喜欢LINQ,但它有一些漏洞,就像这个问题一样。

一般情况下进行计数很好,但是当计算需要一段时间来计算/检索时,这可能会成为一个问题。

Any()扩展方法很好,如果你只是想检查任何,但如果你想要检查至少没有内置函数,它会做到这一点并且是懒惰的。

最后,如果列表中至少有一定数量的项目,我写了一个函数来返回true。

public static bool AtLeast<T>(this IEnumerable<T> source, int number)
{
    if (source == null)
        throw new ArgumentNullException("source");

    int count = 0;
    using (IEnumerator<T> data = source.GetEnumerator())
        while (count < number && data.MoveNext())
        {
            count++;
        }
    return count == number;
}

使用:

var query = bools.Where(b => b).AtLeast(2);

这样做的好处是不需要在返回结果之前评估所有项目。

[Plug]我的项目NExtension包含AtLeast,AtMost和覆盖,允许您将谓词与Atleast / Most检查混合使用。 [/插头]

答案 11 :(得分:1)

我想给出一个C ++ 11可变参数模板答案。

template< typename T>
T countBool(T v)
{
    return v;
}

template< typename T, typename... Args>
int countBool(T first, Args... args)
{
    int boolCount = 0;
    if ( first )
        boolCount++;
    boolCount += countBool( args... );
    return boolCount;
}

简单地按如下方式调用它会创建一种计算bool数量的相当优雅的方法。

if ( countBool( bool1, bool2, bool3 ) > 1 )
{
  ....
}

答案 12 :(得分:1)

我现在有一个更好的,非常短的!

bool[] bools = { b1, b2, b3, b4, b5 };
if (bools.Where(x => x).Count() > 1)
{
   //do stuff
}

答案 13 :(得分:1)

if (NumberOfTrue(new List<bool> { bool1, bool2, bool3, bool4 }) >= 2)
{
    // do stuff
}

int NumberOfTrue(IEnumerable<bool> bools)
{
    return bools.Count(b => b);
}

答案 14 :(得分:1)

不完全漂亮......但这是另一种方法:

if (
    (a && (b || c || d || e)) ||
    (b && (c || d || e)) ||
    (c && (d || e)) ||
    (d && e)
)

答案 15 :(得分:1)

我会使用params参数做这样的事情。

        public void YourFunction()
        {
            if(AtLeast2AreTrue(b1, b2, b3, b4, b5))
            {
                // do stuff
            }
        }

        private bool AtLeast2AreTrue(params bool[] values)
        {
            int trueCount = 0;
            for(int index = 0; index < values.Length || trueCount >= 2; index++)
            {
                if(values[index])
                    trueCount++;
            }

            return trueCount > 2;

        }

答案 16 :(得分:1)

投注到整数和总结应该有效,但它有点难看,在某些语言中可能无法实现。

这样的东西
int count = (bool1? 1:0) + (bool2? 1:0) + (bool3? 1:0) + (bool4? 1:0) + (bool5? 1:0);

或者如果你不关心空间,你可以预先计算真值表并使用bools作为索引:

if (morethanone[bool1][bool2][bool3][bool4][bool5]) {
 ... do something ...
}

答案 17 :(得分:0)

if((b1.CompareTo(false)+ b2.CompareTo(false)+ b3.CompareTo(false)+ ...)&gt; 1)

//其中不止一个是真的

...

否则

...

答案 18 :(得分:0)

如果您只有五个不同的值,则可以通过将位打包为short或int并检查它是否为0或1位答案中的任何一个来轻松地进行测试。您可以获得的唯一无效数字是..

0x 0000 0000 
0x 0000 0001
0x 0000 0010
0x 0000 0100
0x 0000 1000
0x 0001 0000

这为您提供了六个值来搜索,将它们放在查找表中,如果它不在那里,您就得到了答案。

这给你一个简单的答案。

   public static boolean moreThan1BitSet(int b)
   {
      final short multiBitLookup[] = { 
            1, 1, 1, 0, 1, 0, 0, 0,
            1, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0,
            1, 0, 0, 0, 0, 0, 0, 0,
            0, 0, 0, 0, 0, 0, 0, 0
      };
      if(multiBitLookup[b] == 1)
         return false;
      return true;
   }

这不能很好地扩展到8位,但你只有5位。

答案 19 :(得分:0)

你提到了

  

一个有趣的选择是将布尔值存储在一个字节中,   做一个右移并与原始字节进行比较。   像if (myByte && (myByte >> 1))

这样的东西

我不认为该表达式会给你想要的结果(至少使用C语义,因为表达式无效C#):

如果(myByte == 0x08),则表达式将返回true,即使只设置了一位。

如果您的意思是“if (myByte & (myByte >> 1))”,那么即使设置了2位,(myByte == 0x0a)表达式也会返回false。

但是这里有一些计算单词中位数的技巧:

Bit Twiddling Hacks - Counting bits

您可能会考虑使用Kernighan的计数方法,但要提前纾困,因为您只需知道是否设置了多个位:

int moreThanOneBitSet( unsigned int v)
{
    unsigned int c; // c accumulates the total bits set in v

    for (c = 0; v && (c <= 1); c++)
    {
      v &= v - 1; // clear the least significant bit set
    }

    return (c > 1);
}

当然,使用查找表也不错。

答案 20 :(得分:0)

在大多数语言中,true等于非零值,而false为零。我没有确切的语法,但在伪代码中,如何:

if ((bool1 * 1) + (bool2 * 1) + (bool3 * 1) > 2)
{
    //statements here
}

答案 21 :(得分:-1)

我最近遇到了同样的问题,我有三个布尔值,我需要检查其中只有一个是真的。为此,我使用了xor运算符,如下所示:

bool a = true;
bool b = true;
bool c = false;

if (a || b || c)
{
    if (a ^ b ^ c){
        //Throw Error
    }
}

此代码将抛出错误,因为a和b都是true。

供参考:http://www.dotnetperls.com/xor

我刚刚在C#中找到了xor运算符,如果有人知道这个策略的任何陷阱,请告诉我。