Check if at least two out of three booleans are true
一位面试官最近问我这个问题:给定三个布尔变量A、B和C,如果三个变量中至少有两个是真的,则返回真。
我的解决方案如下:
1 2 3 4 5 6 7 8 | boolean atLeastTwo(boolean a, boolean b, boolean c) { if ((a && b) || (b && c) || (a && c)) { return true; } else{ return false; } } |
他说这可以进一步改进,但如何改进呢?
而不是写作:
1 2 3 4 5 | if (someExpression) { return true; } else { return false; } |
写:
1 | return someExpression; |
至于表达式本身,类似于:
1 2 3 | boolean atLeastTwo(boolean a, boolean b, boolean c) { return a ? (b || c) : (b && c); } |
或者这个(你觉得哪个更容易掌握):
1 2 3 | boolean atLeastTwo(boolean a, boolean b, boolean c) { return a && (b || c) || (b && c); } |
它只测试一次
- JLS 15.25条件运算符?:
只是为了使用XOR来回答一个相对直接的问题…
1 | return a ^ b ? c : a |
为什么不按字面意思执行呢?:)
1 | (a?1:0)+(b?1:0)+(c?1:0) >= 2 |
在C语言中,您只需编写
为了响应TofuBeer对Java字节码的比较,这里有一个简单的性能测试:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 | 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); } } } |
在我的机器上打印以下内容(在英特尔核心2上运行Ubuntu + SunJava1.60Y15-B03与热点服务器VM(141-B02,混合模式)):
第一次和第二次迭代:
1 2 3 4 5 6 | 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 |
后期迭代:
1 2 3 4 5 | 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)。
这里,如果我用一个EDCOX1×5 VM交换机运行Java,将会发生什么:
1 2 3 4 5 | 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倍,但是EDCOX1×6版本则获胜。
从运行OS X的最新代码的ToSubeer获得的结果:
1 2 3 4 5 | 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 Java1.602626-B03-33-11A511的Paul Wagland结果
1 2 3 4 5 6 7 | 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 2 3 4 5 6 | | C | !C ------|---|---- A B | 1 | 1 A !B | 1 | 0 !A !B | 0 | 0 !A B | 1 | 0 |
从中可以推断,第一行需要一组润滑油,第一列需要两组润滑油,从而获得多基因润滑油的最佳解决方案:
1 2 3 4 | (C && (A || B)) || (A && B) <---- first row ^ | first column without third case |
目标应该是可读性。阅读代码的人必须立即理解您的意图。这是我的解决方案。
1 2 3 4 5 6 | int howManyBooleansAreTrue = (a ? 1 : 0) + (b ? 1 : 0) + (c ? 1 : 0); return howManyBooleansAreTrue >= 2; |
1 | return (a==b) ? a : c; |
说明:
如果
您不需要使用运算符的短路形式。
这与您的版本执行相同数量的逻辑操作,但是完全没有分支。
这是一种测试驱动的通用方法。不是像目前为止提供的大多数解决方案那样"高效",而是清晰的、经过测试的、有效的和通用的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | 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; } } |
总结一下。它被称为布尔代数是有原因的:
1 2 3 4 5 6 7 | 0 x 0 = 0 1 x 0 = 0 1 x 1 = 1 0 + 0 = 0 1 + 0 = 1 1 + 1 = 0 (+ carry) |
如果你看这里的真值表,你会发现乘法是布尔的,简单地说加法就是异或。
要回答您的问题:
1 | return (a + b + c) >= 2 |
1 2 3 4 | boolean atLeastTwo(boolean a, boolean b, boolean c) { return ((a && b) || (b && c) || (a && c)); } |
这真的取决于你所说的"改进"是什么意思:
Clearer?
1 2 3 4 | boolean twoOrMoreAreTrue(boolean a, boolean b, boolean c) { return (a && b) || (a && c) || (b && c); } |
Terser?
1 2 3 4 | boolean moreThanTwo(boolean a, boolean b, boolean c) { return a == b ? a : c; } |
更一般?
1 2 3 4 5 6 7 8 9 10 11 12 13 | boolean moreThanXTrue(int x, boolean[] bs) { int count = 0; for(boolean b : bs) { count += b ? 1 : 0; if(count > x) return true; } return false; } |
更具伸缩性?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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; } |
更快?
1 | // Only profiling can answer this. |
哪一个"改进"在很大程度上取决于形势。
下面是另一个使用map/reduce的实现。这可以很好地扩展到数十亿个布尔值?在分布式环境中。使用MUGODB:
创建布尔型数据库
1 2 3 | db.values.insert({value: true}); db.values.insert({value: false}); db.values.insert({value: true}); |
创建地图,减少功能:
编辑:我喜欢curtaindog关于将map/reduce应用于泛型列表的回答,所以这里有一个map函数,它接受一个回调,该回调决定是否应该计算一个值。
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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; } |
运行图/减少:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; } |
另一个直接代码示例:
1 2 3 4 5 | int n = 0; if (a) n++; if (b) n++; if (c) n++; return (n >= 2); |
显然,这不是最简洁的代码。
补遗
另一个(稍微优化的)版本:
1 2 3 4 5 | int n = -2; if (a) n++; if (b) n++; if (c) n++; return (n >= 0); |
这可能运行得稍微快一些,假设与0的比较将使用比与2的比较更快(或更少)的代码。
在这里(到目前为止)回答:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 | 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更新的答案。
还有另一种方法,但不是很好:
1 |
最明显的改进是:
1 2 3 4 5 6 7 | // 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; } |
然后
1 2 3 4 | // 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)); } |
但这些改进是微不足道的。
我不喜欢三元(最上面的答案是
1 2 3 4 5 6 7 | boolean atLeastTwo(boolean a, boolean b, boolean c) { if (a) { return b||c; } else { return b&&C; } |
在克洛杰里:
1 2 | (defn at-least [n & bools] (>= (count (filter true? bools)) n) |
用途:
1 | (at-least 2 true false true) |
我们可以将bools转换为整数并执行以下简单检查:
1 | (int(a) + int(b) + int(c)) >= 2 |
我想我还没有看到这个解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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; } |
它的优点是一旦达到你要找的数字,它就会断开。如果这是"1000000个值中至少有2个是真的",前两个值实际上是真的,那么它应该比一些更"正常"的解更快。
由于没有规定如何改进代码,我将努力改进代码,使其更有趣。我的解决方案是:
1 2 3 4 5 6 7 8 9 10 | 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; } |
如果有人想知道这段代码是否有效,可以使用相同的逻辑进行简化:
1 2 3 4 5 6 7 8 9 | 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; |
}
这可以进一步归结为:
1 | return ((a || b) && (c)) || (a && b); |
但现在不再有趣了。
C解决方案。
1 2 3 | int two(int a, int b, int c) { return !a + !b + !c < 2; } |
或者您可能更喜欢:
1 2 3 | int two(int a, int b, int c) { return !!a + !!b + !!c >= 2; } |
1 2 3 4 5 6 |
这么做的方法太多了…
文字解释适用于所有主要语言:
1 | return (a ? 1:0) + (b ? 1:0) + (c ? 1:0) >= 2; |
但我可能会让人们更容易阅读,并且可以扩展到三种以上——这似乎被许多程序员忘记了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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; } |
1 | return 1 << $a << $b << $c >= 1 << 2; |
作为@tofubeer tofubeer优秀文章的补充,请考虑@pdox pdox的答案:
1 2 3 4 | static boolean five(final boolean a, final boolean b, final boolean c) { return a == b ? a : c; } |
还可以考虑"javap-c"给出的反汇编版本:
1 2 3 4 5 6 7 8 9 | 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的答案编译的字节代码比以前的任何答案都少。它的执行时间与其他时间相比如何?
1 2 3 4 5 | one 5242 ms two 6318 ms three (moonshadow) 3806 ms four 7192 ms five (pdox) 3650 ms |
至少在我的电脑上,pdox的答案比@moonshadow moonshadow的答案略快,使pdox成为最快的整体(在我的HP/Intel笔记本电脑上)。
最简单的方法(IMO)不容易混淆和阅读:
1 2 3 | // Three booleans, check if two or more are true return ( a && ( b || c ) ) || ( b && c ); |
通过真值表计算:
1 | return (a & b) | (c & (a ^ b)); |
这种类型的阅读效果更好:
1 2 3 4 5 6 | if (a) { return b || c; } else { return b && c; } |
在C:
1 | return !!a + !!b + !!c >= 2; |
露比:
它可以在javavm上的jruby中运行。;-)
他可能没有寻找任何复杂的东西,比如位比较运算符(通常不复杂,但是有布尔值,使用位比较运算符是非常奇怪的),或者一些非常迂回的东西,比如转换为int并求和。
解决这一问题最直接和自然的方法是使用如下表达式:
1 | a ? (b || c): (b && c) |
如果你愿意的话,把它放在一个函数中,但是它并不复杂。该解决方案在逻辑上简洁高效。
应该是:
1 | (a || b && c) && (b || c && a) |
另外,如果
1 | (a + b*c) * (b + c*a) > 0 |
当我看到这个问题时,我的第一个想法是:
1 2 3 4 5 6 7 8 | int count=0; if (a) ++count; if (b) ++count; if (c) ++count; return count>=2; |
在看到其他帖子后,我承认
1 | return (a?1:0)+(b?1:0)+(c?1:0)>=2; |
更优雅。我想知道相对的运行时间是什么。
无论如何,我认为这种解决方案比
1 | return a&b | b&c | a&c; |
因为品种更容易扩展。如果稍后我们添加第四个必须测试的变量呢?如果变量的数量是在运行时确定的,并且我们被传递一个未知大小的布尔数组,会怎么样?依赖于计数的解决方案比依赖于列出所有可能组合的解决方案更容易扩展。此外,当列出所有可能的组合时,我怀疑犯错误要容易得多。比如试着为"4中的任意3"编写代码,并确保您不会错过任何代码,也不会复制任何代码。现在用"任意5/7"试试。
我找到了这个解决方案。
1 2 3 4 | boolean atLeastTwo(boolean a, boolean b, boolean c) { bool result = !(a ^ b ^ c) && !(!a & !b & !c) || (a & b & c); return result; } |
三元操作符可以让书呆子的汁液流动,但它们可能会令人困惑(使代码不易维护,从而增加了bug注入的可能性)。杰夫·阿特伍德在这里说得很好:
It's a perfect example of trading off
an utterly meaningless one time
write-time savings against dozens of
read-time comprehension penalties-- It
Makes Me Think.
为了避免使用三元运算符,我创建了以下函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 | function atLeastTwoTrue($a, $b, $c) { $count = 0; if ($a) { $count++; } if ($b) { $count++; } if ($c) { $count++; } if ($count >= 2) { return true; } else { return false; } } |
这和其他一些解决方案一样酷吗?不,更容易理解吗?对。这会导致可维护性更强、错误代码更少吗?对。
EDCOX1×4×-Java如何使用三个比较而不是OP的六。
错了,我应该早点检查。
对这个问题的最佳答案应该是"作为一名员工,我写它是很重要的,这样我的意思就清晰了,同时在绩效需要的地方保持效率。"下面是我写它的方式:
1 2 3 | function atLeastTwoAreTrue(a, b, c) { return (a && b) || (b && c) || (a && c); } |
实际上,这个测试是如此的做作,以至于如果你用一个简单的注释来适应它,那么写一个最快、最神秘的方法是完全可以接受的。但是,总的来说,在这个一行程序的世界中,我们需要在这个世界上有更多可读的代码。-)
不是在性能上下文中,而是良好的代码(可重用的可扩展和可读代码)
1 2 3 4 5 6 7 8 9 10 | static boolean trueBooleans (int howMany,boolean ... bools) { int total = 0; for (boolean b:bools) if (b && (++total == howMany)) return true; return false; } |
在我编写Java的拙劣意见中,容易处理意外变化和重复代码比简洁(脚本语言域)或快速程序更重要。
仅供参考,这只是一个完整的加法器的执行位。在硬件方面,您可以使用逻辑工作来根据不同的布尔表达式来确定最佳电路。我想传统的XOR解决方案要比海报所展示的不那么简洁的表达更费劲。
让这三个布尔值为a、b和c…。
您可以使用k-map并使用布尔表达式…
在这种情况下,布尔表达式将是a(b+c)+c
或者如果((a&;&;(b_c))c){回归真实;}其他的返回错误;
这个问题的非简化解决方案是:
1 | a'bc + abc' + abc + ab'c |
减少使用k-maps,可以得到:
1 | bc + ab + ac |
我们可以通过在A'BC和ABC的造币厂使用独家或联合使用ABC和AB'C的造币厂来进一步减少这一点:
1 | b(a ^ c) + ac |
当然,这个问题更多的是关于你如何解决问题/思考,而不是你实际的编码能力。
一个稍微简洁的版本可以是
return ((a ^ b) && (b ^ c)) ^ b
但是就像以前的海报上说的,如果我在我正在研究的任何代码中看到这个,就会有人听到了。:)
所提出问题中的2和3肯定是神奇的数字。"正确"的答案取决于面试官是否试图掌握布尔逻辑(我不认为PDOX的答案在这方面是最好的)或者你对架构问题的理解。
我倾向于使用一个map-reduce解决方案,它可以接受任何具有任意条件的列表。
x=或(a+b,c)
A BC X
1 1 0 0
0 0 1 1
0 1 1 1
如果目标是为三个操作数返回三个值中的两个,则算术和迭代方法往往相对无效。在许多CPU架构中,一个好的形式是"返回((A&B)&C)(A&B);"。这需要四个布尔运算。在单累加器机器上(在小型嵌入式系统中很常见),每字节总有七条指令。"RETURN(A&B)(A&C)(B&C);"形式可能看起来更好,但它需要五个布尔运算,或者一台累加器机器上每字节九个指令。
顺便说一下,在CMOS逻辑中,计算"三取二"需要12个晶体管(相比之下,一个反相器需要两个,一个两输入NAND或不需要四个,一个三输入NAND或不需要六个)。
有一件事我没有看到其他人指出,在求职面试的"请给我写一些代码"部分,一个标准的做法是说"你能改进一下吗?"或者"你对此完全满意"或者"这是尽可能优化的吗?"当你说你完成了。有可能你听到"你会怎样改进它",因为"这可能会得到改进;如何改进?".在这种情况下,将
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int count=0; boolean atLeastTwo(boolean a, boolean b, boolean c) { if (a) count++; if (b) count++; if (c) count++; if (count>1) return true; else return false; } |
在C中,从我的头顶开始:
1 2 3 4 | public bool lol(int minTrue, params bool[] bools) { return bools.Count( ( b ) => b ) >= minTrue; } |
应该很快。
呼叫如下:
1 | lol( 2, true, true, false ); |
这样,您就可以将规则(两个必须为true)留给调用方,而不是将它们嵌入到方法中。
现在用Java 8,我真的很喜欢这样的事情:
1 2 3 | boolean atLeastTwo(boolean a, boolean b, boolean c) { return Stream.of(a, b, c).filter(active -> active).count() >= 2; } |
使用Java 8的流功能,对任意数量的任意数量的布尔值采取另一种方法。如果在处理所有元素之前流达到极限,则流会短路:
1 2 3 4 5 6 7 8 9 10 11 12 13 | public static boolean atLeastTrue(int amount, Boolean ... booleans) { return Stream.of(booleans).filter(b -> b).limit(amount).count() == amount; } public static void main(String[] args){ System.out.println("1,2:" + atLeastTrue(1, true, false, true)); System.out.println("1,1:" + atLeastTrue(1, false, true)); System.out.println("1,0:" + atLeastTrue(1, false)); System.out.println("1,1:" + atLeastTrue(1, true, false)); System.out.println("2,3:" + atLeastTrue(2, true, false, true, true)); System.out.println("3,2:" + atLeastTrue(3, true, false, true, false)); System.out.println("3,3:" + atLeastTrue(3, true, true, true, false)); } |
输出:
1 2 3 4 5 6 7 | 1,2: true 1,1: true 1,0: false 1,1: true 2,3: true 3,2: false 3,3: true |
这个怎么样:
1 | (a - b) ? c : a |
丙:
1 | if (!!a + !!b + !!c >= 2) |
在我看来,三分之三是相当任意的数字,函数应该使用任意的数字。为了回答这个问题,我写了一个函数,如果数组中的x是真的,就可以算出这个函数。
1 2 3 | bool istrue ( int x, bool[] list) y = count true in list return y >= x |
如果我把布尔值转换成一个数,如果这个数不是二的幂,它至少有两个真值。
1 2 | a*4 + b*2 + c*1 = N return( N != 0 && (N&(N-1)) != 0) |
我只是给你一个选择。
使用三元运算符解决问题的最简单形式是:
1 | return a ? (b ? true : c) : (b ? c : false); |
您可能还希望通过使用需求的双重否定来投资寻找解决方案,也就是说,您需要满足条件,而不是至少两个真值,最多一个假值。
函数
1 2 3 4 5 6 7 8 9 | static int ho(bool a) { return a ? 1 : 0; } static bool ko(bool a, bool b, bool c) { return ho(a) + ho(b) + ho(c) >= 2 ? true : false; } |
1 2 3 4 5 6 7 | public static boolean atLeast(int atLeastToBeTrue, boolean...bools){ int booleansTrue = 0; for(boolean tmp : bools){ booleansTrue += tmp ? 1 : 0; } return booleansTrue >= atLeastToBeTrue; } |
你可以从
另一个:
1 | return a? b||c : b&&c |
1 2 3 4 5 6 7 8 | function atLeastTwoTrue($a, $b, $c) { int count = 0; count = (a ? count + 1 : count); count = (b ? count + 1 : count); count = (c ? count + 1 : count); return (count >= 2); } |
我相信使用普通的布尔运算符(a b)&;&;(b c)是很好的,而且更简单。
你可以把这三个字母中的任何一个换成另外两个字母中的任何一个,它仍然是相同的表达式。
我认为最简单的解决方案是:
返回(A&B)C;
我的第一个想法是
1 | return (a||b)&&(b||c) |
但为了便于阅读,我喜欢你们建议的A+B+C>=2解决方案。