检查三个布尔中至少两个是否为真


579

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

我的解决方案如下:

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

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


170
内联return语句。
Finglas 2010年

45
听起来像是“谁拥有最高的智商”采访。我会失败的。
克里斯·杜特罗

79
atLeastTwo(iWantYou, iNeedYou, imEverGonnaLoveYou)
安德鲁·格林

92
人们为什么反对最琐碎的问题?
BlueRaja-Danny Pflughoeft

46
普遍且易于理解的问题获得了很多好评。非常具体和技术性的问题没有。
杰伊(Jay)

Answers:


820

而不是写:

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最多一次。

参考文献


144
+1:解决难题的方法很不错,但希望我们在现实世界中看不到这样的东西:)
朱丽叶2010年

124
@Juliet:我不知道,我想如果这是一个现实世界的要求(带有真实的变量名),它会读得很好。考虑一下return hasGoodAttendance ? (passedCoursework || passed Exam) : (passedCoursework && passedExam),对我来说很好。
Andrzej Doyle 2010年

18
我认为这看起来不错,但是如果将领域中的要求理解为“至少两个”,那么阅读起来会更容易atLeastTwo(hasgoodAttendance, passedCoursework, passedExam)。“至少有2个布尔值是正确的”的想法足够通用,足以发挥其应有的功能。

17
@Lese:在面对面采访中询问最优化的代码是不切实际的,而且我敢说这没用。微型优化在需要时由运行时性能分析结果而不是人的直觉(众所周知的直觉)指导。您当然可以向受访者询问进一步优化此过程的过程;这比结果本身更重要。
polygenelubricants 2010年

17
三元运算符是您应该能够阅读的常见习语。如果您看不懂它,则应该研究它,直到可以为止。在贬义的意义上,三元运算符的使用不是我认为的“聪明”。但是,是的,如果您通常使用“至少两个”逻辑,则将其作为方法调用的主体。
斯蒂芬·P

492

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

return a ^ b ? c : a

158
哇,很酷的解决方案。但对我来说,其倒置版本更容易理解:a == b?a:c
Rotsor

5
a ^ b?c:a ^ b?c:a ^ b?c:a
alexanderpas

4
是的,.. XOR的新闻报道如此糟糕,您很少有机会使用它。
八十一联合

18
@ Stimul8d可能是因为,对于布尔值,它与!=相同,但可读性较差?弄清楚这对我来说是一个尤里卡的时刻...
Tikhon Jelvis

2
我更喜欢纯二进制形式:return((a ^ b)&c)| (a&b)。它是无分支的(更快)并且易于阅读:(a或b为真,c为真)或(a和b均为真)。请注意,(a | b)和(a ^ b)都适用于此公式。
flanglet 2012年

217

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

