Оптимизация метода логической проверки

Я написал метод, который проверяет, есть ли n номер true booleans присутствуют в boolean[]. Затем я улучшил его до формата var args, который выглядит следующим образом:

public static boolean checkNTrue(int n, boolean... args) {
    assert args.length > 0;
    int count = 0;
    for( boolean b : args ) {
        if(b)   count++;
        if( count > n )
            return false;
    }
    return ( count == n );
}

Я реализую его, чтобы проверить, если только один всех этих boolean s истинно, используя:

boolean result = checkNTrue(1, false, true, false, false);

Приведенный выше возвращает true, подтверждающий требуемый случай. Но что, если есть только два аргумента boolean? Простой XOR может вернуть правильный результат.

И в случае 3 booleans , return (a ^ b ^ c) ^ (a && b && c); отлично работает. Итак, мой вопрос:

Нужно ли мне писать отдельные методы для проверки в случае с 2 и 3 переменными?

Или этот метод будет работать нормально? Я приглашаю все виды критики с помощью downvotes и комментариев, но, пожалуйста, просветите меня! : -)

11 голосов | спросил Astrobleme 14 MarpmFri, 14 Mar 2014 19:34:04 +04002014-03-14T19:34:04+04:0007 2014, 19:34:04

5 ответов


11

Преждевременная оптимизация - плохая идея. Прежде чем пытаться оптимизировать что-либо, запустите профилировщик, чтобы определить, где ваши узкие места. Если вы не выполняете эту функцию в узком цикле, маловероятно, что эта функция будет узким местом.

Особые случаи, которые вы вводите, не предоставляются бесплатно:

  • Чем больше строк кода вы пишете, тем больше ошибок у вас будет статистически.
  • В ваших модульных тестах также должны быть рассмотрены особые случаи.
  • При проверке, применяются ли ваши особые случаи, немного накладных расходов.

Принимая во внимание все это, почти наверняка не стоит добавлять специальные случаи.


Утверждение assert args.length > 0 может быть неуместным. Учитывая этот код в вакууме, нет ничего, чтобы гарантировать, что это утверждение логически верно. Скорее, это проверка проверки времени выполнения. Ошибки валидации должны вызывать IllegalArgumentException. Они не должны быть ошибками утверждения. (Утверждения могут быть отключены, побеждая ваш чек.)

Если это был не общедоступный метод, и вы уверены, что вы написали свой код таким образом, чтобы на всех сайтах было по крайней мере два аргумента, тогда утверждение могло бы быть подходящим.

Вот философский вопрос: должен ли checkNTrue(0) be true или false? Я думаю, что это должно быть true.

ответил 200_success 14 MarpmFri, 14 Mar 2014 19:56:38 +04002014-03-14T19:56:38+04:0007 2014, 19:56:38
7
  1. Операторы

    assert могут быть отключены, поэтому я бы выбрал исключение:

    if (args.length < 1) {
        throw new IllegalArgumentException("must have at least one boolean parameter");
    }
    
  2. if(b)   count++;
    

    Я бы поместил оператор count++ в отдельную строку. Из Code Complete, 2nd Edition , p759:

      

    С заявлениями в своих строках, код читается сверху вниз, а не сверху вниз и слева направо. Когда вы ищете определенную строку кода, ваш глаз должен иметь возможность следить за левым полем кода. Он не должен опускаться в каждую строку только потому, что одна строка может содержать два утверждения.

  3. Я бы переименовал метод в checkExactlyNTrue для упрощения понимания. (Он ответил бы на мой первоначальный вопрос: что он делает, если у меня больше, чем n true)

  4.   

    Нужно ли писать отдельные методы для проверки в случае с 2 и 3 переменными?

    Если у вас нет конкретной причины, почему вы это сделаете? Будь проще. Метод в вопросе работает с двумя и тремя параметрами, я бы использовал это.

  5.   

    И в случае 3 булевых, возвращаем (a ^ b ^ c) ^ (a & b & c); отлично работает. Итак, мой вопрос:

    Это было бы очень трудно прочитать и понять, метод в вопросе лучше выражает цель /цель метода.

ответил palacsint 14 MarpmFri, 14 Mar 2014 20:00:37 +04002014-03-14T20:00:37+04:0008 2014, 20:00:37
4
if(b)   count++;
if( count > n )
    return false;

... можно улучшить как ...

if (b && ++count > n)
    return false;

... который использует «оценку короткого замыкания», поэтому материал после && не запускается, если b, если false, то есть не стоит проверять count, когда он не только был изменен. Я бы не поставил на оптимизатора, понимающего эти отношения достаточно хорошо, чтобы исключить дополнительную проверку в ситуациях !b.

ответил Tony D 15 MaramSat, 15 Mar 2014 03:04:52 +04002014-03-15T03:04:52+04:0003 2014, 03:04:52
2

На данный момент ваш код достаточно прост, так что довольно легко понять, что он делает, и убедиться, что это правильно. Одним из основных принципов программирования является Держите его простым .

Добавление особых случаев даст вам больше кода для поддержки и потенциального появления ошибок. Вероятно, это не то, что вам нужно.

Если вы хотите быть уверены, что ваша функция возвращает то же самое, что и xor, вы можете использовать это в своем модульном тесте ( вы собирались добавить модульные тесты, не так ли?)

Если ваши аргументы по добавлению особых случаев были выступлениями, я думаю, вы не должны слишком беспокоиться об этом, так как это соответствует ситуации, которая была бы очень быстрой в любом случае.

ответил Josay 14 MarpmFri, 14 Mar 2014 20:02:45 +04002014-03-14T20:02:45+04:0008 2014, 20:02:45
2

Если вы действительно хотите, чтобы это было быстро - как курение, бесстрашно, удивительно быстро - тогда выбросьте все, что вы зашли так далеко, и перепишите все это как реализация O (1) вместо реализации O (n).

Всегда помните первое правило оптимизации: Самый быстрый способ сделать что-то - это не делать этого. И вещь, которую вы хотите избежать, - это цикл через массив для подсчета значений. Доступ к памяти дорог, особенно если они приводят к промахам кэш-памяти L1 или L2 или ошибкам страницы VM.

Итак, вот что я сделал бы: Создайте класс-оболочку для булевого массива. Предоставляйте методы чтения и записи для доступа к элементам массива. Ведение значений c значений. Когда элемент установлен (например, значение true или false хранится в каком-либо индексе), сравните новое значение со старым значением. Если значение переходит от false к true, приращение c на 1; если значение переходит от истинного к ложному, декремент c на 1. Затем в методе, который проверяет, что n присутствуют истинные булевские значения, просто сравните n с c, и все готово. Чрезвычайно минимальные накладные расходы и чрезвычайно быстрая проверка без циклов.

ответил Todd Lehman 15 MaramSat, 15 Mar 2014 11:36:32 +04002014-03-15T11:36:32+04:0011 2014, 11:36:32

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132