真偽とAND・OR・NOT|論理・アルゴリズム・情報の基礎

このページはプロモーション(広告)を含みます。

論理・アルゴリズム・情報の基礎(初級・読む25分+演習30分)

この授業のゴール

仕事でどう使うか

データを扱う仕事では、「どの行を対象にするか」を毎日のように決めます。その判断はほぼすべて、真(正しい)か偽(正しくない)かで答えられる条件の組み合わせです。条件の組み合わせ方を1つ間違えると、対象者が増えたり減ったりしますが、画面にはエラーが出ません。

路線 この授業の使いどころ
DS(データサイエンティスト) 分析対象の抽出条件(「有効な注文かつ期間内」)を作る。条件を取り違えると、集計の分母が変わり、結論そのものが変わる
ML(機械学習エンジニア) 学習データの除外ルール(「欠損またはテスト用のデータは除く」)を書く。ORとANDの取り違えで、学習データを大量に捨てる事故が起きる
DX(DX推進・AI企画) 業務ルールを「条件表」にして要件定義書に書く。「会員かつ3,000円以上は割引」のような文を、抜けなく表にして関係者と合意する
RS(研究者) 被験者の組み入れ条件・除外条件を文章で定義する。「または」が両方を含むかどうかで、対象者数が変わる

この授業のゴールは、日常語の「かつ」「または」「でない」を、1つの意味に決めて使えるようになることです。

前提

次の言葉と操作を使います。不安があれば先に戻ってください。

この授業のコードは、画面内の 実行 ボタンでそのまま動かせます。出力を見て、自分の予想と合っているかを毎回確認してください。

身近な状況:「会員なら割引」のはずが、割引されない

あるネットショップが、次のキャンペーンを始めました。

会員かつ購入額3,000円以上のお客さまは、10%割引

店長はこの文を、画面の設定担当者に伝えました。ところが翌日、次の問い合わせが来ました。

2つ目は、設定担当者が「かつ」を「または」と読み違えたために起きました。日常会話では「かつ」と「または」が曖昧に使われますが、システムはどちらか1つの意味でしか動きません。

まず、文の中の判断を2つに分けます。

判断 名前 答えは
会員である is_member 真(True)か偽(False)
購入額が3,000円以上である amount >= 3000 真(True)か偽(False)

2つの判断のそれぞれが真か偽かで、お客さまは4通りに分かれます。下のコードで、4人の例を確かめましょう。and は「両方が真」を意味する Python の書き方です(定義は後で説明します)。

# (会員か, 購入額) の4人
cases = [(True, 3000), (True, 2999), (False, 5000), (False, 100)]
for is_member, amount in cases:
    print(is_member, amount, '→', is_member and amount >= 3000)

対象になるのは、1人目だけです。2人目は金額が1円足りず、3人目は会員でなく、4人目はどちらも満たしません。

直感的な説明:ANDは「両方」、ORは「少なくとも片方」、NOTは「逆」

3つの言葉を、入口の改札にたとえます。

ここで大切なのは、ORの扱いです。日常会話の「紅茶またはコーヒー」は、ふつうどちらか1つを選ぶ意味ですが、論理のORは両方も含みます。「会員または3,000円以上」という条件では、会員で5,000円買った人も対象です。

注意:ORを「どちらか一方だけ」と説明している資料や会話に出会ったら、必ず「両方のときはどうなるか」を聞き返してください。両方のときの扱いを決めていない条件は、仕様として不完全です。「どちらか一方だけ」を表したいときは、排他的論理和(XOR:エックスオア)という別の名前があります。

たとえ話は、ここまでです。次の節で、記号と表を使って正確に定義します。

正確な定義:真理値表と、Pythonでの書き方

真偽値とは、真(True)と偽(False)の2種類だけを取る値です。2つの条件 $A$、$B$ を組み合わせる論理演算は、次の表で定義されます。この表を真理値表と呼びます。1行が「$A$ と $B$ の真偽の1通りの組み合わせ」を表します。

$A$ $B$ $A$ AND $B$ $A$ OR $B$ NOT $A$
偽 偽 偽 偽 真
偽 真 偽 真 真
真 偽 偽 真 偽
真 真 真 真 偽

太字の箇所が、ORとXORの結果が違う唯一の行です(XORは、この行が偽になります)。

Python では、AND は and、OR は or、NOT は not と書きます。戻り値の型は、条件が bool(真偽値の型)のとき bool です。次のコードは、上の表を Python 自身に作らせます。product は、リストの要素のすべての組み合わせを順に取り出す関数です。