(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<i+sz1;j++)
        {
            for(int k=j;k<j+sz2;k++)
            {
                sum += majority1(data[i], data[j], data[k])?1:0; 
                sum += majority1(data[i], data[k], data[j])?1:0; 
                sum += majority1(data[j], data[k], data[i])?1:0; 
                sum += majority1(data[j], data[i], data[k])?1:0; 
                sum += majority1(data[k], data[i], data[j])?1:0; 
                sum += majority1(data[k], data[j], data[i])?1:0; 
            }
        }
        return sum;
    }

    static int loop2(boolean[] data, int i, int sz1, int sz2)
    {
        int sum = 0;
        for(int j=i;j<i+sz1;j++)
        {
            for(int k=j;k<j+sz2;k++)
            {
                sum += majority2(data[i], data[j], data[k])?1:0; 
                sum += majority2(data[i], data[k], data[j])?1:0; 
                sum += majority2(data[j], data[k], data[i])?1:0; 
                sum += majority2(data[j], data[i], data[k])?1:0; 
                sum += majority2(data[k], data[i], data[j])?1:0; 
                sum += majority2(data[k], data[j], data[i])?1:0; 
            }
        }
        return sum;
    }

    static int loop3(boolean[] data, int i, int sz1, int sz2)
    {
        int sum = 0;
        for(int j=i;j<i+sz1;j++)
        {
            for(int k=j;k<j+sz2;k++)
            {
                sum += majority3(data[i], data[j], data[k])?1:0; 
                sum += majority3(data[i], data[k], data[j])?1:0; 
                sum += majority3(data[j], data[k], data[i])?1:0; 
                sum += majority3(data[j], data[i], data[k])?1:0; 
                sum += majority3(data[k], data[i], data[j])?1:0; 
                sum += majority3(data[k], data[j], data[i])?1:0; 
            }
        }
        return sum;
    }

    static int loop4(boolean[] data, int i, int sz1, int sz2)
    {
        int sum = 0;
        for(int j=i;j<i+sz1;j++)
        {
            for(int k=j;k<j+sz2;k++)
            {
                sum += majority4(data[i], data[j], data[k])?1:0; 
                sum += majority4(data[i], data[k], data[j])?1:0; 
                sum += majority4(data[j], data[k], data[i])?1:0; 
                sum += majority4(data[j], data[i], data[k])?1:0; 
                sum += majority4(data[k], data[i], data[j])?1:0; 
                sum += majority4(data[k], data[j], data[i])?1:0; 
            }
        }
        return sum;
    }

    static int loopDEAD(boolean[] data, int i, int sz1, int sz2)
    {
        int sum = 0;
        for(int j=i;j<i+sz1;j++)
        {
            for(int k=j;k<j+sz2;k++)
            {
                sum += majorityDEAD(data[i], data[j], data[k])?1:0; 
                sum += majorityDEAD(data[i], data[k], data[j])?1:0; 
                sum += majorityDEAD(data[j], data[k], data[i])?1:0; 
                sum += majorityDEAD(data[j], data[i], data[k])?1:0; 
                sum += majorityDEAD(data[k], data[i], data[j])?1:0; 
                sum += majorityDEAD(data[k], data[j], data[i])?1:0; 
            }
        }
        return sum;
    }

    static void work()
    {
        boolean [] data = new boolean [10000];
        java.util.Random r = new java.util.Random(0);
        for(int i=0;i<data.length;i++)
            data[i] = r.nextInt(2) > 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<data.length-sz1-sz2;i++)
            sum += loop1(data, i, sz1, sz2);

        t1 = System.currentTimeMillis();

        for(int i=0;i<data.length-sz1-sz2;i++)
            sum += loop2(data, i, sz1, sz2);

        t2 = System.currentTimeMillis();

        for(int i=0;i<data.length-sz1-sz2;i++)
            sum += loop3(data, i, sz1, sz2);

        t3 = System.currentTimeMillis();

        for(int i=0;i<data.length-sz1-sz2;i++)
            sum += loop4(data, i, sz1, sz2);

        t4 = System.currentTimeMillis();

        for(int i=0;i<data.length-sz1-sz2;i++)
            sum += loopDEAD(data, i, sz1, sz2);

        tDEAD = System.currentTimeMillis();

        System.out.println("a&&b || b&&c || a&&c : " + (t1-t0) + " ms");
        System.out.println("   a ? b||c : b&&c   : " + (t2-t1) + " ms");
        System.out.println("   a&b | b&c | c&a   : " + (t3-t2) + " ms");
        System.out.println("   a + b + c >= 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

我想知道,java VM会如何降低性能在(a + b + c> = 2)的情况下随着时间的推移性能?

如果我使用-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中运行它,它的速度几乎慢了100倍,但最终a&&b || b&&c || a&&c版本胜出了。

来自Tofubeer的结果以及运行OS X的最新代码:

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

4
a+b+c >= 2:这不适用于底片,对吗?!!a我不确定,您可能必须做这件事。
polygenelubricants 2010年

8
<s> -1。您永远都不要为C这么做。您不知道true的值是什么(它很容易为-1)。</ s>实际上,我猜C99在其标准中包括将true定义为1。我仍然不会这样做。
马克·彼得斯2010年

1
如果您的输入是布尔运算的结果,那可能吗?C ++中的“布尔”类型可能吗?
Rotsor 2010年

2
@Rotsor:没有人说输入必须是布尔运算的结果。即使没有负数,您也要玩火,就好像您将其定义为2一样,您的情况也会有误报。但是我对此并不关心,因为我不喜欢将布尔值混合到算术中的想法。您的Java解决方案很明显,因为它不依赖于从布尔值到整数类型的细微转换。
Mark Peters 2010年


143

此类问题可以通过卡诺地图来解决:

      | 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

10
@Justin,卡诺图将逻辑运算的数量从3个AND和2个OR减少到2个AND和2个OR。@杰克,感谢您让我想起卡诺地图的存在。
塔西(Tachy)2010年

14
为新事物+1。我的下一个功能规格将包括K地图,无论它是否需要它。
贾斯汀·R.2010年

2
可读性差可以通过(1)注释中的适当表格和(2)适当的单元测试... +1来弥补在学校学到的有用知识。
moala 2012年

140

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

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

return howManyBooleansAreTrue >= 2;

21
我同意这个前提,但是(a && b)|| (b && c)|| (a && c)比您的解决方案恕我直言更具可读性。
阿德里安·格里戈里

62
嗯,现在我需要一个“四个布尔中的两个”版本... danatel的版本现在容易得多
Arafangion 2010年

6
或在Scala中:Seq(true, true, false).map(if (_) 1 else 0).sum >= 2
回顾

5
@retronym:嗯,不。Java方式在Scala中工作得很好,并且更具可读性和效率。
Seen Osewa 2010年

134
return (a==b) ? a : c;

说明:

如果为a==b,则两者均为true或均为false。如果两者都为true,则我们找到了两个true布尔值,并且可以返回true(通过返回a)。如果两个都是假,即使c是true 也不能有两个true布尔值,因此我们返回false(返回a)。那是(a==b) ? a一部分。那: c呢 好吧,如果a==b为假,则恰好为ab必须为真,因此我们找到了第一个真布尔值,剩下的唯一重要的事情就是如果c也为真,因此我们c作为答案返回。


8
c从来没有经过测试...太棒了!
CurtainDog 2010年

使用平等的传递关系和布尔值是对还是错+1的事实
Christophe Roussy

3
真优雅!我不得不用笔和纸检查一下才能相信:)先生,您好!
阿德里安

