Основные понятия булевой логики.
- Основные понятия булевой логики.
Основные логические операции
Все логические операции в контексте C++ применимы исключительно для значений с логическим типом данных —bool
. В
случае, когда в условии или логическом выражении оказывается тип данных отличный от логического, язык будет пытаться
приводить используемый тип к bool
(иногда успешно, а иногда нет).
NOT
Логическая операция НЕ
представима в виде оператора !
. Эта операция “отрицает” значение которое вы ей передаёте.
Таблица истинности будет выглядеть подобным образом:
X | !X |
---|---|
0 | 1 |
1 | 0 |
Рассмотрим фрагмент кода с примерами работы:
cout << (!true); //false
cout << (!(!true));//true
cout << (!(10 > 5));//false
AND
Логическая операция И
представима в виде оператора &&
. Для этой операции существует единственный набор входных
параметров, при котором она принимает значение ИСТИНА
. Это набор, в котором оба параметра истинны.
Таблица истинности будет выглядеть подобным образом:
X | Y | X && Y |
---|---|---|
0 | 0 | 0 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
Рассмотрим фрагмент кода с примерами работы:
cout << (true && false); //false
cout << (true && !false);//true
cout << ((10 > 5) && 1);//true
cout << ((x > 5) && (x <= 5));//false
OR
Логическая операция ИЛИ
представима в виде оператора ||
. Для этой операции существует единственный набор входных
параметров, при котором она принимает значение ЛОЖЬ
. Это набор, в котором оба параметра ложны.
Таблица истинности будет выглядеть подобным образом:
X | Y | X || Y |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 1 |
Рассмотрим фрагмент кода с примерами работы:
cout << (true || false); //true
cout << (true || !false);//true
cout << (!(10 > 5) || 0);//false
cout << ((x > 5) || (x <= 5));// true
Импликация
В C++ на примитивном уровне не реализована, но легко заменяется с помощью выражения !X OR Y
. Эта операция выполняет
функцию операции следовательно
. Из X
следует Y
. Если верен X
, то верен должен быть и Y
. Если X
ложен, то Y
может быть любым.
Из истины может следовать только истина, а из лжи всё что угодно.
Таблица истинности будет выглядеть подобным образом: |X|Y|X -> Y| |:—:|:—:|:—:| |0|0|1| |0|1|1| |1|0|0| |1|1|1|
Equals
Логическая операция РАВНО
представима в виде оператора ==
. Суть операции в совпадении значений двух параметров,
которые в неё передаются. Отрицание эквивалентности можно записать как !=
.
Таблица истинности будет выглядеть подобным образом:
X | Y | X == Y |
---|---|---|
0 | 0 | 1 |
0 | 1 | 0 |
1 | 0 | 0 |
1 | 1 | 1 |
Рассмотрим фрагмент кода с примерами работы:
cout << (true == false); //false
cout << (true == !false);//true
cout << ((10 > 5) == 1);//true
cout << ((x > 5) != (x <= 5));//true
XOR
Логическая операция ИСКЛЮЧАЮЩЕЕ ИЛИ
представима в виде оператора ^
. Для корректного использования этой операции в
логических выражениях нужно использовать только логические типы или приведение. Суть операции в том, что значение
только одного параметра может быть истинным.
Таблица истинности будет выглядеть подобным образом:
X | Y | X ^ Y |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
Рассмотрим фрагмент кода с примерами работы:
cout << (true ^ false); //true
cout << (true ^ !false);//false
cout << ((10 > 5) ^ bool(25));//false
cout << ((x > 5) ^ (x <= 5));//true
Булевы тождества и их проверка. Таблицы истинности.
При написании собственных булевых тождеств или функций необходимо делать проверку. Её осуществляют с помощью таблицы истинности. Такая таблица содержит в себе все возможные значения параметров и функции/тождества. Рассмотрим таблицу для функции $F = X_1andX_2orX_3$
X1 | X2 | X3 | F |
---|---|---|---|
0 | 0 | 0 | 0 |
0 | 0 | 1 | 1 |
0 | 1 | 0 | 0 |
0 | 1 | 1 | 1 |
1 | 0 | 0 | 0 |
1 | 0 | 1 | 1 |
1 | 1 | 0 | 1 |
1 | 1 | 1 | 1 |
Законы булевой логики (свойства логических операций).
Основными законами считаются:
- Закон двойного отрицания
!(!A) == A
- Коммутативность
A (|| &&) B == B (|| &&) A
- Ассоциативность
A (|| &&) (B (|| &&) C) == (A (|| &&) B) (|| &&) C
- Дистрибутивность
A || (B && C) == (A && B) || (A && C)
- Правило де Моргана
!A (&& ||) !B == !(A (|| &&) B)
- и т. д.
Будьте ВНИМАТЕЛЬНЫ при составлении и упрощении ваших условий.
Представление целых чисел в памяти компьютера.
Большинство современных компьютеров используют двоичную логику и производят вычисления в двоичной системе счисления.
Типичное число в двоичной системе выглядит так: 1110 1111
.
Перевод из двоичной в десятичную и обратно выполняется очень просто.
Значение при переводе вычисляется по формуле: $ 2^{i}{k_i} $, где k – значение бита, а i – позиция бита в числе.
Позиция бита | Значение бита | Значение при переводе |
---|---|---|
0 | 1 | 1 |
1 | 1 | 2 |
2 | 1 | 4 |
3 | 1 | 8 |
4 | 0 | 0 |
5 | 1 | 32 |
6 | 1 | 64 |
7 | 1 | 128 |
Значение числа $111011112=1+2+4+8+0+64+128 =239{10}$
Переполнение
У каждой переменной будь она целочисленная или с плавающей точкой есть память, которую она занимает. Метод хранения и объем этой памяти напрямую влияют на то какие числа туда можно помещать.
Допустим, что в ячейке размером 4 бита лежит число 15 (максимально возможное). $1111_2$
Если мы попробуем прибавить к этому числу 1, то по логике должны получить 16, но т.к. на хранение выделено только 4 бита, мы получаем 0. $0|1111_2+1=1|0000_2$
Битовые операции над целыми числами и их применения.
Набор битовых операция будет очень сильно напоминать набор логических. Разница лишь в том, что битовые операции можно применять к целым числам.
- AND - &
- OR - |
- NOT - ~
- XOR - ^
- Сдвиг влево «
- Сдвиг вправо »
При использовании все эти операторы будут возвращать число, полученное побитовым применением соответствующей логической операции к битам параметров.
Например:
cout << (255 & 1);//1
cout << (255 | 0);//255
cout << (255 ^ 128);//127
cout << (~0);//-1
Сдвиг влево и сдвиг вправо
Сдвиг влево и сдвиг право — внезапно побитово сдвигают число влево или в право, тем самым умножая («) или
деля (») его на 2. Является “быстрым” целочисленным умножением/делением на 2.
Условный оператор.
Конструкции if и if else.
В языке С++ существуют конструкции, которые называются условиями.
Они записываются как if
:
if(true)
{
//код выполняется
}
if(false)
{
//код НЕ выполняется
}
Условие состоит из нескольких частей:
if(условие)
{
тело условия - часть программы, которая будет
выполняться исключительно при истинности условия.
}
Пример:
cin >> x;
if(x % 2 == 0)
{
cout << "X is even!";
}
else
{
cout << "X is odd!";
}
Часть программы заключенная между фигурными скобками { } называется БЛОКОМ КОДА или *ОБЛАСТЬЮ ВИДИМОСТИ
После блока условия можно воспользоваться конструкцией else
, блок кода, относящийся к нему, будет исполняться только
при условии, что условие в if
было ложным.
Таким же образом можно составлять достаточно длинные и сложные конструкции с else if
.
Например:
if(apple == "green")
{
cout << "This apple is GREEN!";
}
else if(apple == "red")/*Здесь можно было бы обойтись и без else,
но тогда программе бы пришлось выполнять много ненужных сравнений.*/
{
cout << "This apple is RED!";
}
else if (apple == "purple")
{
cout << "This apple is not an apple at all!";
}
else
{
cout << "It`s a banana >:D";
}
Вы можете ставить сколько угодно else if
после if
, но else
может быть только один и должен стоять на **
последнем месте**.
Вложенные условные операторы.
При необходимости обрабатывать длинные и сложные условия вы можете “вкладывать” условные конструкции друг в друга. Например:
if(shape == "sphere")
{
if(color == "blue")
{
cout << "It`s a whale!";
}
else if (color == "green")
{
cout << "It's a watermelon!";
}
}
else if (shape == "cube")
{
cout << "It`s a box!";
}
Объединение условий (and, or, not).
Так как в условие для if
можно записать любое логическое тождество, то для составления сложных условий нужно
пользоваться логическими операторами для того, что бы не сильно увеличивать вложенность и улучшить читабельность.
Запишем проверку Число Х делится на 3, на 5 или не делится на 10
.
Без составного условия:
if(x % 3 == 0)
{
if(x % 5 == 0)
{
//ваш код
}
else if(x % 10 != 0)
{
//ваш код
}
}
else if(x % 10 != 0)
{
//ваш код
}
С составным условием:
if(x % 3 == 0 && x % 5 == 0 || x % 10 != 0)
{
//ваш код
}
Вопросов “Зачем?” возникать не должно…
При проверке длинной “колбасы” из условий, разделённых с помощью операции ||
, программа прекратит проверку и
зайдёт в блок при первой встрече значения ИСТИНА
. Аналогично и для операции &&
и значения ЛОЖЬ
— программа **
прекратит расчёт** следующих условий, и вход выполнен не будет.
Пример:
if(true && (true||...) && false && ...)/*с помощью многоточий
показаны места, которые не будут рассчитаны*/
{
}
Оптимизация логических выражений компилятором (false and X, true or Y).
В случае, когда результат выражения в условии известен до запуска программы, компилятор может упростить всё условие или даже весь блок кода. Пример:
if(true || x < 10 && x < 93021 && false)
{
//всегда будет выполняться
}
Оператор Case.
Существует ещё одна конструкция, позволяющая обрабатывать условия.
switch (x)
{
case 1:
cout << "X == 1";
break;
case 2:
cout << "X == 2";
break;
case 3:
cout << "X == 3";
break;
default:
cout << "X != 1, 2, 3.";
};
В скобках у switch
указываем переменную, значения которой хотим обрабатывать. И дальше перечисляем необходимые
значения этой переменной в case
. default
будет выполняться, если ни одно условие не было выполнено.
Важно : если не ставить break;
после необходимых вам операций, то выполнение пойдёт просто “вниз” до
первого break;
.
x = 1;
switch(x)
{
case 1:
cout << 1;
case 2:
cout << 2;
break;
case 3:
cout << 3;
};
Вывод программы будет 12
.