feat(main.py):添加CLI工具支持问题管理功能

📝 docs(README.md):更新项目说明并添加main.py使用指南
⬆️ chore(pyproject.toml):添加typer依赖以支持CLI功能
 test(solutions):添加0036号问题的解决方案文件
This commit is contained in:
2026-01-06 14:04:31 +08:00
parent 0ea015f3af
commit 6fb29a863c
5 changed files with 253 additions and 4 deletions

View File

@@ -3,12 +3,21 @@
主要记录一下解决 [Project Euler](https://projecteuler.net/) 问题的方法,自娱自乐成分较大,
也会有一些新奇的想法。主要使用 Python 解决,也会有 Haskell 、 Rust 或者 Lean4 的解决方法。
项目使用 `uv` 进行管理,毕竟主要还是使用 Python来写的。
项目使用 `uv` 进行管理,毕竟绝大多数问题都是Python来写的解决方法
-----
**KEY POINT **
要把数学回归数学,而不是回归程序。
从数学角度去考虑问题,用数学化简计算逻辑。
-----
**`main.py` 的使用**
为了便于管理和使用,简单创建了一个脚本。
这个脚本只有三个主要功能一个是创建新问题的文件一个是列出已创建问题还有一个是运行指定问题的python解法。

68
main.py
View File

@@ -1,6 +1,68 @@
def main():
print("Hello from projecteuler!")
import runpy
from pathlib import Path
from profile import run
from tarfile import SOLARIS_XHDTYPE
import typer
app = typer.Typer()
@app.command("add", help="Add a new problem to the projecteuler repository.")
def add_newproblem(num: int, name: str | None = None) -> None:
"""Add a new problem to the projecteuler repository."""
typer.echo(f"Adding problem {num} to the projecteuler repository.")
if name:
title = f"{num:04d}.{name}"
else:
title = f"{num:04d}"
Path(f"solutions/{title}").mkdir(parents=True, exist_ok=True)
Path(f"solutions/{title}/euler_{num}.py").touch()
@app.command("list", help="List all problems in the projecteuler repository.")
def list_problems(every: bool = False):
"""List all problems in the projecteuler repository."""
if every:
typer.echo("Listing all problems in the projecteuler repository.")
else:
typer.echo("Listing near problems in the projecteuler repository.")
for path in Path("solutions").iterdir():
if path.is_dir():
if every:
tmp = list(path.iterdir())
tmp = all(ff.is_dir() for ff in tmp)
if tmp:
for ff in path.iterdir():
if ff.is_dir():
typer.echo(f"{ff.name}")
else:
typer.echo(f"{path.name}")
else:
if "_" in path.name:
typer.echo(f"{path.name} (completed)")
else:
typer.echo(f"{path.name}")
@app.command("solution", help="Run a solution for a given problem.")
def run_solution(num: int) -> None:
folders = list(Path("solutions").iterdir())
folders = [
folder
for folder in folders
if folder.is_dir() and folder.name.startswith(f"{num:04d}")
]
runpy.run_path(
(folders[0] / f"euler_{num}.py").resolve().as_posix(), run_name="__main__"
)
@app.command("version", help="Display the version of the projecteuler CLI.")
def version():
"""Display the version of the projecteuler CLI."""
typer.echo("projecteuler solution version 0.1.0")
if __name__ == "__main__":
main()
app()

View File

@@ -8,4 +8,5 @@ dependencies = [
"mplusa>=0.0.3",
"numpy>=2.3.5",
"sympy>=1.14.0",
"typer-slim[standard]>=0.21.0",
]

View File

@@ -0,0 +1,74 @@
"""
The decimal number, 585 = 1001001001_2 (binary), is palindromic in both bases.
Find the sum of all numbers, less than one million, which are palindromic in base 10 and base 2.
(Please note that the palindromic number, in either base, may not include leading zeros.)
"""
import time
def timer(func):
def wrapper(*args, **kwargs):
start_time = time.time()
result = func(*args, **kwargs)
end_time = time.time()
print(f"{func.__name__} took time: {end_time - start_time:.6f} seconds")
return result
return wrapper
def is_palindromic(num: int) -> bool:
return str(num) == str(num)[::-1]
@timer
def main_simple(limit: int = 1000000) -> None:
res = []
for num in range(0, limit + 1):
if is_palindromic(num):
if is_palindromic(int(bin(num)[2:])):
res.append(num)
print(sum(res))
def palindromes_of_length(length: int) -> list[int]:
"""生成所有指定位数的回文数"""
if length == 1:
return list(range(10))
result = []
# 前半部分的起始和结束
start = 10 ** ((length - 1) // 2)
end = 10 ** ((length + 1) // 2)
for half in range(start, end):
s = str(half)
if length % 2 == 0:
# 偶数位123 → 123321
palindrome = s + s[::-1]
else:
# 奇数位123 → 12321
palindrome = s + s[-2::-1]
result.append(int(palindrome))
return result
@timer
def main_quick(limit: int = 1000000) -> None:
res = []
length = len(str(limit - 1))
for num in range(1, length + 1):
for palindrome in palindromes_of_length(num):
if is_palindromic(int(bin(palindrome)[2:])):
res.append(palindrome)
print(sum(res))
if __name__ == "__main__":
limit = 1000000
main_simple(limit)
main_quick(limit)

103
uv.lock generated
View File

@@ -2,6 +2,48 @@ version = 1
revision = 3
requires-python = ">=3.12"
[[package]]
name = "click"
version = "8.3.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/3d/fa/656b739db8587d7b5dfa22e22ed02566950fbfbcdc20311993483657a5c0/click-8.3.1.tar.gz", hash = "sha256:12ff4785d337a1bb490bb7e9c2b1ee5da3112e94a8622f26a6c77f5d2fc6842a", size = 295065, upload-time = "2025-11-15T20:45:42.706Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/98/78/01c019cdb5d6498122777c1a43056ebb3ebfeef2076d9d026bfe15583b2b/click-8.3.1-py3-none-any.whl", hash = "sha256:981153a64e25f12d547d3426c367a4857371575ee7ad18df2a6183ab0545b2a6", size = 108274, upload-time = "2025-11-15T20:45:41.139Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "markdown-it-py"
version = "4.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
]
[[package]]
name = "mplusa"
version = "0.0.3"
@@ -91,6 +133,7 @@ dependencies = [
{ name = "mplusa" },
{ name = "numpy" },
{ name = "sympy" },
{ name = "typer-slim", extra = ["standard"] },
]
[package.metadata]
@@ -98,6 +141,38 @@ requires-dist = [
{ name = "mplusa", specifier = ">=0.0.3" },
{ name = "numpy", specifier = ">=2.3.5" },
{ name = "sympy", specifier = ">=1.14.0" },
{ name = "typer-slim", extras = ["standard"], specifier = ">=0.21.0" },
]
[[package]]
name = "pygments"
version = "2.19.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" },
]
[[package]]
name = "rich"
version = "14.2.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/fb/d2/8920e102050a0de7bfabeb4c4614a49248cf8d5d7a8d01885fbb24dc767a/rich-14.2.0.tar.gz", hash = "sha256:73ff50c7c0c1c77c8243079283f4edb376f0f6442433aecb8ce7e6d0b92d1fe4", size = 219990, upload-time = "2025-10-09T14:16:53.064Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/25/7a/b0178788f8dc6cafce37a212c99565fa1fe7872c70c6c9c1e1a372d9d88f/rich-14.2.0-py3-none-any.whl", hash = "sha256:76bc51fe2e57d2b1be1f96c524b890b816e334ab4c1e45888799bfaab0021edd", size = 243393, upload-time = "2025-10-09T14:16:51.245Z" },
]
[[package]]
name = "shellingham"
version = "1.5.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/58/15/8b3609fd3830ef7b27b655beb4b4e9c62313a4e8da8c676e142cc210d58e/shellingham-1.5.4.tar.gz", hash = "sha256:8dbca0739d487e5bd35ab3ca4b36e11c4078f3a234bfce294b0a0291363404de", size = 10310, upload-time = "2023-10-24T04:13:40.426Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/f9/0595336914c5619e5f28a1fb793285925a8cd4b432c9da0a987836c7f822/shellingham-1.5.4-py2.py3-none-any.whl", hash = "sha256:7ecfff8f2fd72616f7481040475a65b2bf8af90a56c89140852d1120324e8686", size = 9755, upload-time = "2023-10-24T04:13:38.866Z" },
]
[[package]]
@@ -111,3 +186,31 @@ sdist = { url = "https://files.pythonhosted.org/packages/83/d3/803453b36afefb7c2
wheels = [
{ url = "https://files.pythonhosted.org/packages/a2/09/77d55d46fd61b4a135c444fc97158ef34a095e5681d0a6c10b75bf356191/sympy-1.14.0-py3-none-any.whl", hash = "sha256:e091cc3e99d2141a0ba2847328f5479b05d94a6635cb96148ccb3f34671bd8f5", size = 6299353, upload-time = "2025-04-27T18:04:59.103Z" },
]
[[package]]
name = "typer-slim"
version = "0.21.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/f9/3b/2f60ce16f578b1db5b8816d37d6a4d9786b33b76407fc8c13b0b86312c31/typer_slim-0.21.0.tar.gz", hash = "sha256:f2dbd150cfa0fead2242e21fa9f654dfc64773763ddf07c6be9a49ad34f79557", size = 106841, upload-time = "2025-12-25T09:54:55.998Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b4/84/e97abf10e4a699194ff07fd586ec7f4cf867d9d04bead559a65f9e7aff84/typer_slim-0.21.0-py3-none-any.whl", hash = "sha256:92aee2188ac6fc2b2924bd75bb61a340b78bd8cd51fd9735533ce5a856812c8e", size = 47174, upload-time = "2025-12-25T09:54:54.609Z" },
]
[package.optional-dependencies]
standard = [
{ name = "rich" },
{ name = "shellingham" },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]