C ++. Логическое выражение - PullRequest
0 голосов
/ 27 мая 2020

Пытаюсь решить следующую задачу. Учитывая целое число x, я хочу вычислить 2 числа (a и b), которые:

  1. 1) делятся на 2 или 3 (например,% 2 == 0 или% 3 == 0 )
  2. 2) x = a + b

Я написал следующий код, чтобы найти такие a и b.

cin >> x;
if (x % 2 == 0) { 
     a = b = x / 2;
} else {
    a = x / 2;
    b = a + 1;
}
while((a % 3 !=0 || a % 2 != 0)  && (b % 2 != 0 || b % 3 != 0)) {
    a++;
    b--;
}

Однако это не работает . Например, когда x равно 13, выводится, что a = 6 и b = 7. Но 7 не делится на 2 или 3. В чем проблема?

1 Ответ

3 голосов
/ 27 мая 2020

Внимательно изучите ваше условие продолжения (где n - произвольное целое число, возможно, разное в каждом случае использования, поэтому, например, a != 3n просто означает, что a не делится на три). Я покажу процесс:

while((a % 3 != 0 || a % 2 != 0)  && (b % 2 != 0 || b % 3 != 0))
      ( a != 3n   OR  a != 2n  ) AND ( b != 2n   OR   b != 3n )
      (        a != 6n         ) AND (        b != 6n         )

Он говорит: продолжайте, пока оба a не делятся на два, и три, и b не кратно двум и трем. Другими словами, он будет продолжаться только в том случае, если оба a и b кратны шести. С другой стороны, он, конечно, выйдет, если a или b не делится на шесть.

Поскольку входное значение 13 устанавливает a = 6 и b = 7, случай продолжения ложен на первой итерации (семь не кратно шести).

Возможно, было бы лучше переосмыслить способ определения допустимости определенных числовых комбинаций (а) . Например (предполагая, что числа должны быть от 1 до N - 1, поскольку в противном случае ваше пространство решений, вероятно, будет бесконечным), вы можете использовать что-то вроде:

#include <iostream>

int main() {
    // Get the number.

    int num;
    std::cout << "Number? ";
    std::cin >> num;

    // Check all i + j = n for 1 <= i,j < n.

    for (int i = 1, j = num - 1; i < j; ++i, --j) {
        // Disregard if either number not a multiple of 2 or 3.

        if ((i % 2) != 0 && (i % 3) != 0) continue;
        if ((j % 2) != 0 && (j % 3) != 0) continue;

        std::cout << num << " => " << i << ", " << j << "\n";
        return 0;
    }

    std::cout << num << " => no solution\n";
    return 0;
}

Примечание. Я использую i < j как условие продолжения for, предполагая, что они должны быть отдельными числами. Если им разрешено использовать тот же номер , измените его на i <= j.


(a) Использование всех and, or и not (даже неявно, путем изменения условий продолжения и выхода) иногда доставляет больше проблем, чем оно того стоит, поскольку теоремы Де Моргана, как правило, вступают в игру:

_____     _   _
A ∩ B  ⇔  A ∪ B  : (not(A and B))  is  ((not A) or  (not B))
_____     _   _
A ∪ B  ⇔  A ∩ B  : (not(A or  B))  is  ((not A) and (not B))

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


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

Это потому, что, кроме патологических случаев, суммы меньше пяти (или меньше четырех, если решение позволяет имеют одинаковые числа):

  • каждое четное число 2n, n > 1 является суммой 2 и 2n - 2, обоих кратных двум (2n - 2 = 2(n - 1)); и
  • каждое нечетное число 2n + 1, n > 2 представляет собой сумму 3 и 2n + 1 - 3, первое из которых кратно трем, а второе - кратному двум (2n + 1 - 3 = 2n - 2 = 2(n - 1)).

Итак, на самом деле l oop не требуется:

if (num < 5) { // 4 if allowing duplicates.
    std::cout << num << " => no solution\n";
} else {
    int first = (num % 2) == 0 ? 2 : 3;
    std::cout << num << " => " << first << ", " << (num - first) << "\n";
}

Это на самом деле дает другой результат для некоторых чисел, например 17 = 2 + 15 = 3 + 14, но оба решения по-прежнему верны.

...