3
我想关于这个问题,“如果ab同意,他们拥有多数投票,所以去不管它是什么,否则,他们不同意,所以c是决定性的一票”
本·米尔伍德

34

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

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

它执行与您的版本相同数量的逻辑运算,但是完全没有分支。


11
为什么要在1次评估时就强制进行5次评估?实际上,它实际上并没有执行相同数量的逻辑运算。实际上,它总是会执行得更多。
马克·彼得斯2010年

2
我认为混合二进制算术和布尔算术是一个坏主意。就像用扳手将螺丝钉拧在墙上。最糟糕的是它们具有不同的语义。
Peter Tillemans 2010年

12
@Mark-可能更快...取决于错误分支预测对CPU管道的影响。但是,最好将此类微优化留给JIT编译器。
Stephen C 2010年

4
可以用Java(或任何其他语言)做类似的事情...有几点警告:1)它需要更快(在这种情况下,我相信是,请参阅我的第二个答案)2)更好3)最重要的是记录在案,因为它是“奇数”。只要它能达到目的并被记录下来,就可以在合理的情况下“违反规则”。
TofuBeer

11
@Peter Tillemans没有将二进制运算符混合在一起,在Java中,它们是布尔运算符。
starblue 2010年

27

这是一种测试驱动的通用方法。到目前为止,还没有大多数解决方案提供的“效率”高,而是清晰,经过测试,可以正常工作和通用化。

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;
    }
}

8
哇,我从未见过经过全面测试的方法。
Rotsor 2010年

51
由于许多原因,我个人认为这段代码很糟糕。我不会拒绝投票,但是如果我在生产代码中看到了这个,我会骂。极其简单的布尔运算不需要像这样复杂。
CapeyCasey 2010年

10
我很想知道您的原因,@ CaptainCasey。我认为这是相当不错的代码。有一个很好的通用函数,它易于理解,易于验证,并且利用它的特定功能也易于理解和验证。在现实世界中,我会将它们公开,并放在另一个班级中。除此之外-我很乐意将此代码投入生产。哦-是的-我将countBooleans()重命名为countTrue()。
卡尔·马纳斯特

5
如果与性能无关,那么该解决方案对我来说几乎是完美的:非常易于阅读和扩展。那正是var-args的目的。
atamanroman 2010年

7
什么鬼,人?这是清晰且经过良好测试的代码,它看起来很多的唯一原因是因为它包含测试。A +++,会再次投票。
ChristofferHammarström,2010年

24

总结一下。之所以称其为布尔代数,是因为:

  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

2
我认为这是最优雅的解决方案。
TorbjørnKristoffersen

