当前位置:  开发笔记 > 编程语言 > 正文

检查三个布尔值中是否至少有两个是真的

如何解决《检查三个布尔值中是否至少有两个是真的》经验,为你挑选了23个好方法。

一位采访者最近问我这个问题:给定三个布尔变量a,b和c,如果三个中至少有两个为真,则返回true.

我的解决方案是:

boolean atLeastTwo(boolean a, boolean b, boolean c) {
    if ((a && b) || (b && c) || (a && c)) {
        return true;
    }
    else{
        return false;
    }
}

他说这可以进一步改善,但如何?



1> polygenelubr..:

而不是写:

if (someExpression) {
    return true;
} else {
    return false;
}

写:

return someExpression;

至于表达本身,这样的事情:

boolean atLeastTwo(boolean a, boolean b, boolean c) {
    return a ? (b || c) : (b && c);
}

或者这个(无论你发现哪个更容易掌握):

boolean atLeastTwo(boolean a, boolean b, boolean c) {
    return a && (b || c) || (b && c);
}

它测试ab准确一次,c最多一次.

参考

JLS 15.25条件运算符?:


+1:这个谜题的可爱解决方案,但希望我们在现实世界中看不到这样的东西:)
@Juliet:我不知道,我认为如果这是一个真实世界的要求(使用真正的变量名称),它会读得很好.考虑一下`return hasGoodAttendance?(passCoursework ||通过考试):( passCoursework && passExam)`,这对我来说很好看.
我不认为这看起来很糟糕*,但如果域中的要求被理解为"至少两个",我认为更容易阅读`atLeastTwo(hasgoodAttendance,passCoursework,passingExam)`."至少2个bool是真的"这个概念足够通用,值得拥有自己的功能.
@Lese:在面对面访谈中询问最微优化的代码是不切实际的,我敢说,没用.当需求驱动时,微优化由运行时分析结果引导,而不是人类本能(已知是非常可怕的).你当然可以向受访者询问你进一步优化这一过程的过程; 这比结果本身更重要.
三元运算符是您应该能够阅读的常用习语.如果你不能阅读它,你应该研究它,直到你可以.在贬义的意义上,使用三元运算符并不是我认为"聪明"的东西.但是,如果您通常使用"至少两个"逻辑,我会把它作为方法调用的主体.
我喜欢其他所有答案都试图优化,而这显然不是采访者想要听到的.
太优雅了.我必须再欣赏美丽了一会儿.我会坐在这里晒太阳,直到老板过来.

2> Tim Stone..:

只是为了使用XOR来回答一个相对直接的问题......

return a ^ b ? c : a


哇,很酷的解决方案.但对我来说,它的倒置版本更容易理解:a == b?a:c
@ Stimul8d可能是因为,对于布尔,它与!=相同但可读性较差?弄清楚这对我来说是一个尤里卡时刻......
我比接受的答案更喜欢这个.
a ^ b?c:a ^ b?c:a ^ b?c:a
是啊,XOR得到了如此糟糕的新闻,你很少有机会使用它.
我更喜欢纯二进制形式:return((a ^ b)&c)| (a&b).它是无分支(更快)且易于阅读:( a或b为真,c为真)或(a和b均为真).请注意,(a | b)和(a ^ b)都适用于此公式.

3> Rotsor..:

为什么不按字面意思实现呢?:)

(a?1:0)+(b?1:0)+(c?1:0) >= 2

在C中你可以写a+b+c >= 2(或者!!a+!!b+!!c >= 2非常安全).

为了回应TofuBeer对java字节码的比较,这里有一个简单的性能测试:

class Main
{
    static boolean majorityDEAD(boolean a,boolean b,boolean c)
    {
        return a;
    }

    static boolean majority1(boolean a,boolean b,boolean c)
    {
        return a&&b || b&&c || a&&c;
    }

    static boolean majority2(boolean a,boolean b,boolean c)
    {
        return a ? b||c : b&&c;
    }

    static boolean majority3(boolean a,boolean b,boolean c)
    {
        return a&b | b&c | c&a;
    }

