✨ feat(solutions):新增排列问题解决方案和斐波那契问题优化
📝 docs(solutions):添加排列问题README文档说明欧拉数算法 ♻️ refactor(solutions):重构排列问题代码,添加数学计算方法 ✨ feat(solutions):新增斐波那契问题解决方案,支持大数计算和Binet公式 🔧 chore(solutions):为两个解决方案添加性能计时器装饰器
This commit is contained in:
14
solutions/0024.Permutations/README.md
Normal file
14
solutions/0024.Permutations/README.md
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
# 有序排列
|
||||||
|
|
||||||
|
我没想到的是,排列也有很多事情是我没想到的,比如 [欧拉数 Eulerian Number](https://en.wikipedia.org/wiki/Eulerian_number)。
|
||||||
|
在 [OEIS A008292](https://oeis.org/A008292) 还有更多讨论。
|
||||||
|
|
||||||
|
|
||||||
|
本来以为使用python自带的排列方法,就是最快的了。但没想到欧拉数的有序计算逻辑似乎更好用:
|
||||||
|
|
||||||
|
对于 $n$ 个元素的排列:
|
||||||
|
- 总共有 $n!$ 种排列
|
||||||
|
- 以某个特定元素开头的排列有 $(n−1)!$ 种
|
||||||
|
- 这构成了变进制的基础,可以用来计算第 $k$ 个排列。
|
||||||
|
|
||||||
|
相比在序列上移动要快速多了,毕竟是与原序列的长度相关,而不与位置相关。
|
||||||
85
solutions/0024.Permutations/euler_24.py
Normal file
85
solutions/0024.Permutations/euler_24.py
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
"""
|
||||||
|
A permutation is an ordered arrangement of objects.
|
||||||
|
For example, 3124 is one possible permutation of the digits 1, 2, 3 and 4.
|
||||||
|
If all of the permutations are listed numerically or alphabetically, we call it lexicographic order.
|
||||||
|
The lexicographic permutations of 0, 1 and 2 are:
|
||||||
|
012 021 102 120 201 210
|
||||||
|
|
||||||
|
What is the millionth lexicographic permutation of the digits 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9?
|
||||||
|
"""
|
||||||
|
|
||||||
|
import time
|
||||||
|
from itertools import permutations
|
||||||
|
|
||||||
|
|
||||||
|
def timer(func):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
start_time = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end_time = time.time()
|
||||||
|
print(f"Execution time: {end_time - start_time:.6f} seconds")
|
||||||
|
return result
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def ordered_permutations(n: int) -> list[int]:
|
||||||
|
digits = list(range(10))
|
||||||
|
all_permutations = permutations(digits)
|
||||||
|
for _ in range(n - 1):
|
||||||
|
next(all_permutations)
|
||||||
|
return next(all_permutations)
|
||||||
|
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def main_iter():
|
||||||
|
n = 1000000
|
||||||
|
result = ordered_permutations(n)
|
||||||
|
print(f"used iter: {''.join(map(str, result))}")
|
||||||
|
|
||||||
|
|
||||||
|
def get_permutation(seq, k):
|
||||||
|
"""
|
||||||
|
返回序列 seq 的第 k 个字典序排列 (1-based)
|
||||||
|
|
||||||
|
参数:
|
||||||
|
seq: 可迭代对象(列表、元组、字符串等),元素需有序
|
||||||
|
k: 第 k 个排列(从1开始计数)
|
||||||
|
|
||||||
|
返回:
|
||||||
|
与输入类型相同的排列结果
|
||||||
|
"""
|
||||||
|
n = len(seq)
|
||||||
|
|
||||||
|
# 预计算阶乘
|
||||||
|
factorial = [1]
|
||||||
|
for i in range(1, n):
|
||||||
|
factorial.append(factorial[-1] * i)
|
||||||
|
|
||||||
|
# 将输入转为列表(复制一份避免修改原序列)
|
||||||
|
elements = list(seq)
|
||||||
|
k -= 1 # 转为0-based索引
|
||||||
|
|
||||||
|
# 构建结果
|
||||||
|
result = []
|
||||||
|
for i in range(n, 0, -1):
|
||||||
|
index = k // factorial[i - 1] # 当前位选择的元素索引
|
||||||
|
result.append(elements.pop(index))
|
||||||
|
k %= factorial[i - 1] # 更新剩余位
|
||||||
|
|
||||||
|
# 根据输入类型返回相应格式
|
||||||
|
if isinstance(seq, str):
|
||||||
|
return "".join(result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def main_math():
|
||||||
|
n = 1000000
|
||||||
|
result = get_permutation("0123456789", n)
|
||||||
|
print(f"used math: {result}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main_iter()
|
||||||
|
main_math()
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
"""
|
|
||||||
A permutation is an ordered arrangement of objects.
|
|
||||||
For example, 3124 is one possible permutation of the digits 1, 2, 3 and 4.
|
|
||||||
If all of the permutations are listed numerically or alphabetically, we call it lexicographic order.
|
|
||||||
The lexicographic permutations of 0, 1 and 2 are:
|
|
||||||
012 021 102 120 201 210
|
|
||||||
|
|
||||||
What is the millionth lexicographic permutation of the digits 0, 1, 2, 3, 4, 5, 6, 7, 8 and 9?
|
|
||||||
"""
|
|
||||||
|
|
||||||
from itertools import permutations
|
|
||||||
|
|
||||||
|
|
||||||
def ordered_permutations(n: int) -> list[int]:
|
|
||||||
digits = list(range(10))
|
|
||||||
all_permutations = permutations(digits)
|
|
||||||
for _ in range(n - 1):
|
|
||||||
next(all_permutations)
|
|
||||||
return next(all_permutations)
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
n = 1000000
|
|
||||||
result = ordered_permutations(n)
|
|
||||||
print("".join(map(str, result)))
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
77
solutions/0025.Fibonacci/euler_25.py
Normal file
77
solutions/0025.Fibonacci/euler_25.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
"""
|
||||||
|
The Fibonacci sequence is defined by the recurrence relation:
|
||||||
|
F_n = F_{n-1} + F_{n-2}, where F_1 = 1 and F_2 = 1.
|
||||||
|
|
||||||
|
Hence the first 12 terms will be: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144
|
||||||
|
The 12th term, F_12, is the first term to contain three digits.
|
||||||
|
|
||||||
|
What is the index of the first term in the Fibonacci sequence to contain 1000-digits?
|
||||||
|
"""
|
||||||
|
|
||||||
|
import math
|
||||||
|
import time
|
||||||
|
|
||||||
|
|
||||||
|
def timer(func):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
start_time = time.time()
|
||||||
|
result = func(*args, **kwargs)
|
||||||
|
end_time = time.time()
|
||||||
|
during = end_time - start_time
|
||||||
|
if during > 1:
|
||||||
|
print(f"function {func.__name__} taken: {during:.6f} seconds")
|
||||||
|
elif during > 0.001:
|
||||||
|
print(f"function {func.__name__} taken: {during * 1000:.3f} milliseconds")
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
f"function {func.__name__} taken: {during * 1000000:.3f} microseconds"
|
||||||
|
)
|
||||||
|
return result
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
|
||||||
|
def big_sum(a: str, b: str) -> str:
|
||||||
|
length = max(len(a), len(b))
|
||||||
|
carry = 0
|
||||||
|
result = []
|
||||||
|
for i in range(length):
|
||||||
|
digit_a = int(a[-i - 1]) if i < len(a) else 0
|
||||||
|
digit_b = int(b[-i - 1]) if i < len(b) else 0
|
||||||
|
sum_digit = digit_a + digit_b + carry
|
||||||
|
result.append(str(sum_digit % 10))
|
||||||
|
carry = sum_digit // 10
|
||||||
|
if carry:
|
||||||
|
result.append(str(carry))
|
||||||
|
return "".join(result[::-1])
|
||||||
|
|
||||||
|
|
||||||
|
def find_fibonacci_index(digits: int) -> int:
|
||||||
|
a, b = "1", "1"
|
||||||
|
index = 1
|
||||||
|
while len(a) < digits:
|
||||||
|
a, b = b, big_sum(a, b)
|
||||||
|
index += 1
|
||||||
|
return index
|
||||||
|
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def main_in_bigint():
|
||||||
|
print(find_fibonacci_index(1000))
|
||||||
|
|
||||||
|
|
||||||
|
def binet(n: int) -> int:
|
||||||
|
index = math.ceil(
|
||||||
|
(math.log10(math.sqrt(5)) + (n - 1)) / math.log10(0.5 * (1 + math.sqrt(5)))
|
||||||
|
)
|
||||||
|
return index
|
||||||
|
|
||||||
|
|
||||||
|
@timer
|
||||||
|
def main_use_binet():
|
||||||
|
print(f"{binet(1000)}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main_in_bigint()
|
||||||
|
main_use_binet()
|
||||||
Reference in New Issue
Block a user