9
新手的错误,虽然,一个布尔值不为0,这并不意味着它始终为1
tomdemuyt

13
除了帖子上的标记为“ Java”之外,并且在Java中将它们定义为布尔值时,您不能编写“ a + b + c”。
杰伊(Jay)

要使用Java工作,必须为return ((a?1:0) + (b?1:0) + (c?1:0)) >= 2
David R Tribble 2013年

h,我之所以投票赞成这是因为我认为这是一个C ++问题……为什么我要阅读Java问题?:/
Carlo Wood)

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

15

这实际上取决于您所说的“改进”的含义:

更清晰?

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

Terser?

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.

哪一个“改进”在很大程度上取决于情况。


14

这是另一个使用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;
}

运行图/缩小:

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;
}

@Anurag:我非常喜欢M / R和Google最近给予它的曝光(即使这不是FP的真正M / R),我也会在您的回答中称呼bullsh!t。执行Real-World™“材料”的代码行数以十亿计,其中没有使用任何一行地图/归约。有人回答这样的问题与在我的书肯定是标记为:“试图播放smartie”。更不用说大多数面试官都无法告诉您您是否要大肆宣传他们,因为他们实际上从未在职业生涯中使用M / R编写过一个程序。
语法T3rr0r 2011年

2
@Syntax-每个人都有权发表自己的意见。我的答案只是解决问题的另一种方法。当然,三个布尔值听起来有些夸张,但这并不意味着我要成为这里的聪明人。这是每个人都使用的解决问题的常用方法-将问题分解成小块。那就是数学归纳的工作方式,大多数递归算法的工作方式,这就是人们通常解决问题的方式。
Anurag

13

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

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."<init>":()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
}

您会发现?:的版本比原始版本的固定版本稍好。最好的一种是完全避免分支的一种。从较少的指令(在大多数情况下)的角度来看,这是很好的,对于CPU的分支预测部分来说更好,因为分支预测中的错误猜测会导致CPU停顿。

我想说,最有效的一种是总体上来自月影的一种。平均而言,它使用最少的指令,并减少了CPU中流水线停顿的机会。

要100%确定,您需要找出每条指令的成本(以CPU周期为单位),不幸的是,该成本并不容易获得(您必须先查看热点的来源,然后查看CPU厂商的规格)。为每条生成的指令采用)。

有关代码的运行时分析,请参见Rotsor更新的答案。


5
您只查看字节码。众所周知,JIT将采用字节码中带有分支的版本,并将其转换为本地代码中不带分支的版本。但是人们会倾向于认为字节码中的分支越少越好。
戴维·康拉德

13

直接代码的另一个示例:

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的比较将使用更快(或更少)的代码,这可能会稍快一些。


+1 @Loadmaster,很抱歉,但是您错了!这是最简洁的答案。(即简短且明确表示);)
Ash


@ M.Mimpen:仅适用于类对象。对于基本类型(如n上述),任何体面的编译器都会将每个++操作编译为单个CPU指令,无论是前置还是后置。
David R Tribble 2014年

12

还有另一种方法,但不是很好:

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

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


1
或(a?1:0)+(b?1:0)+(c?1:0)> = 2
彼得·劳里

12

最明显的改进是:

// 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));
}

但是这些改进很小。


10

我不喜欢三元(return a ? (b || c) : (b && c);从最上面的答案开始),而且我不认为有人见过。它是这样写的:

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

8

Clojure中

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

用法:

(at-least 2 true false true)

2
+1通用版本显示了Lisps的强大功能。谢谢,
dsmith

6

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

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;
}

它的优点是,一旦达到所需数量,它就会损坏。因此,如果这是“前100万个值中至少有2个是真实的”,而前两个实际上是真实的,那么它应该比某些“正常”的解决方案更快。


那可能应该是:if(++ counter == howMany)而不是递增,然后分别检查。
Joe Enos 2010年

2
甚至更短:if(b &&(++ counter == howMany))
Joe Enos 2010年

1
我这样做boolean ... boolValues比较容易,但是仍然需要一个数组
Stephen

我不是最新使用Java的人-不知道它的存在。有点奇怪的语法,但是它很有用-偶尔我会用C#(params关键字)来做,它确实使事情变得更好。或者,我不了解Java,但是在.NET中,数组和所有集合都实现IEnumerable <T>,所以我可能会使用与Java等效的任何东西。
Joe Enos