    static boolean majority4(boolean a,boolean b,boolean c)
    {
        return (a?1:0)+(b?1:0)+(c?1:0) >= 2;
    }

    static int loop1(boolean[] data, int i, int sz1, int sz2)
    {
        int sum = 0;
        for(int j=i;j 0;
        long t0,t1,t2,t3,t4,tDEAD;
        int sz1 = 100;
        int sz2 = 100;
        int sum = 0;

        t0 = System.currentTimeMillis();

        for(int i=0;i= 2    : " + (t4-t3) + " ms");
        System.out.println("       DEAD          : " + (tDEAD-t4) + " ms");
        System.out.println("sum: "+sum);
    }

    public static void main(String[] args) throws InterruptedException
    {
        while(true)
        {
            work();
            Thread.sleep(1000);
        }
    }
}

这将在我的机器上打印以下内容(使用HotSpot Server VM(14.1-b02,混合模式)在Intel Core 2 + sun java 1.6.0_15-b03上运行Ubuntu):

第一次和第二次迭代:

a&&b || b&&c || a&&c : 1740 ms
   a ? b||c : b&&c   : 1690 ms
   a&b | b&c | c&a   : 835 ms
   a + b + c >= 2    : 348 ms
       DEAD          : 169 ms
sum: 1472612418

后来的迭代:

a&&b || b&&c || a&&c : 1638 ms
   a ? b||c : b&&c   : 1612 ms
   a&b | b&c | c&a   : 779 ms
   a + b + c >= 2    : 905 ms
       DEAD          : 221 ms

我想知道,对于(a + b + c> = 2)情况,java VM可以做什么会降低性能随时间的变化.

如果我使用-clientVM开关运行java会发生什么:

a&&b || b&&c || a&&c : 4034 ms
   a ? b||c : b&&c   : 2215 ms
   a&b | b&c | c&a   : 1347 ms
   a + b + c >= 2    : 6589 ms
       DEAD          : 1016 ms

神秘...

如果我在GNU Java Interpreter中运行它,它会慢几百倍,但a&&b || b&&c || a&&c版本会胜出.

使用运行OS X的最新代码从Tofubeer获得的结果:

a&&b || b&&c || a&&c : 1358 ms
   a ? b||c : b&&c   : 1187 ms
   a&b | b&c | c&a   : 410 ms
   a + b + c >= 2    : 602 ms
       DEAD          : 161 ms

来自Paul Wagland的Mac Java 1.6.0_26-b03-383-11A511的结果

a&&b || b&&c || a&&c : 394 ms 
   a ? b||c : b&&c   : 435 ms
   a&b | b&c | c&a   : 420 ms
   a + b + c >= 2    : 640 ms
   a ^ b ? c : a     : 571 ms
   a != b ? c : a    : 487 ms
       DEAD          : 170 ms


-1.你不应该为C做那个.你不知道true的值是什么(它可以很容易地为-1).实际上我猜C99在其标准中包括true被定义为1.但是我仍然不会这样做.
对微基准测试要谨慎:http://java.sun.com/docs/hotspot/HotSpotFAQ.html#benchmarking_simple
`a + b + c> = 2`:这不适用于否定,对吗?你可能不得不做`!! a`的事情,我不确定.
@Rotsor:没有人说输入必须是布尔运算的结果.即使没有否定,你也会玩火,就好像你把它定义为2你的条件会有误报.但我并不关心这一点,因为我不喜欢将布尔混合成算术的想法.您的Java解决方案很明确,因为它不依赖于从布尔到整数类型的细微转换.

4> Jack..:

这种问题可以通过卡诺图来解决:

      | C | !C
------|---|----
 A  B | 1 | 1 
 A !B | 1 | 0
!A !B | 0 | 0
!A  B | 1 | 0

从中推断您需要第一行的组和第一列的两个组,从而获得多基因润滑剂的最佳解决方案:

(C && (A || B)) || (A && B)  <---- first row
       ^
       |
   first column without third case


+1新的东西.我的下一个功能规格将包括K-map,无论是否需要它.
@Justin,卡诺图将逻辑运算的数量从3个AND和2个OR减少到2个AND和2个OR.@Jack,谢谢你提醒我卡诺图的存在.
也许差的可读性可以通过(1)评论中的适当表格和(2)适当的单元测试来补偿......对于在学校学到的有用的东西+1.

5> danatel..:

可读性应该是目标.阅读代码的人必须立即理解您的意图.所以这是我的解决方案.

int howManyBooleansAreTrue =
      (a ? 1 : 0)
    + (b ? 1 : 0)
    + (c ? 1 : 0);

return howManyBooleansAreTrue >= 2;


嗯,现在我需要一个"两个四个布尔"版本... danatel的版本现在更容易*.
我同意这个前提,但是(a && b)|| (b && c)|| (a && c)比你的解决方案恕我直言更易读.
或者在Scala中:`Seq(true,true,false).map(if(_)1 else 0).sum> = 2`
@retronym:嗯,没有.Java方法在Scala中运行得很好,并且更具可读性和更高效.
这具有可读性并且非常容易更改.会聘请.

6> 小智..:
return (a==b) ? a : c;

说明:

如果a==b,那么两者都是真的或两者都是假的.如果两者都是真的,我们找到了两个真正的布尔值,并且可以返回true(通过返回a).如果两者都是假的,即使c是真的也不会有两个真正的布尔值,所以我们返回false(通过返回a).那是(a==b) ? a一部分.怎么样: c?好吧,如果a==b是假的,那么恰好是一个ab必须是真的,所以我们找到了第一个真正的布尔值,唯一重要的是if c也是真的,所以我们返回c作为答案.


c甚至从未测试过......太棒了!
+1虽然不完全透明但优雅.
太优雅了!我不得不用笔和纸来相信它:)先生,谢谢!
我认为这是"如果`a`和`b`同意,他们有多数投票,所以不管它是什么,否则,他们不同意,所以'c`是决定性投票"

7> moonshadow..:

您不需要使用运算符的短路形式.

return (a & b) | (b & c) | (c & a);

这将执行与您的版本相同数量的逻辑操作,但是完全无分支.


@Mark - 它可能更快......取决于不正确的分支预测对CPU管道的影响.但是,最好将这些微优化保留给JIT编译器.
当1可以做时,你为什么要强制进行5次评估?它实际上并没有执行相同数量的逻辑运算.事实上,它总是**表现更多.
@Peter Tillemans没有混合二元运算符,在Java中这些是布尔运算符.
用Java(或任何其他语言)做这样的事情是很好的......有几点需要注意:1)它需要更快(在这种情况下,我相信它是,见我的第二个答案)2)更好显着更快(不确定是否),3)最重要的记录,因为它是"奇怪的".只要它有用,并且有记录,在有意义的情况下"违反规则"是可以的.
我认为混合二进制算术和布尔算法是一个坏主意.就像用扳手在墙上驱动螺丝一样.最糟糕的是它们具有不同的语义.

