3つのうち2つ以上のブール値がtrueかどうかを確認します


579

インタビュアーが最近私にこの質問をしました。3つのブール変数a、b、cが与えられ、3つのうち少なくとも2つが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

45
「最高のIQを持っている」インタビューのように聞こえます。私は失敗するでしょう。
Chris Dutrow、2010年

79
atLeastTwo(iWantYou, iNeedYou, imEverGonnaLoveYou)
Andrew Grimm

92
なぜ人々は最も些細な質問に賛成するのですか?
BlueRaja-ダニーPflughoeft

46
一般的で理解しやすい質問は、多くの賛成票を獲得します。非常に具体的で技術的な質問はありません。
ジェイ

回答:


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:パズルの素敵な解決策ですが、現実にはこのようなものは見られないでしょう:)
Juliet

124
@ジュリエット:わかりません。これが(実際の変数名を使用した)現実の要件である場合は、十分に理解できると思います。考えてみてreturn hasGoodAttendance ? (passedCoursework || passed Exam) : (passedCoursework && passedExam)、それは私には問題なく見えます。
Andrzej Doyle

18
見た目は悪くないと思いますが、ドメインの要件が「少なくとも2つ」であると理解されている場合は、読みやすくなりますatLeastTwo(hasgoodAttendance, passedCoursework, passedExam)。「少なくとも2つのブール値が真である」という考えは、独自の機能に値するほど一般的です。
Ken

17
@リース:対面インタビューで最もミクロに最適化されたコードを尋ねることは非現実的であり、あえて言うと役に立たない。マイクロ最適化は、ニーズによって駆動される場合、人間の本能(ひどいことが知られている)ではなく、実行時プロファイリングの結果によって導かれます。これをさらに最適化するプロセスをインタビュー対象者に確実に尋ねることができます。結果自体よりも重要です。
polygenelubricants

17
三項演算子は、読めるようにすべき一般的なイディオムです。読めない場合は、読めるまで勉強してください。三項演算子の使用は、軽蔑的な意味で「賢い」と私が考えるものではありません。しかし、はい、一般的に「少なくとも2つ」のロジックを使用している場合は、これをメソッド呼び出しの本文として使用します。
スティーブンP

494

XORを使用して比較的単純な問題に答えるためだけに...

return a ^ b ? c : a

160
うわー、クールなソリューション。しかし、私にとっては、その逆バージョンの方が理解しやすいです:a == b?a:c
Rotsor 2010年

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

4
ええ、XORはそのような悪い報道を受けて、あなたがそれを使う機会を得ることはめったにありません。
EightyOne Unite 2010

19
@ Stimul8dは、ブール値の場合、!=と同じですが、読みにくくなります。それを理解することは私にとってユーレカの瞬間でした...
Tikhon Jelvis

2
私は純粋にバイナリ形式を好む:return((a ^ b)&c)| (a&b)。分岐がなく(速く)、読みやすい:(aまたはbがtrueでcがtrue)または(aとbが両方ともtrue)。(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);
        }
    }
}

これは私のマシンで以下を出力します(UbuntuをIntel Core 2 + sun java 1.6.0_15-b03でHotSpot Server VM(14.1-b02、混合モード)で実行):

1回目と2回目の反復:

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インタープリターで実行すると、速度はほぼ100倍遅くなりますが、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

Mac Java 1.6.0_26-b03-383-11A511でのPaul Waglandの結果

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として定義されているという標準が含まれていると思います。私はまだこれをしません。
Mark Peters、

1
入力がブール演算の結果である場合、それは可能ですか?そして、それはC ++の「ブール」型で可能ですか?
Rotsor

2
@Rotsor:入力がブール演算の結果である必要があるとは誰も言っていません。ネガティブがない場合でも、2と定義するように、火で遊んでいます。条件が誤検出されます。しかし、ブール値を算術に混ぜるという考えが嫌いなほど、私はそれを気にしません。Javaソリューションは、ブール型から整数型への微妙な変換に依存しないという点で明確です。
マークピーターズ


