パリティチェック×美容アプリケーション

こんばんは、Yuinaです。

最近、資格の勉強をしていてパリティビットやパリティチェックなどの言葉が出てきました。

イメージを掴むために、美容アプリをテーマにしてプログラムを書いてみました。

よかったらご覧ください。

パリティチェックが使える場面

パリティチェックが使える場面は、何かの「データ」が送受信される場合です。

機能例:

①ユーザー設定の保存・読み込み ユーザーの肌悩みや製品の評価など、設定データをサーバーと同期する際に、データが途中で破損していないか確認する。

②製品情報のダウンロード 美容液の最新情報(成分、価格など)をサーバーから取得する際、データにエラーがないことを保証する。

③処方データの共有 AIが生成した美容液の処方データを、アプリから工場に送信する際、データの正確性を保証する。

今回のアプリへの組み込み設計

①データの数値化 ユーザーの評価や選択を、パリティチェックが与えるビット列に変換するルールを定義。

②パリティビットの追加 サーバーへの送信前に、変換したデータにパリティビットを追加。

③サーバー側のチェック サーバー側でデータを受信した際、パリティチェックを実行し、エラーが検出されたら再送を要求する。

コード一覧

パリティチェックを管理するクラス

class ParityCheck:

def __init__(self,data_length=8):
self.data_length = data_length

# 偶数パリティビットを付加するメソッド
def add_even_parity(self,data):
# bin関数を使い、入力された整数dataを2進数に変換し、
# その中の1の数を数える
one_count = bin(data).count('1')
# 数えた1の数が偶数か奇数かを判定
parity_bit = one_count % 2

# 決定したパリティビットを元のデータの最上位ビットに追加
# |は論理和演算子
return (parity_bit << self.data_length) | data

# パリティチェック
def check_even_parity(self, data_with_parity):
# bin関数を使い、入力された整数dataを2進数に変換し、
# その中の1の数を数える
one_count = bin(data_with_parity).count('1')
print(f" {type(data_with_parity)}")
# カウントした1の数が偶数であればTrue
return one_count % 2 == 0

アプリのデータを処理するクラス

class BeautyApp:
# 初期化
def __init__(self):
self.parity_check = ParityCheck(data_length=4) # 評価項目は4つと仮定

# ユーザーの評価をビット列に変換
def encode_bit(self, ratings):
encoded_data = 0
# 評価項目を順番に処理
target_keys = ['保湿', '美白', 'ハリ', '敏感肌']

for i, key in enumerate(target_keys):
if ratings.get(key) == 1:
encoded_data |= (1 << i) # 後ほど詳しく解説

return encoded_data

# パリティビットを付加する
def add_parityBit(self, data):
data_with_parity = self.parity_check.add_even_parity(data)

# パリティビット有り無しを比較
print(f"送信データ(オリジナル): {bin(data)}")
print(f"送信データ(パリティ付き): {bin(data_with_parity)}")

return data_with_parity

# 送られてきたデータの整合性を見る
def result_parity_check(self, data_with_parity):
is_ok = self.parity_check.check_even_parity(data_with_parity)
print(f"受信データ: {bin(data_with_parity)}")
print(f"データチェック結果: {'正常' if is_ok else 'エラー'}")

return is_ok

# アプリの実行
beauty_app = BeautyApp()

# ユーザーが入力した評価
user_input = {
'保湿': 1,
'美白': 1,
'ハリ': 0,
'敏感肌': 0
}

# encode_bitメソッドを呼び出す(ユーザーが入力した評価をビット列に変換)
user_input = beauty_app.encode_bit(user_input) # 結果: 0b0011

# add_parityBitメソッドを呼び出す(パリティビットを付加)
print("--- ユーザー評価の送信シミュレーション ---")
sent_data = beauty_app.add_parityBit(user_input)

# result_parity_checkメソッドを呼び出す(サーバーがデータを受信し、チェックする)
print("\n--- サーバー側での受信シミュレーション ---")
beauty_app.result_parity_check(sent_data)

# result_parity_checkメソッドを呼び出す(意図的にデータを破損させる)
print("\n--- データ破損のシミュレーション ---")
corrupted_data = sent_data ^ (1 << 0) # 0番目のビットを1にする
beauty_app.result_parity_check(corrupted_data)

解説

では、まずBeautyAppから見ていきましょう。

encode_bitメソッド

  # ユーザーの評価をビット列に変換
def encode_bit(self, ratings):
encoded_data = 0
# 評価項目を順番に処理
target_keys = ['保湿', '美白', 'ハリ', '敏感肌']

for i, key in enumerate(target_keys):
if ratings.get(key) == 1:
encoded_data |= (1 << i) # 論理和

return encoded_data

target_keysは、メソッドによって4ビットの整数に変換されます。

  • '保湿'が1なので、0番目のビットが1になります。
  • '美白'が1なので、1番目のビットが1になります。
  • 'ハリ''敏感肌'は0なので、2番目と3番目のビットは0のままです。

