# ゼロ知識証明 [ゼロ知識証明 DEMO.ipynb - Colab](https://colab.research.google.com/drive/1LWOkY65HJ3oXVJGIZ1TSG0cINGa_HVON?usp=sharing) ```python questions = [ ("この数字の合計は奇数ですか?", lambda num: sum(map(int, str(num))) % 2 == 1), ("この数字の最後の桁は奇数ですか?", lambda num: int(str(num)[-1]) % 2 == 1), ("この数字を3で割ると余りが1になりますか?", lambda num: int(num) % 3 == 1), ("この数字のどこかに '5' が含まれますか?", lambda num: '5' in str(num)), ("この数字の最初の桁は5以上ですか?", lambda num: int(str(num)[0]) >= 5), ("この数字の2桁目と4桁目を足すと奇数ですか?", lambda num: (int(str(num)[1]) + int(str(num)[3])) % 2 == 1), ("この数字の桁の合計は10以上ですか?", lambda num: sum(map(int, str(num))) >= 10), ("この数字の桁のうち、偶数の桁はいくつありますか?", lambda num: sum(1 for d in str(num) if int(d) % 2 == 0)), ("この数字の桁のうち、一番大きい桁はいくつですか?", lambda num: max(map(int, str(num)))), ("この数字の桁を逆に並べたとき、最初の桁は奇数ですか?", lambda num: int(str(num)[::-1][0]) % 2 == 1), ("この数字を7で割ると余りが3になりますか?", lambda num: int(num) % 7 == 3), ("この数字のすべての桁は異なる数字ですか?", lambda num: len(set(str(num))) == len(str(num))), ("この数字に含まれる偶数の合計は5以上ですか?", lambda num: sum(int(d) for d in str(num) if int(d) % 2 == 0) >= 5), ("この数字の桁の平均は5以上ですか?", lambda num: sum(map(int, str(num))) / len(str(num)) >= 5), ("この数字の最初と最後の桁の差は3以上ですか?", lambda num: abs(int(str(num)[0]) - int(str(num)[-1])) >= 3), ("この数字の桁を昇順に並べると、最初の桁は偶数ですか?", lambda num: int(sorted(str(num))[0]) % 2 == 0), ("この数字の2番目の桁は最初の桁より大きいですか?", lambda num: int(str(num)[1]) > int(str(num)[0])), ("この数字の4桁目は2桁目より大きいですか?", lambda num: int(str(num)[3]) > int(str(num)[1])), ("この数字の各桁を2倍にした合計は20以上ですか?", lambda num: sum(map(lambda x: int(x) * 2, str(num))) >= 20), ("この数字のどこかに '8' が含まれますか?", lambda num: '8' in str(num)), ("この数字を5で割ると余りが0になりますか?", lambda num: int(num) % 5 == 0), ("この数字の最も小さい桁は3以上ですか?", lambda num: min(map(int, str(num))) >= 3), ("この数字を2で割ったときの余りは1ですか?", lambda num: int(num) % 2 == 1), ("この数字の最初の桁と最後の桁は同じですか?", lambda num: str(num)[0] == str(num)[-1]), ("この数字を4で割ると割り切れますか?", lambda num: int(num) % 4 == 0), ] ``` ```python import random class Server: def __init__(self, questions): self.questions = questions # 秘密の数字(0000〜9999の間でランダムに決定) secret_number = random.randint(1000, 9999) self.secret_number = str(secret_number).zfill(4) def ask_questions(self, num_questions=5): return random.sample(self.questions, num_questions) def check_answers(self, answers): for (question_text, check_func), a in answers.items(): correct_answer = check_func(self.secret_number) if a != correct_answer: return False # 1つでも矛盾があれば失敗 return True def __repr__(self): return f"秘密の数字: {self.secret_number})" ``` ```python class Client: def __init__(self, server): self.server = server def get_questions(self): return self.server.ask_questions() def challenge(self, challenge_number): questions = self.get_questions() answers = {} for q in questions: check_func = q[1] a = check_func(challenge_number) print(f"質問: {q[0]} → ユーザーの答え: {a}") answers[q] = a # challenge_number を Server に送ってない! is_valid = self.server.check_answers(answers) return is_valid ``` ```python server = Server(questions) client = Client(server) print(f"秘密の数字: {server.secret_number}(ユーザーには見えない)\n") is_valid = False while is_valid == False: challenge_number = input("ユーザーの数字を入力してください: ") if challenge_number.isnumeric() == False: print("数値を入力してください") exit() if len(challenge_number) != 4: print("4桁の数字を入力してください") exit() print() is_valid = client.challenge(challenge_number) if (is_valid): print("\n✅ ユーザーは秘密の数字を知っている!") else: print("\n🚨 ユーザーは秘密の数字を知らない!") ``` ``` 秘密の数字: 9239(ユーザーには見えない) ユーザーの数字を入力してください: 9999 質問: この数字の桁のうち、偶数の桁はいくつありますか? → ユーザーの答え: 0 質問: この数字を4で割ると割り切れますか? → ユーザーの答え: False 質問: この数字を2で割ったときの余りは1ですか? → ユーザーの答え: True 質問: この数字を5で割ると余りが0になりますか? → ユーザーの答え: False 質問: この数字の桁の合計は10以上ですか? → ユーザーの答え: True 🚨 ユーザーは秘密の数字を知らない! ユーザーの数字を入力してください: 9239 質問: この数字の最後の桁は奇数ですか? → ユーザーの答え: True 質問: この数字の桁を逆に並べたとき、最初の桁は奇数ですか? → ユーザーの答え: True 質問: この数字のどこかに '5' が含まれますか? → ユーザーの答え: False 質問: この数字の合計は奇数ですか? → ユーザーの答え: True 質問: この数字の最初と最後の桁の差は3以上ですか? → ユーザーの答え: False ✅ ユーザーは秘密の数字を知っている! ```