143

この種の質問は、カルノーマップで解決できます。

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

そこから、最初の行に1つのグループ、最初の列に2つのグループが必要であると推測し、多重潤滑剤の最適なソリューションを取得します。

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

10
@ジャスティン、カーナフマップは、論理演算の数を3つのANDと2つのORから2つのANDと2つのORに減らしました。@ジャック、カーナフマップの存在を思い出してくれてありがとう。
Tachy

14
新しい何かのための+1。私の次の機能仕様には、それが必要かどうかにかかわらず、Kマップが含まれます。
ジャスティンR.

2
おそらく、可読性の悪さは、(1)コメント付きの適切な表と(2)適切な単体テスト... +1で補うことができます。
moala

140

読みやすさが目標です。コードを読む人はあなたの意図をすぐに理解する必要があります。これが私の解決策です。

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

return howManyBooleansAreTrue >= 2;

21
前提に同意しますが、(a && b)|| (b && c)|| (a && c)は、ソリューションIMHOよりもはるかに読みやすくなっています。
エイドリアングリゴー

62
うーん、今度は「4つのブール値のうち2つ」バージョンが必要になります...ダナテルのバージョンの方がずっと簡単になりました。
アラファンギオン2010年

6
またはScalaの場合:Seq(true, true, false).map(if (_) 1 else 0).sum >= 2
retronym

5
@retronym:うーん、違います。Javaの方法はScalaでうまく機能し、読みやすく、効率的です。
Seun Osewa 2010

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

説明:

の場合a==b、両方がtrueまたは両方がfalseです。両方がtrueの場合、2つのtrueブール値が見つかったため、(を返すことによりa)trueを返すことができます。両方がfalseの場合、trueであっても2つの真のブール値は存在できないcため、(を返すことによりa)falseを返します。それはその(a==b) ? a一部です。どう: cですか?まあa==bfalseの場合は、1つaまたはbtrueでなければならないので、最初のtrueブール値を見つけました。残っているのはif cもtrue だけなのでc、答えとして返します。


8
cもテストされていません...素晴らしい!
CurtainDog 2010年

平等の推移的な関係とブール値が真または偽のいずれかであるという事実を使用します+1
Christophe Roussy 2013年

3
とてもエレガント!私はそれを信じるためにペンと紙でチェックしなければなりませんでした:)あなたへの称賛!
エイドリアン

3
私は「あれば、この考えabそう、同意し、彼らはそれが他の、彼らは反対し、あるものは何でも一緒に行く、過半数の票を持ってc決める投票がある」
ベン・ミルウッド

34

演算子の短絡形式を使用する必要はありません。

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

これは、バージョンと同じ数の論理演算を実行しますが、完全にブランチレスです。


11
なぜ1ができるのに5つの評価を強制したいのですか?実際には、同じ数の論理演算を実行するわけではありません。実際、それは常により多く実行します。
マークピーターズ

2
バイナリ演算とブール演算を混在させることは悪い考えだと思います。それはレンチで壁にねじを打ち込むようなものです。最悪なのは、セマンティクスが異なることです。
Peter Tillemans、2010年

12
@Mark-高速になる可能性があります... CPUパイプラインでの誤った分岐予測の影響によって異なります。ただし、そのようなマイクロ最適化はJITコンパイラーに任せるのが最善です。
スティーブンC

4
Java(または他の言語)でこのようなことを行うのは問題ありませんが、いくつかの注意点があります:1)より高速である必要があります(この場合、私はそうだと思います。2番目の回答を参照してください)2)望ましい非常に高速(そうであるかどうかは不明)、3)「奇数」であるため最も重要に文書化されている。それが目的を果たし、文書化されている限り、それが理にかなっているときに「ルールを破る」ことは問題ありません。
TofuBeer

