mirror of
https://github.com/InternLM/InternBootcamp.git
synced 2026-04-19 12:58:04 +00:00
init-commit
This commit is contained in:
commit
18a552597a
3461 changed files with 1150579 additions and 0 deletions
261
internbootcamp/libs/cryptomath/crypto_math.py
Executable file
261
internbootcamp/libs/cryptomath/crypto_math.py
Executable file
|
|
@ -0,0 +1,261 @@
|
|||
import random
|
||||
import string
|
||||
|
||||
def generate_crypto_math(num_letters, num_puzzles, num_add=2):
|
||||
"""
|
||||
生成 Crypto-math puzzle 对的函数,可指定加号数量 (num_add)。
|
||||
|
||||
:param num_letters: int
|
||||
表示题目中所有字母的数量(即所用到的独立字母总数)。
|
||||
:param num_puzzles: int
|
||||
需要生成的 puzzle 对(question-answer 对)的数量。
|
||||
:param num_add: int
|
||||
题目里的加号数量,即加数个数。例如:
|
||||
- num_add=2 -> X + Y = Z
|
||||
- num_add=3 -> X + Y + W = Z
|
||||
...
|
||||
:return: list
|
||||
返回一个列表,列表元素为 {"question": question_str, "answer": answer_str} 形式的字典。
|
||||
"""
|
||||
# 若所需字母数超过可用数字(0-9)数量,直接返回空列表
|
||||
if num_letters > 10:
|
||||
return []
|
||||
|
||||
results = []
|
||||
seen_questions = set() # 用于去重
|
||||
|
||||
while len(results) < num_puzzles:
|
||||
# 1. 随机生成 num_add 个加数
|
||||
addends = [random.randint(1, 999) for _ in range(num_add)]
|
||||
Z = sum(addends) # 结果
|
||||
|
||||
# 2. 提取所有数字
|
||||
union_digits = set()
|
||||
for val in addends:
|
||||
union_digits |= set(str(val))
|
||||
union_digits |= set(str(Z))
|
||||
|
||||
# 如果数字种类不等于所需字母数,则跳过
|
||||
if len(union_digits) != num_letters:
|
||||
continue
|
||||
|
||||
# 3. 将数字随机映射到字母
|
||||
# - 随机选出 num_letters 个字母
|
||||
letters_pool = random.sample(string.ascii_uppercase, num_letters)
|
||||
union_digits_list = list(union_digits)
|
||||
random.shuffle(union_digits_list)
|
||||
|
||||
digit_to_letter = {}
|
||||
for i, d in enumerate(union_digits_list):
|
||||
digit_to_letter[d] = letters_pool[i]
|
||||
|
||||
# 4. 构造 question
|
||||
def num_to_letters(num):
|
||||
"""将整数 num 转为对应的字母串"""
|
||||
return ''.join(digit_to_letter[d] for d in str(num))
|
||||
|
||||
# 拼接加数部分 (e.g. "AB + CD + EF")
|
||||
addends_str = "+".join(num_to_letters(val) for val in addends)
|
||||
question_str = f"{addends_str}={num_to_letters(Z)}"
|
||||
|
||||
# 去重检测
|
||||
if question_str in seen_questions:
|
||||
continue
|
||||
|
||||
# 5. 构造 answer(字母->数字)
|
||||
letter_to_digit = {v: k for k, v in digit_to_letter.items()}
|
||||
sorted_letters = sorted(letter_to_digit.keys()) # 按字母排序
|
||||
answer_str = "[[" + ",".join(f"{letter}={letter_to_digit[letter]}"
|
||||
for letter in sorted_letters) + "]]"
|
||||
|
||||
puzzle_dict = {
|
||||
"puzzle": question_str,
|
||||
"target": answer_str,
|
||||
"num_add": num_add,
|
||||
"num_letters": num_letters
|
||||
}
|
||||
|
||||
# 记录并保存
|
||||
seen_questions.add(question_str)
|
||||
results.append(puzzle_dict)
|
||||
|
||||
return results
|
||||
|
||||
|
||||
# # ============= 测试示例 =============
|
||||
# if __name__ == "__main__":
|
||||
# # 生成 3 道 puzzle,每道 puzzle 中有 3 个加数(num_add=3),并且题目中恰好有 4 个独立字母(num_letters=4)
|
||||
# puzzles = generate_crypto_math(num_letters=10, num_puzzles=10, num_add=20)
|
||||
# for idx, p in enumerate(puzzles, start=1):
|
||||
# print(f"Puzzle {idx}:")
|
||||
# question = p["puzzle"]
|
||||
# answer = p["target"]
|
||||
# print(" Question:", question)
|
||||
# print(" Answer: ", answer)
|
||||
# answer = {item.split('=')[0]: int(item.split('=')[1]) for item in answer[2:-2].split(",")}
|
||||
# for letter, digit in answer.items():
|
||||
# question = question.replace(letter, str(digit))
|
||||
# print(" Replaced question:", question)
|
||||
# print(p)
|
||||
# try:
|
||||
# eval_res = eval(question.replace('=', '=='))
|
||||
# except:
|
||||
# print(" Eval failed!")
|
||||
# eval_res = False
|
||||
# if not eval_res:
|
||||
# print(" Check failed!")
|
||||
# break
|
||||
# print()
|
||||
|
||||
|
||||
|
||||
|
||||
import itertools
|
||||
|
||||
|
||||
def solve_crypto_math(question, max_iterations=2_000_000):
|
||||
"""
|
||||
对给定的 Crypto-math puzzle (如 "A+BC=DB") 进行求解。
|
||||
如果有解,返回形如 "[[A=7,B=2,C=5,D=3]]" 的字符串;无解则返回 False。
|
||||
|
||||
:param question: str, 形如 "A+BC=DB" 或 "ONE+ONE+TWO=FOUR"
|
||||
:param max_iterations: int, 限制最大尝试次数,防止极端情况下计算过长
|
||||
:return: str 或 bool
|
||||
"""
|
||||
print(f"[Step 0] 准备开始求解题目: {question}")
|
||||
# 1. 解析题目
|
||||
if '=' not in question:
|
||||
print("[Step 1] 解析失败:题目中没有 '=',返回 False")
|
||||
return False
|
||||
|
||||
left_side, right_side = question.split('=')
|
||||
left_side = left_side.strip()
|
||||
right_side = right_side.strip()
|
||||
|
||||
print(f"[Step 1] 成功用 '=' 将题目分为:left_side='{left_side}',right_side='{right_side}'")
|
||||
|
||||
left_terms = left_side.split('+') # 左侧有多个加数
|
||||
# 这里假设右侧只有一个整体表达式,如 "DB" 或 "FOUR"
|
||||
# 如果右侧也可能有多个加数,可以类似地再 split('+')
|
||||
right_term = right_side
|
||||
|
||||
print(f"[Step 2] 左侧加数列表 = {left_terms}")
|
||||
print(f"[Step 2] 右侧表达式 = {right_term}")
|
||||
|
||||
# 2. 收集所有出现的字母 & 首字母
|
||||
# (用于确保多位数首字母不为 0)
|
||||
letters_set = set()
|
||||
leading_letters = set()
|
||||
|
||||
def analyze_term(term):
|
||||
"""
|
||||
term 为字符串(如 'ABC'),
|
||||
收集其中的所有字母,并找出首字母。
|
||||
"""
|
||||
term = term.strip()
|
||||
for ch in term:
|
||||
if ch.isalpha():
|
||||
letters_set.add(ch)
|
||||
# 若是多位数或至少 2 位,则把首字母记录为 leading letter
|
||||
if len(term) > 1 and term[0].isalpha():
|
||||
leading_letters.add(term[0])
|
||||
|
||||
# 分析左侧
|
||||
for t in left_terms:
|
||||
analyze_term(t)
|
||||
|
||||
# 分析右侧
|
||||
analyze_term(right_term)
|
||||
|
||||
letters = sorted(letters_set) # 收集到的全部字母并排序
|
||||
print(f"[Step 3] 收集到的所有字母 = {letters}")
|
||||
print(f"[Step 3] 需要确保以下首字母 != 0: {sorted(list(leading_letters))}")
|
||||
|
||||
# 如果字母数量大于10,无法映射到 0-9 的唯一数字
|
||||
if len(letters) > 10:
|
||||
print(f"[Step 3] 字母数量 = {len(letters)},超过 10 个,不可能一一映射到数字 0-9。返回 False")
|
||||
return False
|
||||
|
||||
# 3. 准备求解
|
||||
# 定义一个小函数,将某个映射应用到字符串上,返回对应的数字值
|
||||
def term_value(term, mapping):
|
||||
"""
|
||||
将类似 'ABC' 根据 mapping 替换成数字,然后转换为 int。
|
||||
比如 'ABC' -> '123' -> int(123)。
|
||||
"""
|
||||
return int(''.join(str(mapping[ch]) for ch in term if ch.isalpha()))
|
||||
|
||||
# 4. 穷举:letters 的所有数字排列
|
||||
# 注意 leading_letters 不能为 0
|
||||
# leading_letters 可能是集合,如 {'A', 'B', 'D'} => 对应的映射必须 > 0
|
||||
attempt_count = 0
|
||||
print(f"[Step 4] 准备开始尝试所有可能的字母->数字排列…(最多 {max_iterations} 次)")
|
||||
|
||||
for perm in itertools.permutations(range(10), len(letters)):
|
||||
attempt_count += 1
|
||||
if attempt_count > max_iterations:
|
||||
# 超过给定的迭代上限,直接返回 False 以防无限拖延
|
||||
print(f"[Step 4] 已尝试超过 {max_iterations} 种排列,仍未找到解,返回 False")
|
||||
return False
|
||||
|
||||
# 构造 letter -> digit 映射
|
||||
mapping = {}
|
||||
for i, letter in enumerate(letters):
|
||||
mapping[letter] = perm[i]
|
||||
|
||||
# 如果有任何 leading letter 被映射成了 0,跳过
|
||||
if any(mapping[ld] == 0 for ld in leading_letters):
|
||||
continue
|
||||
|
||||
# 为了演示,可以在每隔一定数量的 permutation 时打印提示
|
||||
# if attempt_count % 10000 == 0:
|
||||
print(f"[Progress] 已尝试 {attempt_count} 次排列,当前映射示例 = {mapping}")
|
||||
|
||||
# 计算左侧总和
|
||||
try:
|
||||
left_sum = sum(term_value(t, mapping) for t in left_terms)
|
||||
right_val = term_value(right_term, mapping)
|
||||
except ValueError:
|
||||
# 若转换出错,跳过(一般不会发生,防御性写法)
|
||||
print(f"[Warning] 第 {attempt_count} 次尝试转换数值时出错,跳过。映射 = {mapping}")
|
||||
continue
|
||||
|
||||
# 打印一下当前计算值(可以注释掉或更精简)
|
||||
# print(f"尝试第 {attempt_count} 次映射: {mapping}, 左边和= {left_sum}, 右边= {right_val}")
|
||||
|
||||
if left_sum == right_val:
|
||||
# 找到可行解,返回
|
||||
print(f"[Success] 在第 {attempt_count} 次尝试中找到解: 左侧 {left_sum} = 右侧 {right_val}")
|
||||
letter_to_digit_strs = []
|
||||
for letter in sorted(mapping.keys()):
|
||||
letter_to_digit_strs.append(f"{letter}={mapping[letter]}")
|
||||
answer_str = "[[" + ",".join(letter_to_digit_strs) + "]]"
|
||||
print(f"[Step 5] 返回解: {answer_str}")
|
||||
return answer_str
|
||||
|
||||
# 所有排列尝试完都没找到可行解,返回 False
|
||||
print(f"[Step 6] 穷举完所有排列也没能找到可行解,返回 False")
|
||||
return False
|
||||
|
||||
|
||||
# # ==================== 测试示例 ====================
|
||||
# if __name__ == "__main__":
|
||||
# # SYL+JAT+FOO+TJJ+KJF+OA+OLL+TSS+JK+FYR+JAS+TTT+OFY+AOO+JFR+FJS+YJT+JSL+LSA+OTF=LLKTT -> [[A=5,F=6,J=4,K=2,L=1,O=9,R=0,S=3,T=7,Y=8]]
|
||||
# # 381+457+699+744+246+95+911+733+42+680+453+777+968+599+460+643+847+431+135+976=11277
|
||||
# for puzzle in ["A+BC=DB"]: # , "A+BB=A", "ONE+ONE+TWO=FOUR", "SYL+JAT+FOO+TJJ+KJF+OA+OLL+TSS+JK+FYR+JAS+TTT+OFY+AOO+JFR+FJS+YJT+JSL+LSA+OTF=LLKTT"
|
||||
# sol = solve_crypto_math(puzzle, max_iterations=2_000_000)
|
||||
# print(puzzle, "->", sol)
|
||||
# if sol != False:
|
||||
# sol = {item.split('=')[0]: int(item.split('=')[1]) for item in sol[2:-2].split(",")}
|
||||
# for letter, digit in sol.items():
|
||||
# puzzle = puzzle.replace(letter, str(digit))
|
||||
# try:
|
||||
# eval_res = eval(puzzle.replace('=', '=='))
|
||||
# except:
|
||||
# print(" Eval failed!")
|
||||
# eval_res = False
|
||||
# if not eval_res:
|
||||
# print(" Check failed!")
|
||||
# break
|
||||
# print(puzzle)
|
||||
# print()
|
||||
Loading…
Add table
Add a link
Reference in a new issue