diff --git a/solutions/0024.Permutations/README.md b/solutions/0024.Permutations/README.md new file mode 100644 index 0000000..525c8a4 --- /dev/null +++ b/solutions/0024.Permutations/README.md @@ -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$ 个排列。 + +相比在序列上移动要快速多了,毕竟是与原序列的长度相关,而不与位置相关。 diff --git a/solutions/0024.Permutations/euler_24.py b/solutions/0024.Permutations/euler_24.py new file mode 100644 index 0000000..94f27c7 --- /dev/null +++ b/solutions/0024.Permutations/euler_24.py @@ -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() diff --git a/solutions/0024/euler_24.py b/solutions/0024/euler_24.py deleted file mode 100644 index 02640d0..0000000 --- a/solutions/0024/euler_24.py +++ /dev/null @@ -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() diff --git a/solutions/0025.Fibonacci/euler_25.py b/solutions/0025.Fibonacci/euler_25.py new file mode 100644 index 0000000..01a0b5f --- /dev/null +++ b/solutions/0025.Fibonacci/euler_25.py @@ -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()