11
@Peter Tillemans二項演算子との混合はありません。Javaではこれらはブール演算子です。
starblue

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
個人的には、このコードは非常に多くの理由からひどいものです。私は反対投票するつもりはありませんが、これを製品コードで見たことがあれば、私は呪いをかけるでしょう。非常に単純なブール演算は、このように複雑である必要はありません。
CaptainCasey 2010年

10
@CaptainCasey、あなたの理由を知りたいと思います。これはかなり良いコードだと思います。理解しやすく、検証しやすい優れた一般化された関数と、それを利用する特定の関数があり、理解と検証も簡単です。現実の世界では、それらを公開して別のクラスに入れます。それ以外-私はこのコードを喜んで本番環境に入れました。ああ-はい-countBooleans()をcountTrue()に名前変更します。
Carl Manaster、2010年

5
パフォーマンスに関するものでなければ、このソリューションは私にはほぼ完璧に見えます。非常に読みやすく、拡張可能です。それがまさにvar-argsの目的です。
アタマンロマン

7
一体何なんだ、人々?これは明確で十分にテストされたコードであり、多くのように見える唯一の理由は、テストが含まれているためです。A +++、再び賛成票を投じます。
ChristofferHammarström2010

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
ルーキーの間違いしかし、ブール値は常に1を意味するものではありませんので、0でない
tomdemuyt

13
ただし、投稿のタグに「Java」と書かれており、Javaでブール値として定義されている場合は「a + b + c」とは記述できません。
ジェイ

Javaで作業するには、それが必要return ((a?1:0) + (b?1:0) + (c?1:0)) >= 2です。
David R Tribble 2013年

C ++の質問だと思ったので、投票しました。Javaの質問を読んでいるのはなぜですか。:/
カルロウッド

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

テルサー?

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

マップの作成、関数の削減:

編集:私はmap / reduceをジェネリックリストに適用することについてのCurtainDogの 答えが好きなので、値をカウントする必要があるかどうかを決定するコールバックを受け取るマップ関数に進みます。

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

@Anurag:私がM / RとGoogleが最近それに与えた露出(FPからの1つの真のM / Rでなくても)が好きなだけで、私はあなたの答えでbullsh!tを呼ぶ傾向があります。数十億行ものコード行があり、マップ/リデュースが1行も使用されていないReal-World [TM]の「もの」を実行しています。これでそのような質問に答える人は、私の本では間違いなく「スマーティーをプレイしようとしている」というフラグが立てられています。言うまでもなく、ほとんどの面接担当者は、彼らが実際にM / Rを使用して単一のプログラムを書いたことがないため、彼らを強引にしようとしているのかどうかを判断することはできません。
SyntaxT3rr0r

2
@構文-誰もが自分の意見を受け取る権利があります。私の答えは、問題を調べるもう1つのアプローチです。確かに、3つのブール値では誇張されて聞こえますが、だからといってここでスマーティーパンツを目指しているわけではありません。これは、誰もが使用する問題解決の一般的なアプローチです。問題を細かく分割します。これが数学的帰納法のしくみであり、これが最も再帰的なアルゴリズムのしくみであり、一般的な問題の解決方法です。
Anurag、2011

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の分岐予測部分には適しています。

最も効果的なのは月影全体の効果だと思います。平均して使用する命令が最も少なく、CPUでパイプラインがストールする可能性が低くなります。

100%確実にするには、各命令のコスト(CPUサイクル)を調べる必要があります。これは残念ながらすぐには利用できません(ホットスポットのソースを確認してから、CPUベンダーの仕様をその時点で確認する必要があります)生成された命令ごとに取得されます)。

コードのランタイム分析については、Rotsorによる更新された回答を参照してください。


5
あなたはバイトコードを見ているだけです。ご存知のとおり、JITはバイトコードに分岐があるバージョンを取得し、ネイティブコードに分岐がないバージョンに変換します。しかし、バイトコードのブランチは少ないほうがよいと考える傾向があります。
David Conrad

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ハッシュコード値を均等に使用している可能性が偽そうに当てはまると1237のための1231に固定されています<= 3699


