Java 检查三分之二的布尔值是否为真
最近一位采访者问我这样一个问题:给定三个布尔变量a、b和c,如果三个变量中至少有两个为真,则返回真 我的解决办法如下:Java 检查三分之二的布尔值是否为真,java,boolean,boolean-logic,Java,Boolean,Boolean Logic,最近一位采访者问我这样一个问题:给定三个布尔变量a、b和c,如果三个变量中至少有两个为真,则返回真 我的解决办法如下: boolean atLeastTwo(boolean a, boolean b, boolean c) { if ((a && b) || (b && c) || (a && c)) { return true; } else{ return false; } }
boolean atLeastTwo(boolean a, boolean b, boolean c) {
if ((a && b) || (b && c) || (a && c)) {
return true;
}
else{
return false;
}
}
他说这可以进一步改进,但是如何改进呢?而不是写:
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) {
if (a) {
return b||c;
}
else {
return b&&C;
}
或者这个(无论你觉得哪个更容易掌握):
它只测试a
和b
一次,最多测试c
工具书类
- 为什么不按字面意思实施呢?:)
在C语言中,您可以只编写
a+b+C>=2
(或者!!a+!!b+!!C>=2
,这样非常安全)
为了响应ToBubeer对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);
}
}
}
以后的迭代:
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虚拟机可以做些什么来降低性能
下面是使用-client
VM开关运行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
神秘
如果我运行它,它会慢近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
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
最明显的改进包括:
// 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));
}
但是这些改进是很小的。您不需要使用运算符的短路形式
return(a&b)|(b&c)|(c&a)代码>
这与您的版本执行相同数量的逻辑操作,但是完全没有分支。可读性应该是目标。阅读代码的人必须立即理解您的意图。这就是我的解决方案
int howManyBooleansAreTrue =
(a ? 1 : 0)
+ (b ? 1 : 0)
+ (c ? 1 : 0);
return howManyBooleansAreTrue >= 2;
int two(int a, int b, int c) {
return !a + !b + !c < 2;
}
这类问题可以通过以下方法解决:
由此推断,第一行需要一个组,第一列需要两个组,以获得多基因润滑油的最佳解决方案:
(C && (A || B)) || (A && B) <---- first row
^
|
first column without third case
(C&&(A | | | B))| |(A&&B)在这里获取答案(到目前为止):
并通过反编译器运行它们(javap-cx>results.txt):
从“X.java”编译而来
公共类X扩展了java.lang.Object{
公共X();
代码:
0:aload_0
1:invokespecial#1;//方法java/lang/Object。“:()V
4:返回
静态布尔a(布尔、布尔、布尔);
代码:
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:我轮到你了
静态布尔b(布尔、布尔、布尔);
代码:
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:转到33
32:iconst_0
33:我轮到你了
静态布尔c(布尔,布尔,布尔);
代码:
0:iload_0
1:iload_1
2:土地
3:iload_1
4:iload_2
5:土地
6:ior
7:iload_2
8:iload_0
9:土地
10:ior
11:我轮到你了
静态布尔d(布尔、布尔、布尔);
代码:
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:转到18
17:iconst_0
18:iadd
19:iload_2
20:ifeq 27
23:iconst_1
24:转到28
27:iconst_0
28:iadd
29:iconst_2
30:if_icmplt 37
33:iconst_1
34:转到38
37:iconst_0
38:我轮到你了
}
您可以看到?:的版本比您的原始版本的固定版本稍好一些。最好的方法是完全避免分支。从指令更少(大多数情况下)的角度来看,这是好的,对于CPU的分支预测部分来说更好,因为分支预测中的错误猜测可能会导致CPU暂停
我认为最有效的一个是来自moonshadow的。它平均使用最少的指令,减少了CPU中管道暂停的机会
要100%确定,您需要找出每条指令的成本(以CPU周期为单位),不幸的是,这并不容易获得(您必须查看热点的源代码,然后CPU供应商指定每条生成指令所需的时间)
有关代码的运行时分析,请参阅Rotsor更新的答案。另一个直接代码示例:
boolean atLeastTwo(boolean a, boolean b, boolean c)
{
return ((a && b) || (b && c) || (a && c));
}
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的比较快(或者更少),那么这可能会运行得稍快一些。这里是一种测试驱动的通用方法。目前提供的大多数解决方案都不那么“高效”,但它们清晰、经过测试、有效且通用
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;
}
}
下面是另一个使用map/reduce的实现。这在分布式环境中可以扩展到数十亿布尔值。使用MongoDB:
创建布尔值数据库:
db.values.insert({value: true});
db.values.insert({value: false});
db.values.insert({value: true});
创建地图,减少功能:
编辑:我喜欢将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;
}
还有另一种方法,但不是很好:
return (Boolean.valueOf(a).hashCode() + Boolean.valueOf(b).hashCode() + Boolean.valueOf(c).hashCode()) < 3705);
return(Boolean.valueOf(a).hashCode()+Boolean.valueOf(b.hashCode()+Boolean.valueOf(c.hashCode())<3705);
Boolean
hashcode值固定为1231表示真,1237表示假,因此同样可以使用最简单的方法(IMO),不易混淆且易于阅读:
// Three booleans, check if two or more are true
return ( a && ( b || c ) ) || ( b && c );
做这件事的方法太多了…我不喜欢三元(返回a?(b
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;
}
return (Boolean.valueOf(a).hashCode() + Boolean.valueOf(b).hashCode() + Boolean.valueOf(c).hashCode()) < 3705);
// Three booleans, check if two or more are true
return ( a && ( b || c ) ) || ( b && c );
Function ReturnTrueIfTwoIsTrue(bool val1, val2, val3))
{
return (System.Convert.ToInt16(val1) +
System.Convert.ToInt16(val2) +
System.Convert.ToInt16(val3)) > 1;
}
boolean atLeastTwo(boolean a, boolean b, boolean c) {
if (a) {
return b||c;
}
else {
return b&&C;
}
return a ^ b ? c : a
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;
}
return 1 << $a << $b << $c >= 1 << 2;
0 x 0 = 0
1 x 0 = 0
1 x 1 = 1
0 + 0 = 0
1 + 0 = 1
1 + 1 = 0 (+ carry)
return (a + b + c) >= 2
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;
}
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;
}
(defn at-least [n & bools]
(>= (count (filter true? bools)) n)
(at-least 2 true false true)
return (a & b) | (c & (a ^ b));
return (a==b) ? a : c;
static boolean five(final boolean a, final boolean b, final boolean c)
{
return a == b ? a : 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
one 5242 ms
two 6318 ms
three (moonshadow) 3806 ms
four 7192 ms
five (pdox) 3650 ms
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.
return !!a + !!b + !!c >= 2;
(int(a) + int(b) + int(c)) >= 2
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);