与2of3示例相比,此性能如何?返回一个?(b || c):(b && c);
伊恩·斯普罗特

6

我们可以将布尔值转换为整数并执行以下简单检查:

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

6

由于未指定如何改进代码,因此我将努力通过使其更有趣来改进代码。这是我的解决方案:

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);

但是现在它不再有趣了。


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

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


3
看起来更像C#。答案中应这样提及,因为问题是针对Java的:)
BalusC 2010年

5

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;
}

4
return 1 << $a << $b << $c >= 1 << 2;

摆出这个姿势之前没有看到Suvega的回答,几乎是同一回事。
凯文2010年

这真的有效吗?我假设这是PHP,但是我没有访问权限,但我只是问你:如果$ a为0,会发生什么?
Mark Edgar 2010年

@Mark如果$ a为0,则实际上不起作用。这是一个疏忽。感谢您指出了这一点。:)
凯文2010年

4

最简单的方法(IMO),它不会引起混淆并且易于阅读:

// Three booleans, check if two or more are true

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

在功能上是相同的。从语法上讲,它使那些不习惯使用问号条件运算符的人更容易阅读。我愿意打赌,比知道如何使用问号条件运算符的人数更多的人知道如何使用AND和OR运算符。最初的问题要求“改进的答案”。接受的答案确实简化了答案,但提出了一个非常有趣的问题,即认为可以改善什么。您是为了通用可读性还是为了简化而编程?对我来说,这是对已接受答案的改进:)
abelito 2010年

个人喜好。对我来说,比这种解决方案更容易理解更清晰的三元运算符。
nico 2010年

1
是的,我看到了这个问题,并且想知道为什么没有其他人提到此解决方案。如果将OP的逻辑写为布尔代数,则会得到A B + A C + B C,它具有五个运算。通过关联属性,您可以编写A *(B + C)+ B C,它具有四个运算。
维维安河2010年

这与Jack的答案(6月19日)相同,(C && (A || B)) || (A && B)只是更改了* variable`名称...
user85421 2010年

4

文字解释适用于所有主要语言:

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

但是我可能会让人们更容易阅读,并且可以扩展到三个以上,这似乎被许多程序员所忘记:

boolean testBooleans(Array bools)
{
     int minTrue = ceil(bools.length * .5);
     int trueCount = 0;

     for(int i = 0; i < bools.length; i++)
     {
          if(bools[i])
          {
               trueCount++;
          }
     }
     return trueCount >= minTrue;
}

4

作为@TofuBeer TofuBeer优秀文章的补充,请考虑@pdox pdox的答案:

static boolean five(final boolean a, final boolean b, final boolean c)
{
    return a == b ? a : c;
}

还考虑其“ javap -c”给出的反汇编版本:

static boolean five(boolean, boolean, boolean);
  Code:
    0:    iload_0
    1:    iload_1
    2:    if_icmpne    9
    5:    iload_0
    6:    goto    10
    9:    iload_2
   10:    ireturn

pdox的答案比以前的任何答案编译的字节码少。它的执行时间与其他时间相比如何?

one                5242 ms
two                6318 ms
three (moonshadow) 3806 ms
four               7192 ms
five  (pdox)       3650 ms

至少在我的计算机上,pdox的答案仅比@moonshadow moonshadow的答案稍快,这使pdox的整体速度最快(在我的HP / Intel笔记本电脑上)。


3

在Ruby中:

[a, b, c].count { |x| x } >= 2

可以在JavaVM的JRuby中运行。;-)


3

他可能没有在寻找像按位比较运算符(通常不复杂,但使用布尔值,使用按位运算符非常奇怪)之类的令人费解的东西,或者像转换为int并将其求和之类的回旋点。

解决此问题的最直接,最自然的方法是使用这样的表达式:

a ? (b || c): (b && c)

如果愿意,可以将其放在函数中,但这并不是很复杂。该解决方案从逻辑上讲简明高效。


3

在C中:

return !!a + !!b + !!c >= 2;

实际上,这个答案是错误的……应该大于等于2,因为您至少需要两个真实的布尔值,而不是两个。
Paul Wagland

@保罗·瓦格兰(Paul Wagland):非常感谢。
马特·乔纳

@ergosys:我回答了两次吗?
马特·乔纳
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.