結果として、ユーザーの評価は2進数で0011、10進数で3になります。

user_input = 0b0011 (10進数で3)


add_parityBitメソッド

  # パリティビットを付加する
def add_parityBit(self, data):
data_with_parity = self.parity_check.add_even_parity(data)

# パリティビット有り無しを比較
print(f"送信データ(オリジナル): {bin(data)}")
print(f"送信データ(パリティ付き): {bin(data_with_parity)}")

return data_with_parity

ここでは、データにパリティビットが付加されます。

  1. 1の数を数える:
    • bin(user_input).count('1')bin(0b0011).count('1')
    • '0b0011'の中の1の数は2です。
  2. パリティビットを決定する:
    • one_count % 22 % 20
    • 1の数が偶数なので、偶数パリティを保つためにパリティビットは0になります。
  3. パリティビットをデータに付加する:
    • data_lengthは4です。
    • parity_bit << self.data_length0 << 400000
    • | data00000 | 001100011

この結果、送信されるデータsent_dataは2進数で00011となります。先頭の0がパリティビットです。


result_parity_checkメソッド

# 送られてきたデータの整合性を見る
def result_parity_check(self, data_with_parity):
is_ok = self.parity_check.check_even_parity(data_with_parity)
print(f"受信データ: {bin(data_with_parity)}")
print(f"データチェック結果: {'正常' if is_ok else 'エラー'}")

return is_ok

受信側では、送られてきたデータ00011の整合性をチェックします。

  1. 1の数を数える:
    • bin(sent_data).count('1')bin(0b00011).count('1')
    • '0b00011'の中の1の数は2です。
  2. 偶数か奇数かを判定する:
    • one_count % 2 == 02 % 2 == 0True
    • 1の数が偶数なので、データは正常と判断されます

破損したデータのパリティチェック

print("\n--- データ破損のシミュレーション ---")

# データ転送中に1ビット反転したと仮定

corrupted_data = sent_data ^ (1 << 0) # 末尾のビットを反転

app_processor.receive_data_from_server(corrupted_data)

意図的にデータを破損させて、パリティチェックがどのように機能するかを見てみます。

corrupted_data = sent_data ^ (1 << 0)

sent_data = 00011

1 << 0 = 00001 (0番目のビットを1にする)

 XOR演算 (^)は、同じビットなら0、異なるビットなら1を返します。

00011

00001

-----

00010

corrupted_dataは2進数で00010となります。

  1. 1の数を数える:
    • bin(corrupted_data).count('1')bin(0b00010).count('1')
    • '0b00010'の中の1の数は1です。
  2. 偶数か奇数かを判定する:
    • one_count % 2 == 01 % 2 == 0False
    • 1の数が奇数なので、データにエラーが発生したと判断されます。

このように、たった1ビットの変化でも、パリティチェックによってエラーが検出されるのがわかります。


続いて、ParityCheckクラスも見ていきましょう。

add_even_parity メソッド

# 偶数パリティビットを付加するメソッド
def add_even_parity(self,data):
# bin関数を使い、入力された整数dataを2進数に変換し、
# その中の1の数を数える
one_count = bin(data).count('1')
# 数えた1の数が偶数か奇数かを判定
parity_bit = one_count % 2

このメソッドは、データに偶数パリティビットを付加します。

  • bin(data).count('1'): 入力された整数データを2進数文字列に変換し、その中の1の数を数えます。これがパリティチェックの基準となります。
  • parity_bit = one_count % 2: 1の数が偶数(one_count % 20)であればパリティビットは0、奇数(one_count % 21)であればパリティビットは1に決定されます。これにより、パリティビットを含めた全体の1の数が必ず偶数になります。
  • (parity_bit << self.data_length) | data: このビット演算によって、パリティビットが元のデータの先頭に追加されます。
    • <<(左シフト)演算子で、パリティビットをデータ長分だけ左にずらします。
    • |(ビットごとの論理和)演算子で、ずらしたパリティビットと元のデータを結合します。

check_even_parity メソッド

# パリティチェック
def check_even_parity(self, data_with_parity):
# bin関数を使い、入力された整数dataを2進数に変換し、
# その中の1の数を数える
one_count = bin(data_with_parity).count('1')
print(f" {type(data_with_parity)}")
# カウントした1の数が偶数であればTrue
return one_count % 2 == 0

このメソッドは、パリティビットが付加されたデータの整合性をチェックします。

  • bin(data_with_parity).count('1'): 受信したデータ(パリティビットを含む)の全体で1の数を数えます。
  • one_count % 2 == 0: 全体の1の数が偶数であればTrueを返します。これは、データが正しく送信されたことを示します。もし1ビットでも反転して1の数が奇数になっていれば、Falseを返し、「エラー」と判断します。

実行結果

実行結果は上のようになりました。

ありがとうございました✨

タイトルとURLをコピーしました