from itertools import product
print('A     B     A and B  A or B  not A')
for a, b in product([False, True], repeat=2):
    print(f'{str(a):5} {str(b):5} {str(a and b):8} {str(a or b):7} {not a}')

次の3点も正確に覚えてください。

  1. 比較は真偽値を返す:amount >= 3000 は、amount が3000以上なら True、そうでなければ False という値になります。比較演算子は ==(等しい)、!=(等しくない)、<、<=、>、>= です。= は代入であり、等しいかの比較ではありません。
  2. 優先順位は not、and、or の順に強い:A or B and C は A or (B and C) と同じです。迷うときは必ず括弧 ( ) を付けます。
  3. ド・モルガンの法則:否定を内側に入れると、ANDとORが入れ替わります。

$$\text{NOT}(A \text{ AND } B) = (\text{NOT } A) \text{ OR } (\text{NOT } B)$$

$$\text{NOT}(A \text{ OR } B) = (\text{NOT } A) \text{ AND } (\text{NOT } B)$$

たとえば「会員かつ3,000円以上」の否定(割引されない人)は、「会員でない、または3,000円未満」です。「会員でなく、かつ3,000円未満」ではありません。会員でない5,000円のお客さまは、割引されない人に含まれるべきだからです。

段階付き例題:会員割引の条件を、境界を含めて全部確かめる

問題:割引ルール「会員かつ購入額3,000円以上」を、関数 is_target(is_member, amount) として書き、漏れなく検査してください。

ステップ1:入力と出力を決める。入力は、会員か(bool)と、購入額(円、整数 int)の2つ。出力は、割引対象か(bool)です。

ステップ2:式を書く。is_member and amount >= 3000。「以上」なので、3,000円ちょうども対象です。

ステップ3:検査ケースを作る。会員の真偽(2通り)と、金額(3通り)の組み合わせ、合計6ケースにします。金額は、境界の値である3,000円と、その前後の2,999円・3,001円にします。境界の値は、条件の結果が切り替わる場所なので、間違いが最も出やすい場所です。

ステップ4:出力を読む。

from itertools import product

def is_target(is_member, amount):
    return is_member and amount >= 3000

for is_member, amount in product([True, False], [2999, 3000, 3001]):
    print(f'会員={is_member!s:5} 金額={amount} 対象={is_target(is_member, amount)}')

出力を表にすると、次のとおりです。

会員 2,999円 3,000円 3,001円
会員 対象外 対象 対象
非会員 対象外 対象外 対象外

対象になるのは6ケース中2つです。もし >= を > と書き間違えると、会員の3,000円が対象外に変わります。3,000円ちょうどの行を検査に入れておかないと、この間違いは見つかりません。

ステップ5:意味を言葉に戻す。「非会員は金額にかかわらず対象外」「会員は3,000円から対象」という2つの文が、表と一致していれば、条件式はルールどおりに動いています。

操作して理解する:ANDをORに変えると、誰が増えるか

次のコードは、同じ6ケースに対して、ANDのルールとORのルールを並べて評価します。実行する前に、対象者の人数が6人中何人になるかを予想してください。

from itertools import product

rows = list(product([True, False], [2999, 3000, 3001]))
and_rule = [m and a >= 3000 for m, a in rows]
or_rule  = [m or a >= 3000 for m, a in rows]
print('AND対象数', sum(and_rule), '/', len(rows))
print('OR対象数 ', sum(or_rule), '/', len(rows))
for (m, a), x, y in zip(rows, and_rule, or_rule):
    if x != y:
        print('結果が違う行:', m, a, 'AND=', x, 'OR=', y)

予想と答え:

次に、自分で書き換えてみましょう。

  1. and_rule の式を m and a > 3000(>= を > に変更)にすると、AND対象数はいくつになりますか。
  2. 金額のリストに 0 を加えると、対象数はどうなりますか。
  3. 「会員でない、または3,000円未満」(割引されない人)の人数を、not (...) で全体を囲む書き方を使わずに書いてください。

答えは、(1) 1人、(2) AND対象数は2のまま(0円は金額の条件を満たさない)。OR対象数は、会員の0円が加わって5から6に増える(ORでは会員なら金額に関係なく対象)、(3) (not m) or a < 3000 で、6−2=4人です。

発展:短絡評価・3値論理・条件の数と検査ケースの数