8> Carl Manaste..:

这是一种以测试为导向的通用方法.并不像迄今为止提供的大多数解决方案那样"高效",而是清晰,经过测试,工作和推广.

public class CountBooleansTest extends TestCase {
    public void testThreeFalse() throws Exception {
        assertFalse(atLeastTwoOutOfThree(false, false, false));
    }

    public void testThreeTrue() throws Exception {
        assertTrue(atLeastTwoOutOfThree(true, true, true));
    }

    public void testOnes() throws Exception {
        assertFalse(atLeastTwoOutOfThree(true, false, false));
        assertFalse(atLeastTwoOutOfThree(false, true, false));
        assertFalse(atLeastTwoOutOfThree(false, false, true));
    }

    public void testTwos() throws Exception {
        assertTrue(atLeastTwoOutOfThree(false, true, true));
        assertTrue(atLeastTwoOutOfThree(true, false, true));
        assertTrue(atLeastTwoOutOfThree(true, true, false));
    }

    private static boolean atLeastTwoOutOfThree(boolean b, boolean c, boolean d) {
        return countBooleans(b, c, d) >= 2;
    }

    private static int countBooleans(boolean... bs) {
        int count = 0;
        for (boolean b : bs)
            if (b)
                count++;
        return count;
    }
}


由于种种原因,我个人认为这段代码很糟糕.我不打算投票,但如果我在生产代码中看到这个,我会诅咒.一个非常简单的布尔操作不需要像这样复杂.
我很想知道你的理由,@ CaptainCasey.我认为这是非常好的代码.有一个很好的通用功能,易于理解,易于验证,并具有利用它的特定功能,也易于理解和验证.在现实世界中,我会将它们公之于众,并将它们放在另一个类中; 除此之外 - 我很乐意将这些代码投入生产.哦 - 是的 - 我将countBooleans()重命名为countTrue().
哇,在看到这个之前我从来没有见过**完全**测试过的方法.
什么鬼,人呢?这是一个清晰且经过良好测试的代码,它看起来很多的唯一原因是因为它包含了测试.A +++会再次上升.
如果不是性能,这个解决方案对我来说几乎是完美的:非常容易阅读和扩展.这正是var-args的制作方式.
@Carl我的最后一句话有点不清楚吗?我不在乎最后一次机器指令.写半页经文代替一个简单的表达并不清晰,这是混淆.如果您能想到证明正确性的唯一方法是绝对疲惫,那么您如何做任何事情?
@walkytalky我宁愿在正确性,清晰度和普遍性方面进行精神病性的宗教顽强,而不是削减最后一次机器指令; 我认为使用今天的平台有更大的价值.因人而异.
@Carl:我会说这段代码过于概括了.来自其他答案的大多数单方法解决方案同样易于理解和验证.因此,除非您在代码中的其他地方使用`countBooleans`,否则没有理由有两种方法可以使用.

