# ゼロ知識証明
[ゼロ知識証明 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
✅ ユーザーは秘密の数字を知っている!
```