1
または(a?1:0)+(b?1:0)+(c?1:0)> = 2
Peter Lawrey

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素晴らしい汎用バージョンはLispの力を示しています。おかげで、
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;
}

その利点は、探している数に達すると壊れるということです。したがって、これが「この1,000,000の値のうち少なくとも2つがtrueである」場合、最初の2つが実際にtrueであると、より「通常の」ソリューションのいくつかよりも高速になるはずです。


これはおそらく次のようになるはずです。
Joe Enos、2010年

2
またはさらに短い場合:if(b &&(++ counter == howMany))
Joe Enos

1
私はやるだろうboolean ... boolValues、それが呼び出しに簡単ですので、まだ配列取る
スティーブン

私のJavaは最新ではありません-それが存在することを知りませんでした。奇妙な構文の一種ですが、それは便利です-たまにC#(paramsキーワード)でそれを行うので、呼び出しが簡単になります。または、Javaについてはわかりませんが、.NETでは、配列とすべてのコレクションがIEnumerable <T>を実装しているため、Javaと同等のものを使用することになります。
Joe Enos、2010年

これのパフォーマンスは2of3の例とどのように比較されますか??を返す (b || c):(b && c);
Iain Sproat 2010年

6

boolを整数に変換して、この簡単なチェックを実行できます。

(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

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;

これを提起する前にスベガの答えを見なかった、ほとんど同じこと。
Kevin

これは本当に機能しますか?これはPHPだと思いますが、アクセスできませんが、$ aが0の場合はどうなるでしょうか。
Mark Edgar

@Mark $ aが0の場合、実際には機能しません。これは見落としでした。ご指摘いただきありがとうございます。:)
Kevin

4

紛らわしくなく読みやすい最も簡単な方法(IMO):

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

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

機能的には同じです。構文的には、クエスチョンマークの条件演算子の使用に慣れていない人でも読みやすくなります。クエスチョンマークの条件演算子の使用方法を知っている人の数よりも、ANDおよびOR演算子の使用方法を知っている人の方が多いと思います。元の質問は「改善された答え」を要求します。受け入れられた答えは答えを単純化しますが、改善を何と考えるかについて非常に興味深い質問を投げかけます。普遍的な読みやすさのために、または単純化のためにプログラミングしますか?私にとって、これは受け入れられた回答よりも
優れてい

個人的な好み。私にとっては、このソリューションよりもクリーンな三項演算子を理解する方がはるかに簡単です。
nico

1
ええ、私はこの問題を見て、なぜ他の誰もこの解決策について言及しなかったのかと思っていました。OPのロジックをブール代数として書き出すと、5つの演算を持つ A B + A C + B Cが得られます連想プロパティにより、 4つの演算を持つA *(B + C)+ B C を記述できます
ビビアンリバー

ジャックの回答(6月19日)(C && (A || B)) || (A && B)が* variable`の名前を変更したのと同じです...
user85421

4

文字通りの解釈は、すべての主要言語で機能します。

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

しかし、私はおそらく人々が読みやすくし、3つ以上に拡張できるようにするでしょう-これは多くのプログラマーが忘れているようです:

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の優れた投稿への追加として、@ 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の回答よりわずかに速いだけなので、(私のHP / Intelラップトップ上で)pdoxは全体的に最速です。



3

彼はおそらく、ビットごとの比較演算子のように複雑なもの(通常は複雑ではないがブール値を使用しているため、ビットごとの演算子を使用するのは非常に奇妙です)や、intに変換して合計するような非常に回りくどいものを探していません。

これを解決する最も直接的で自然な方法は、次のような式を使用することです。

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

必要に応じて関数に入れますが、それほど複雑ではありません。ソリューションは論理的に簡潔で効率的です。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.