9> 小智..:

总结一下.它被称为布尔代数,原因如下:

  0 x 0 = 0
  1 x 0 = 0
  1 x 1 = 1

  0 + 0 = 0
  1 + 0 = 1
  1 + 1 = 0 (+ carry)

如果你看一下那里的真值表,你可以看到乘法是布尔值,而且只是加法是xor.

回答你的问题:

return (a + b + c) >= 2


除了帖子上的标签上写着"Java",当你在Java中定义为布尔值时,你不能写"a + b + c".
但是新手错误,布尔值不是0,这并不意味着它总是1.
在我看来,这是最优雅的解决方案.

10> 小智..:
boolean atLeastTwo(boolean a, boolean b, boolean c) 
{
  return ((a && b) || (b && c) || (a && c));
}



11> kerkeslager..:

这真的取决于你所说的"改进":

更清晰?

boolean twoOrMoreAreTrue(boolean a, boolean b, boolean c)
{
    return (a && b) || (a && c) || (b && c);
}

更简洁?

boolean moreThanTwo(boolean a, boolean b, boolean c)
{
    return a == b ? a : c;
}

更一般?

boolean moreThanXTrue(int x, boolean[] bs)
{
    int count = 0;

    for(boolean b : bs)
    {
        count += b ? 1 : 0;

        if(count > x) return true;
    }

    return false;
}

更具可扩展性

boolean moreThanXTrue(int x, boolean[] bs)
{
    int count = 0;

    for(int i < 0; i < bs.length; i++)
    {
        count += bs[i] ? 1 : 0;

        if(count > x) return true;

        int needed = x - count;
        int remaining = bs.length - i;

        if(needed >= remaining) return false;
    }

    return false;
}

快点?

// Only profiling can answer this.

哪一个"改进"在很大程度上取决于具体情况.



12> Anurag..:

这是使用map/reduce的另一个实现.这很好地进行扩展十亿布尔值©在分布式环境中.使用MongoDB:

创建values布尔数据库:

db.values.insert({value: true});
db.values.insert({value: false});
db.values.insert({value: true});

创建地图,减少功能:

编辑:我喜欢CurtainDog 关于将map/reduce应用于通用列表的答案,所以这里有一个map函数,它接受一个回调来确定是否应该计算一个值.

var mapper = function(shouldInclude) {
    return function() {
        emit(null, shouldInclude(this) ? 1 : 0);
    };
}

