Вместо того, чтобы писать:
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);
}
Оно проверяет a
и b
ровно один раз, а c
не более одного раза.
В настоящее время с Java 8, я действительно предпочитаю что-то вроде этого:
boolean atLeastTwo(boolean a, boolean b, boolean c) {
return Stream.of(a, b, c).filter(active -> active).count() >= 2;
}
Этот тип вопросов может быть решен с помощью карты Карно :
| 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
Еще один пример прямого кода:
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.
В дополнение к превосходному сообщению @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) .
Буквальное толкование будет работать на всех основных языках:
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;
}
Мне не нравится троичный (return a ? (b || c) : (b && c);
из верхнего ответа), и я не думаю, что видел, чтобы кто-то упоминал об этом. Это написано так:
boolean atLeastTwo(boolean a, boolean b, boolean c) {
if (a) {
return b||c;
}
else {
return b&&C;
}
Читабельность должна быть целью. Тот, кто читает код, должен сразу понять ваше намерение. Вот мое решение.
int howManyBooleansAreTrue =
(a ? 1 : 0)
+ (b ? 1 : 0)
+ (c ? 1 : 0);
return howManyBooleansAreTrue >= 2;
Почему бы не реализовать это буквально? :)
(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, смешанный режим)):
Первая и вторая итерации:
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).
И вот что произойдет, если я запустил java с переключателем VM -клиент
:
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
Mystery ...
И если я запустил его в 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
Результаты от Пола Вагланда с 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
Самый простой способ (IMO), который не сбивает с толку и легко читается:
// Three booleans, check if two or more are true
return ( a && ( b || c ) ) || ( b && c );
Вам не нужно использовать короткие замыкания операторов.
возврат (a и b) | (b & c) | (c & a);
При этом выполняется то же количество логических операций, что и в вашей версии, однако он полностью не имеет ветвей.
return (a==b) ? a : c;
Объяснение:
Если a == b
, то оба истинны или оба ложны. Если оба значения истинны, мы нашли два наших истинных логических значения и можем вернуть значение true (возвращая a
). Если оба ложны, не может быть двух истинных логических значений, даже если c
истинно, поэтому мы возвращаем false (возвращая a
). Это (a == b)? часть
. А как насчет : c
? Хорошо, если a == b
ложно, тогда точно одно из a
или b
должно быть истинным, поэтому мы нашли первое истинное логическое значение и единственное остается только одно, что имеет значение, если c
также истинно, поэтому мы возвращаем c
в качестве ответа.
Вот общий подход, основанный на тестировании. Не такой "эффективный", как большинство предложенных до сих пор решений, но ясный, проверенный, работающий и обобщенный.
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;
}
}
boolean atLeastTwo(boolean a, boolean b, boolean c)
{
return ((a && b) || (b && c) || (a && c));
}
Подведем итоги. Она называется булевой алгеброй не просто так:
0 x 0 = 0
1 x 0 = 0
1 x 1 = 1
0 + 0 = 0
1 + 0 = 1
1 + 1 = 0 (+ carry)
Если вы посмотрите на таблицы истинности, то увидите, что умножение - это булево and, а простое сложение - xor.
Чтобы ответить на ваш вопрос:
return (a + b + c) >= 2
Это действительно зависит от того, что вы подразумеваете под «улучшенным»:
Четче?
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.
Какой из них «улучшен» сильно зависит от ситуации.
Наиболее очевидный набор улучшений:
// 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));
}
Но эти улучшения незначительны.
Вот еще одна реализация с использованием map / reduce. Это хорошо масштабируется до миллиардов логических © в распределенной среде. Использование MongoDB:
Создание базы данных значения
логических значений:
db.values.insert({value: true});
db.values.insert({value: false});
db.values.insert({value: true});
Создание карты, функции сокращения:
Изменить : мне нравится ответ CurtainDog о том, что map / reduce применяются к общим спискам, поэтому здесь идет функция карты, которая принимает обратный вызов, который определяет, следует ли подсчитывать значение или нет.
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;
}
Взяв ответы (пока) здесь:
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
}
Вы можете видеть, что?: немного лучше, чем исправленная версия вашего оригинала. Лучше всего тот, который вообще избегает ветвления. Это хорошо с точки зрения меньшего количества инструкций (в большинстве случаев) и лучше для частей прогнозирования ветвления ЦП, поскольку неправильное предположение при прогнозировании ветвления может вызвать остановку ЦП.
Я бы сказал, что самый эффективный из них - от лунной тени в целом. В среднем он использует наименьшее количество инструкций и снижает вероятность остановки конвейера в ЦП.
Чтобы быть на 100% уверенным, вам нужно будет узнать стоимость (в циклах ЦП) для каждой инструкции, которая, к сожалению, не всегда доступна (вам нужно будет посмотреть в источнике точки доступа, а затем в спецификациях поставщиков ЦП. за время, затраченное на каждую сгенерированную инструкцию).
См. Обновленный ответ Rotsor для анализа кода во время выполнения.
Не думаю, что я видел это решение:
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;
}
Его преимущество в том, что, когда оно достигает числа, которое вы ищете, оно ломается. Так что, если это было «по крайней мере 2 из 1000000 истинных значений», а первые два на самом деле верны, то это должно происходить быстрее, чем некоторые из более «нормальных» решений.
Еще один способ сделать это, но не очень хороший:
return (Boolean.valueOf(a).hashCode() + Boolean.valueOf(b).hashCode() + Boolean.valueOf(c).hashCode()) < 3705);
Значения хэш-кода Boolean
фиксированы на 1231 для true и 1237 для false, поэтому можно было бы использовать <= 3699
Решение на C.
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;
}
In Ruby:
[a, b, c].count { |x| x } >= 2
Который может быть запущен в JRuby на JavaVM. ;-)
Просто ради использования XOR для решения относительно простой задачи ...
return a ^ b ? c : a
В Clojure :
(defn at-least [n & bools]
(>= (count (filter true? bools)) n)
Использование:
(at-least 2 true false true)
Он, вероятно, не ищет чего-либо запутанного, например, операторов побитового сравнения (обычно не запутанных, но с логическими значениями, чрезвычайно странно использовать побитовые операторы) или чего-то очень окольного, например, преобразования в int и их суммирования вверх.
Самый прямой и естественный способ решить эту проблему - использовать такое выражение:
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;
}
Слишком много способов сделать это ...
Лучшим ответом на вопрос должно быть: «Как сотрудник, мне важно писать так, чтобы мой смысл был ясен, сохраняя при этом эффективность там, где это необходимо для производительности». Вот как я бы это написал:
function atLeastTwoAreTrue(a, b, c) {
return (a && b) || (b && c) || (a && c);
}
На самом деле тест настолько продуман, что написание метода, который является самым быстрым и наиболее загадочным из возможных, является идеальным приемлемым вариантом, если вы дополните его простым комментарием. Но в целом в этом однострочном мире нам нужен более читаемый код в этом мире. : -)