一个认为一切根源都是“自己不够强”的INTJ
个人主页:用哲学编程-CSDN博客
专栏:每日一题——举一反三
Python编程学习
Python内置函数
目录
6. 效率优化(Efficiency Optimization)
题目链接:https://pintia.cn/problem-sets/994805260223102976/exam/problems/type/7?problemSetProblemId=1071785997033074688&page=0
我的写法
import math
L,K=map(int,input().split())
N=input()
def is_prime(n):
if n<2:
return False
elif n==2:
return True
elif n%2==0:
return False
else:
for i in range(3,math.isqrt(n)+1,2):
if n%i==0:
return False
return True
for i in range(L-K+1):
if is_prime(int(N[i:i+K])):
#不是print(int(N[i:i+K]))!
print(N[i:i+K])
break
else:
print("404")
代码分析
- 输入部分:
- L, K = map(int, input().split()):读取两个整数 L 和 K,分别表示字符串 N 的长度和要查找的子串长度。
- N = input():读取字符串 N。
- 质数检测函数:
- is_prime(n):一个用于检测整数 n 是否为质数的函数。该函数首先处理一些特殊情况(小于2的数和2本身),然后检查偶数,最后通过从3到 sqrt(n) 的奇数来检查是否存在因子。
- 主循环:
- for i in range(L-K+1)::遍历所有可能的起始位置,以提取长度为 K 的子串。
- if is_prime(int(N[i:i+K]))::检查当前子串表示的整数是否为质数。
- print(N[i:i+K]):如果是质数,则输出该子串并跳出循环。
- else::如果循环结束后没有找到质数子串,则输出 "404"。
时间复杂度分析
- 质数检测函数:is_prime(n) 的时间复杂度主要取决于 n 的大小。在最坏情况下,它需要检查从3到 sqrt(n) 的所有奇数,因此时间复杂度为 O(sqrt(n))。
- 主循环:主循环最多会执行 L-K+1 次,每次调用 is_prime 函数。因此,总的时间复杂度为 O((L-K+1) * sqrt(10^K)),其中 10^K 是子串表示的最大可能整数。
空间复杂度分析
- 输入部分:存储 L、K 和 N 的空间复杂度为 O(L)。
- 质数检测函数:该函数的空间复杂度为 O(1),因为它只使用了常数级别的额外空间。
- 主循环:主循环的空间复杂度也为 O(1),因为它只使用了常数级别的额外空间。
综上所述,总的空间复杂度为 O(L)。
总结
- 时间复杂度:O((L-K+1) * sqrt(10^K))
- 空间复杂度:O(L)
这段代码在处理较大输入时可能会遇到性能问题,特别是当 K 较大时,质数检测的时间复杂度会显著增加。此外,代码在处理大整数时可能会受到 Python 整数表示的限制。
我要更强
优化时间复杂度和空间复杂度的方法有很多,以下是一些可能的优化策略,并附上相应的代码和注释:
1. 使用埃拉托斯特尼筛法预处理质数
预先计算并存储一定范围内的质数,可以显著减少质数检测的时间复杂度。
import math
# 预处理质数
def sieve_of_eratosthenes(limit):
is_prime = [True] * (limit + 1)
is_prime[0] = is_prime[1] = False
for i in range(2, int(math.sqrt(limit)) + 1):
if is_prime[i]:
for j in range(i*i, limit + 1, i):
is_prime[j] = False
return is_prime
# 读取输入
L, K = map(int, input().split())
N = input()
# 预处理质数
limit = 10**K
is_prime = sieve_of_eratosthenes(limit)
# 查找质数子串
found = False
for i in range(L - K + 1):
if is_prime[int(N[i:i+K])]:
print(N[i:i+K])
found = True
break
if not found:
print("404")
2. 使用滑动窗口减少重复计算
通过滑动窗口技术,可以减少对子串的重复转换和质数检测。
import math
# 质数检测函数
def is_prime(n):
if n < 2:
return False
for i in range(2, int(math.sqrt(n)) + 1):
if n % i == 0:
return False
return True
# 读取输入
L, K = map(int, input().split())
N = input()
# 初始化滑动窗口
window = int(N[:K])
if is_prime(window):
print(N[:K])
else:
for i in range(1, L - K + 1):
window = window * 10 - int(N[i-1]) * (10**K) + int(N[i+K-1])
if is_prime(window):
print(N[i:i+K])
break
else:
print("404")
3. 使用位操作优化质数检测
通过位操作可以进一步优化质数检测的效率。
import math
# 质数检测函数
def is_prime(n):
if n < 2:
return False
if n == 2:
return True
if n % 2 == 0:
return False
for i in range(3, int(math.sqrt(n)) + 1, 2):
if n % i == 0:
return False
return True
# 读取输入
L, K = map(int, input().split())
N = input()
# 查找质数子串
found = False
for i in range(L - K + 1):
if is_prime(int(N[i:i+K])):
print(N[i:i+K])
found = True
break
if not found:
print("404")
4. 使用动态规划预处理质数
通过动态规划预处理质数,可以进一步减少质数检测的时间复杂度。
import math
# 预处理质数
def preprocess_primes(limit):
is_prime = [True] * (limit + 1)
is_prime[0] = is_prime[1] = False
for i in range(2, int(math.sqrt(limit)) + 1):
if is_prime[i]:
for j in range(i*i, limit + 1, i):
is_prime[j] = False
return is_prime
# 读取输入
L, K = map(int, input().split())
N = input()
# 预处理质数
limit = 10**K
is_prime = preprocess_primes(limit)
# 查找质数子串
found = False
for i in range(L - K + 1):
if is_prime[int(N[i:i+K])]:
print(N[i:i+K])
found = True
break
if not found:
print("404")
总结
以上方法通过预处理质数、滑动窗口技术和动态规划等手段,可以有效优化时间复杂度和空间复杂度。具体选择哪种方法取决于输入规模和具体应用场景。
哲学和编程思想
这些优化方法体现了多种哲学和编程思想,以下是一些具体的说明:
1. 预处理和缓存(Caching)
预处理质数和缓存结果是一种常见的优化策略,它体现了“空间换时间”的思想。通过预先计算并存储结果,可以在后续操作中快速检索,从而减少重复计算的时间。
- 哲学思想:这是一种实用主义的体现,即通过权衡资源(如内存)的使用来优化性能。
- 编程思想:这是“缓存”和“预计算”的设计模式,常见于动态规划和记忆化搜索等算法中。
2. 滑动窗口(Sliding Window)
滑动窗口技术通过维护一个固定大小的窗口,在数据序列上滑动,从而减少重复计算。这种技术在处理连续子序列问题时非常有效。
- 哲学思想:这是一种“局部性原理”的体现,即系统或过程的当前状态很大程度上取决于其最近的历史状态。
- 编程思想:这是“窗口化”和“状态维护”的设计模式,常见于字符串处理和序列分析等算法中。
3. 位操作(Bit Manipulation)
位操作通过直接操作二进制位来优化计算效率,尤其在处理整数运算时非常有效。
- 哲学思想:这是一种“最小作用原理”的体现,即通过最小的操作来达到最大的效果。
- 编程思想:这是“位运算”和“高效计算”的设计模式,常见于底层优化和算法竞赛中。
4. 动态规划(Dynamic Programming)
动态规划通过将问题分解为重叠子问题,并存储子问题的解来避免重复计算,从而优化时间复杂度。
- 哲学思想:这是一种“分而治之”和“最优子结构”的体现,即通过分解问题并找到子问题的最优解来构建整体的最优解。
- 编程思想:这是“动态规划”和“状态转移”的设计模式,常见于优化问题和组合问题中。
5. 实用主义(Pragmatism)
在优化算法时,实用主义思想强调实际效果和可行性,即通过权衡不同的资源使用(如时间、空间)来达到最佳的性能。
- 哲学思想:实用主义强调结果的有效性和实用性,而不是抽象的理论或原则。
- 编程思想:在编程中,实用主义体现在选择合适的算法和数据结构,以及在性能和资源之间做出权衡。
6. 效率优化(Efficiency Optimization)
在编程中,效率优化是一种常见的思想,它强调通过各种手段(如算法改进、数据结构选择、代码优化)来提高程序的运行效率。
- 哲学思想:效率优化体现了“精益求精”和“持续改进”的原则。
- 编程思想:在编程中,效率优化体现在不断寻找和应用更高效的算法和实现方法。
总结
这些优化方法不仅体现了编程中的各种设计模式和算法思想,还反映了哲学上的实用主义、局部性原理、最小作用原理等思想。通过综合运用这些思想,可以有效地优化算法的时间复杂度和空间复杂度。
举一反三
根据上述哲学和编程思想,结合本题目,以下是一些具体的技巧和建议,帮助在解决类似问题时举一反三:
1. 预处理和缓存
- 预处理质数:在本题目中,预处理质数列表可以显著减少质数检测的时间。你可以将这种预处理思想应用到其他需要频繁质数检测的场景中。
- 缓存中间结果:在其他问题中,如果存在重复计算,可以考虑使用缓存机制来存储中间结果,避免重复计算。
2. 滑动窗口
- 连续子串问题:本题目中使用了滑动窗口来查找质数子串。在处理类似连续子串或子数组的问题时,可以考虑使用滑动窗口技术。
- 动态维护窗口:在其他问题中,如果需要动态维护一个连续的子序列或子数组,滑动窗口是一个有效的工具。
3. 位操作
- 优化整数运算:在本题目中,位操作可以用来优化质数检测。在处理整数运算时,考虑是否可以使用位操作来提高效率。
- 位运算技巧:学习和掌握常用的位运算技巧,如位掩码、位移等,这些技巧在优化算法时非常有用。
4. 动态规划
- 重叠子问题:本题目虽然没有直接使用动态规划,但动态规划的思想可以应用到其他需要解决重叠子问题的问题中。
- 状态转移方程:在其他问题中,如果存在重叠子问题,可以尝试设计状态转移方程,使用动态规划来优化解决方案。
5. 实用主义
- 权衡时间和空间:在选择算法时,权衡时间和空间复杂度,选择最适合当前问题的方案。
- 考虑实际应用:在解决实际问题时,考虑输入规模、性能要求等因素,选择合适的优化策略。
6. 效率优化
- 持续改进算法:不断寻找和应用更高效的算法和实现方法,持续改进代码性能。
- 代码审查和优化:进行代码审查,寻找潜在的优化点,如减少不必要的计算、简化逻辑等。
7. 抽象和泛化
- 抽象问题模型:将具体问题抽象为更一般的模型,寻找通用的解决方法。
- 泛化解决方案:设计通用的算法框架,使其适用于多种类似问题。
8. 学习和借鉴
- 学习经典算法:学习经典的算法和数据结构,理解其设计思想和应用场景。
- 借鉴他人经验:参考他人的解决方案和优化经验,从中学习和借鉴。
9. 实践和测试
- 编写测试用例:编写全面的测试用例,验证算法的正确性和性能。
- 性能分析:使用性能分析工具,找出代码的瓶颈,有针对性地进行优化。
通过综合运用这些技巧和建议,可以在解决类似问题时更加灵活和高效,举一反三,不断提升自己的编程能力和问题解决能力。