var reducer = function(key, values) {
    var sum = 0;
    for(var i = 0; i < values.length; i++) {
        sum += values[i];
    }
    return sum;
}

运行map/reduce:

var result = db.values.mapReduce(mapper(isTrue), reducer).result;

containsMinimum(2, result); // true
containsMinimum(1, result); // false


function isTrue(object) {
    return object.value == true;
}

function containsMinimum(count, resultDoc) {
    var record = db[resultDoc].find().next();
    return record.value >= count;
}


@Andrew - nah花钱,版权很容易

13> TofuBeer..:

在这里得到答案(到目前为止):

public class X
{
    static boolean a(final boolean a, final boolean b, final boolean c)
    {
    return ((a && b) || (b && c) || (a && c));
    }

    static boolean b(final boolean a, final boolean b, final boolean c)
    {
    return a ? (b || c) : (b && c);
    }

    static boolean c(final boolean a, final boolean b, final boolean c)
    {
    return ((a & b) | (b & c) | (c & a));
    }

    static boolean d(final boolean a, final boolean b, final boolean c)
    {
    return ((a?1:0)+(b?1:0)+(c?1:0) >= 2);
    }
}

并通过反编译器运行它们(javap -c X> results.txt):

Compiled from "X.java"
public class X extends java.lang.Object{
public X();
  Code:
   0:   aload_0
   1:   invokespecial   #1; //Method java/lang/Object."":()V
   4:   return

static boolean a(boolean, boolean, boolean);
  Code:
   0:   iload_0
   1:   ifeq    8
   4:   iload_1
   5:   ifne    24
   8:   iload_1
   9:   ifeq    16
   12:  iload_2
   13:  ifne    24
   16:  iload_0
   17:  ifeq    28
   20:  iload_2
   21:  ifeq    28
   24:  iconst_1
   25:  goto    29
   28:  iconst_0
   29:  ireturn

static boolean b(boolean, boolean, boolean);
  Code:
   0:   iload_0
   1:   ifeq    20
   4:   iload_1
   5:   ifne    12
   8:   iload_2
   9:   ifeq    16
   12:  iconst_1
   13:  goto    33
   16:  iconst_0
   17:  goto    33
   20:  iload_1
   21:  ifeq    32
   24:  iload_2
   25:  ifeq    32
   28:  iconst_1
   29:  goto    33
   32:  iconst_0
   33:  ireturn

static boolean c(boolean, boolean, boolean);
  Code:
   0:   iload_0
   1:   iload_1
   2:   iand
   3:   iload_1
   4:   iload_2
   5:   iand
   6:   ior
   7:   iload_2
   8:   iload_0
   9:   iand
   10:  ior
   11:  ireturn

static boolean d(boolean, boolean, boolean);
  Code:
   0:   iload_0
   1:   ifeq    8
   4:   iconst_1
   5:   goto    9
   8:   iconst_0
   9:   iload_1
   10:  ifeq    17
   13:  iconst_1
   14:  goto    18
   17:  iconst_0
   18:  iadd
   19:  iload_2
   20:  ifeq    27
   23:  iconst_1
   24:  goto    28
   27:  iconst_0
   28:  iadd
   29:  iconst_2
   30:  if_icmplt   37
   33:  iconst_1
   34:  goto    38
   37:  iconst_0
   38:  ireturn
}

您可以看到?:1比原始版本的固定版本略好一些.最好的是完全避免分支的那个.从较少指令(在大多数情况下)的观点来看,这是好的,并且对于CPU的分支预测部分更好,因为分支预测中的错误猜测可能导致CPU停滞.

我认为最有效的一个是来自moonshadow整体的那个.它平均使用最少的指令,并减少CPU中流水线停顿的可能性.

要100%确定你需要找出每条指令的成本(以CPU周期为单位),遗憾的是,这些成本并不容易获得(你必须查看热点的来源,然后查看CPU供应商的规格)为每个生成的指令采取).

请参阅Rotsor的更新答案,以获取代码的运行时分析.


你只看字节码.如您所知,JIT将采用字节码中带分支的版本,并将其转换为本机代码中没有分支的版本.但人们倾向于认为字节码中较少的分支会更好.

