diff --git a/README.md b/README.md index 6cf6ec7..086b3e3 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,21 @@ 主要记录一下解决 [Project Euler](https://projecteuler.net/) 问题的方法,自娱自乐成分较大, 也会有一些新奇的想法。主要使用 Python 解决,也会有 Haskell 、 Rust 或者 Lean4 的解决方法。 -项目使用 `uv` 进行管理,毕竟主要还是使用 Python来写的。 +项目使用 `uv` 进行管理,毕竟绝大多数问题都是Python来写的解决方法。 ----- + **KEY POINT :** 要把数学回归数学,而不是回归程序。 从数学角度去考虑问题,用数学化简计算逻辑。 + + +----- + +**`main.py` 的使用** + +为了便于管理和使用,简单创建了一个脚本。 +这个脚本只有三个主要功能,一个是创建新问题的文件,一个是列出已创建问题,还有一个是运行指定问题的python解法。 diff --git a/main.py b/main.py index 686fb6b..7cf97f7 100644 --- a/main.py +++ b/main.py @@ -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() diff --git a/pyproject.toml b/pyproject.toml index c22cbff..f720fec 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,4 +8,5 @@ dependencies = [ "mplusa>=0.0.3", "numpy>=2.3.5", "sympy>=1.14.0", + "typer-slim[standard]>=0.21.0", ] diff --git a/solutions/0036.Palindromes/euler_36.py b/solutions/0036.Palindromes/euler_36.py new file mode 100644 index 0000000..f3c4e45 --- /dev/null +++ b/solutions/0036.Palindromes/euler_36.py @@ -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) diff --git a/uv.lock b/uv.lock index 12dbbe1..fed4621 100644 --- a/uv.lock +++ b/uv.lock @@ -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" }, +]