短絡評価:Python の A and B は、A が偽ならその時点で結果が偽と決まるため、B を評価しません。A or B も、A が真なら B を評価しません。この性質により、x != 0 and 10 / x > 2 のように、左で安全を確認してから右を評価する書き方ができます。順番を入れ替えると、x が0のときにエラーになります。

3値論理:SQL では、値がない状態 NULL が加わるので、条件の結果が「真・偽・不明」の3種類になります。たとえば NULL = 1 は偽ではなく不明です。WHERE は「真」の行だけを残すため、不明の行は偽と同様に消えます。詳しくは NULLと3値論理 で扱います。この授業の真理値表は、NULL がない世界の話です。

組み合わせの数:独立した条件が $n$ 個あるとき、真偽の組み合わせは $2^n$ 通りです。3条件なら8通り、10条件なら1,024通りです。条件が増えるほど、全組み合わせを手で確認する費用が急に増えるので、実務では条件を分けたり、重要な境界に絞って検査したりします(計算量と小さい反例 で費用の増え方を学びます)。

代替手段:条件が多いときは、条件表(行=条件の組み合わせ、列=結果)を作って関係者と確認すると、ANDとORの食い違いが早く見つかります。この表は、条件と必要十分条件 で必要条件・十分条件を整理するときにも使います。

よくある失敗

失敗1:ORを「どちらか一方だけ」と読んでしまう

誤った読み:「会員または3,000円以上」なら、会員で5,000円のお客さまは、どちらも満たすので対象外。

is_member = True
big = True          # 3,000円以上である
print('OR:', is_member or big)
print('どちらか一方だけ(排他的論理和):', is_member != big)

論理のORは、両方が真のときも真です。「どちらか一方だけ」を表したいときは、2つの真偽値が異なることを調べる != を使います。

直し方:条件を文章で書くときは、「両方のときはどうなるか」を1行足して決めます。例:「会員または3,000円以上(両方満たす人も含む)」。

失敗2:`status == 'paid' or 'refunded'` と書いて、常に真になる

「状態が paid または refunded」を、日本語の語順のまま書いた例です。

status = 'cancelled'
wrong = status == 'paid' or 'refunded'
print(wrong)
print(bool(wrong))
right = status in ('paid', 'refunded')
print(right)

status == 'paid' は偽なので、or の右側 'refunded' が結果になります。空でない文字列は、条件の中では真として扱われるため、status が何であっても、この式は真として働きます。取消(cancelled)の注文まで、支払済みか返金済みの扱いになります。

直し方:比較は1つずつ書く(status == 'paid' or status == 'refunded')か、in で「候補の中にあるか」を調べます。右辺に文字列や数値を置くだけの書き方は、エラーも出ないので、特に危険です。

失敗3:NOTのかけ方と優先順位を取り違える

「割引されない人」を知りたいとき、not is_member and amount >= 3000 と書いてしまう例です。

is_member, amount = False, 100
print('誤:', not is_member and amount >= 3000)
print('正:', not (is_member and amount >= 3000))
print('正(ド・モルガン):', (not is_member) or amount < 3000)

not は and より先に評価されるので、誤った式は「会員でないかつ3,000円以上」(非会員の高額購入者)という別の条件になります。非会員で100円のお客さまは割引されない人ですが、誤った式では偽になり、見落とされます。

直し方:否定したい範囲を括弧で囲み、not (...) と書く。または、ド・モルガンの法則で、ANDをORに入れ替えて各条件を否定します。どちらの書き方でも同じ検査ケースの結果になることを、必ず確かめます。

まとめ

用語

真偽値(boolean)

真(True)と偽(False)の2種類だけを取る値。比較の結果として得られる。

論理演算(AND・OR・NOT)(logical operation)

真偽値を組み合わせる演算。ANDは両方が真のとき真、ORは少なくとも片方が真のとき真、NOTは真偽を逆にする。

真理値表(truth table)

条件の真偽のすべての組み合わせについて、結果を並べた表。