14> David R Trib..:

直接代码的另一个例子:

int  n = 0;
if (a) n++;
if (b) n++;
if (c) n++;
return (n >= 2);

显然,这不是最简洁的代码.

附录

另一个(稍微优化)的版本:

int  n = -2;
if (a) n++;
if (b) n++;
if (c) n++;
return (n >= 0);

这可能会稍快一些,假设与0的比较将比使用2的比较使用更快(或可能更少)的代码.



15> TofuBeer..:

最明显的改进是:

// There is no point in an else if you already returned.
boolean atLeastTwo(boolean a, boolean b, boolean c) {
    if ((a && b) || (b && c) || (a && c)) {
        return true;
    }
    return false;
}

然后

// There is no point in an if(true) return true otherwise return false.
boolean atLeastTwo(boolean a, boolean b, boolean c) {
    return ((a && b) || (b && c) || (a && c));
}

但这些改进很小.



16> barrowc..:

另一种方法是做到这一点,但不是一个非常好的方式:

return (Boolean.valueOf(a).hashCode() + Boolean.valueOf(b).hashCode() + Boolean.valueOf(c).hashCode()) < 3705);

Boolean哈希码值是固定的,在1231真正和1237为false,所以可以同样使用了<= 3699



17> Roman A. Tay..:

我不喜欢三元(return a ? (b || c) : (b && c);从最佳答案),我不认为我见过有人提过它.它是这样写的:

boolean atLeastTwo(boolean a, boolean b, boolean c) {
    if (a) {
        return b||c;
    } 
    else {
        return b&&C;
    }



18> Vagif Verdi..:

在Clojure中:

(defn at-least [n & bools]
  (>= (count (filter true? bools)) n)

用法:

(at-least 2 true false true)


+1伟大的通用版本显示了Lisps的强大功能.谢谢,

19> Joe Enos..:

我认为我还没有看到这个解决方案:

boolean atLeast(int howMany, boolean[] boolValues) {
  // check params for valid values

  int counter = 0;
  for (boolean b : boolValues) {
    if (b) {
      counter++;

      if (counter == howMany) {
        return true;
      }
    }
  }
  return false;
}

它的优点是,一旦它达到你正在寻找的数量,它就会中断.因此,如果这是"这1,000,000个值中至少有2个是真的",前两个实际上是真的,那么它应该比一些更"正常"的解决方案更快.


甚至更短:if(b &&(++ counter == howMany))

20> vine'th..:

我们可以将bools转换为整数并执行这个简单的检查:

(int(a) + int(b) + int(c)) >= 2



21> 小智..:

由于没有说明代码应该如何改进,我将努力通过使代码更有趣来改进代码.这是我的解决方案:

boolean atLeastTwo(boolean t, boolean f, boolean True) {
    boolean False = True;
    if ((t || f) && (True || False)) 
        return "answer" != "42";
    if (t && f) 
        return !"France".contains("Paris");
    if (False == True) 
        return true == false;
    return Math.random() > 0.5;
}

如果有人想知道这段代码是否有效,这里是使用相同逻辑的简化:

boolean atLeastTwo(boolean a, boolean b, boolean c) {
    if ((a || b) && (c)) 
        return true;
    if (a && b) 
        return true;
    if (true) 
        return false;
    // The last line is a red herring, as it will never be reached:
    return Math.random() > 0.5; 

}

这可以进一步归结为以下内容:

return ((a || b) && (c)) || (a && b);

但现在不再有趣了.



22> 小智..:
Function ReturnTrueIfTwoIsTrue(bool val1, val2, val3))
{
     return (System.Convert.ToInt16(val1) +
             System.Convert.ToInt16(val2) +
             System.Convert.ToInt16(val3)) > 1;
}

有太多方法可以做到这一点......


看起来更像C#.这应该在答案中提到,因为问题是针对Java的:)

23> 小智..:

AC解决方案.

int two(int a, int b, int c) {
  return !a + !b + !c < 2;
}

或者您可能更喜欢:

int two(int a, int b, int c) {
  return !!a + !!b + !!c >= 2;
}

推荐阅读
小妖694_807
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有