ド・モルガンの法則(De Morgan's laws)

否定を括弧の内側に入れると、ANDとORが入れ替わるという法則。

排他的論理和(XOR)(exclusive OR)

2つの条件のうち、ちょうど1つだけが真のときに真になる演算。両方が真のときは偽。

よくある質問

なぜ、条件を真偽の表にしてまで考える必要があるのですか?

日本語の「かつ」「または」「でない」は、場面によって意味がぶれるからです。表にすると、4通りすべてで結果が決まっているかを確認でき、決まっていない行(仕様の穴)が見つかります。コードを書く前の作業ほど、直す費用は小さくなります。

ORとXOR(排他的論理和)は、どう使い分けますか?

「少なくとも1つ満たせばよい」ならOR、「ちょうど1つだけ満たす」ならXORです。Python では、2つの bool に対して a != b がXORと同じ働きをします。実務の抽出条件は、ほとんどがORです。両方のときの扱いを、必ず仕様に書いてください。

条件式が複雑になったら、どう確認しますか?

条件を変数に分けて名前を付け(is_member、is_big)、真偽の組み合わせを表で全部試します。境界の値(3,000円ちょうどと前後)も加えます。結果を日本語に直して、仕様の文と一致するかを人が読み比べます。

条件を入れ替えると結果は変わりますか? `A and B` と `B and A` は同じですか?

真偽の結果は同じです。ただし Python は左から評価して短絡するため、x != 0 and 10 / x > 2 のように、右の式が左の成立を前提としているときは、順序でエラーの有無が変わります。値の結果が同じでも、評価の安全性は別の問題です。

SQL の `WHERE a AND b OR c` は、Python と同じ優先順位ですか?

ANDがORより先に評価される点は、SQLも同じです。ただし方言差を避けるため、実務では必ず括弧を付けます。また、SQLにはNULLによる「不明」があるため、真偽の2値だけでは足りません(NULLと3値論理)。

「3,000円以上」と「3,000円超」を、仕事の会話でどう区別しますか?

「以上」は3,000円を含み(>=)、「超」「より大きい」は含みません(>)。「未満」は含まず(<)、「以下」は含みます(<=)。会話では、必ず「3,000円ちょうどはどちらか」を確認し、検査ケースに3,000円を入れます。

確認問題

問1 A が真、B が真のとき、A or B の結果はどれか。

  1. ① 偽
  2. ② 真
  3. ③ エラーになる
  4. ④ どちらか一方だけなので判定できない
解答と解説

正解:②

論理のORは「少なくとも片方が真」なので、両方が真のときも真です。

問2 ルール「会員かつ購入額3,000円以上」で、会員が2,999円を購入した。割引の扱いはどれか。

  1. ① 対象外
  2. ② 対象
  3. ③ 会員なので金額に関係なく対象
  4. ④ 非会員の情報がないと判定できない
解答と解説

正解:①

ANDは両方が必要です。金額の条件が偽なので、結果は偽(対象外)です。

問3 「会員かつ3,000円以上」の否定(割引されない人)と同じ意味の条件はどれか。

  1. ① 会員でない、かつ3,000円未満
  2. ② 会員でない、または3,000円以上
  3. ③ 会員である、かつ3,000円未満
  4. ④ 会員でない、または3,000円未満
解答と解説

正解:④

ド・モルガンの法則により、ANDの否定は「各条件を否定してORでつなぐ」です。

問4 status = 'cancelled' のとき、status == 'paid' or 'refunded' はどうなるか。

  1. ① False になり、期待どおりに動く
  2. ② エラーになる
  3. ③ 真として働き、期待に反して動く
  4. ④ None になる
解答と解説

正解:③

orの左が偽なので右の 'refunded' が結果になり、空でない文字列は真として扱われます。

問5 独立した条件が3つあるとき、真偽の組み合わせは何通りか。

  1. ① 6通り
  2. ② 8通り
  3. ③ 9通り
  4. ④ 3通り
解答と解説

正解:②

各条件が真か偽の2通りで、3つ分なので 2×2×2=8通りです。

問6 Python で True or False and False の結果はどれか。

  1. ① False
  2. ② None
  3. ③ True
  4. ④ エラーになる
解答と解説

正解:③

and が or より先に評価されるので、True or (False and False) となり、True or False で真です。

次に学ぶこと

次は 条件と必要十分条件 で、「条件を満たせば結論が成り立つ」とはどういうことか(必要条件と十分条件)を学びます。この授業の真理値表を、そのまま使います。

if文の書き方は 「ifと条件式」(有料版) で扱います。この授業の確認問題だけでモジュールの習得とはしません。ON06 の総合テストと、会員割引の条件を表で検証する自力課題で判定します。

キャリア学習:データサイエンティスト・機械学習エンジニア・DX推進・研究者を目指す人の学習アプリ。164モジュール・984授業を、ブラウザで動くSQLとPythonの演習で。基礎と各分野の最初のモジュールは無料。全部は2,980円の買い切り。

アプリを無料で開く有料版と無料版の違い