题目解析
问题描述
小C对排列感兴趣,她想知道有多少个长度为 n 的排列满足 任意两个相邻元素之和都不是素数。
- 排列:长度为 n 的数组,包含从 1 到 n 的所有整数,每个数字恰好出现一次。
- 素数的性质:一个大于 1 的数,如果它仅能被 1 和其本身整除,那么它是素数。
目标是找到符合条件的排列数量。
解题分析
-
素数判定函数:
首先需要定义一个函数,判断某数是否为素数。对于每个相邻元素之和,判断其是否为素数,并标记这些和。 -
排列生成:
- 利用 Python 中
itertools.permutations,生成从 1 到 n 的所有排列。 - 对每个排列,验证其相邻元素之和是否符合条件(即不为素数)。
- 利用 Python 中
-
预处理优化:
- 预先计算 1 到 n 的所有可能相邻元素之和是否为素数,这样在检查排列时可以避免重复计算。
-
排列过滤:
- 遍历所有排列,验证每个排列是否满足条件。
- 若满足条件,则计数加一。
代码详解
完整代码
from itertools import permutations
def is_prime(num):
"""
判断一个数是否为素数
"""
if num <= 1:
return False
for i in range(2, int(num ** 0.5) + 1):
if num % i == 0:
return False
return True
def precompute_prime_sums(n):
"""
预计算所有可能的相邻元素之和是否为素数
"""
prime_sums = [[False] * (n + 1) for _ in range(n + 1)]
for i in range(1, n + 1):
for j in range(1, n + 1):
if i != j: # 排除自身相加的情况
prime_sums[i][j] = is_prime(i + j)
return prime_sums
def solution(n: int) -> int:
"""
计算符合条件的排列数量
"""
# 预计算素数相邻和
prime_sums = precompute_prime_sums(n)
# 生成所有排列
all_permutations = permutations(range(1, n + 1))
count = 0
for perm in all_permutations:
valid = True
# 检查每个排列的相邻元素之和
for i in range(n - 1):
if prime_sums[perm[i]][perm[i + 1]]:
valid = False
break
if valid:
count += 1
return count
if __name__ == '__main__':
# 测试用例
print(solution(5) == 4) # 输出 4
print(solution(3) == 0) # 输出 0
print(solution(6) == 24) # 输出 24
代码解析
-
判断素数 (
is_prime)- 使用 的方法判断数是否为素数。
- 如果某个数 num 能被 2 到 的任何数整除,则它不是素数。
-
预处理 (
precompute_prime_sums)- 为 1 到 n 的所有可能的两数之和构建二维布尔数组
prime_sums。 - 如果 i + j 是素数,则
prime_sums[i][j]为 True,反之为 False。 - 这样可以在验证排列时快速查询是否满足条件,避免重复计算。
- 为 1 到 n 的所有可能的两数之和构建二维布尔数组
-
生成排列并验证 (
solution)- 使用
itertools.permutations生成所有从 1 到 n 的排列。排列的数量为 n!。 - 对每个排列,从头到尾检查其相邻元素之和是否为素数。如果有任何一个和是素数,则该排列无效。
- 使用
-
计数有效排列
- 如果一个排列符合条件,计数加一。
- 最终返回符合条件的排列数量。
测试样例与解释
样例1
输入:
solution(5)
输出:
4
解释:
所有长度为 5 的排列中,仅有 4 个排列满足相邻元素之和都不是素数。由于排列数量较大,无法列举全部排列。
样例2
输入:
solution(3)
输出:
0
解释:
长度为 3 的排列中,没有任何排列能满足条件。
样例3
输入:
solution(6)
输出:
24
解释:
长度为 6 的排列中,有 24 个排列符合条件。
时间与空间复杂度
时间复杂度
-
素数预处理:
- 预计算相邻元素之和是否为素数,时间复杂度为 ,其中 m 是 1 到 2n 范围内的最大数。
-
排列生成:
- 生成所有排列的时间复杂度为 O(n!)
-
验证排列:
- 对每个排列验证相邻元素之和是否为素数,时间复杂度为 O(n),总计为 。
综合:总时间复杂度为。
空间复杂度
- 用于存储素数预处理的二维数组
prime_sums,空间复杂度为 。 - 用于生成排列的临时空间需求,取决于排列的数量,最大空间复杂度为 O(n)(递归深度)。
总空间复杂度为 。
知识点总结
-
素数判断的优化:
- 素数判断可以用 的方法实现,提升效率。
- 使用预处理将重复计算的代价转移到一次性计算中。
-
排列生成与过滤:
itertools.permutations是生成排列的强大工具,但需要注意其性能问题。- 对于 nn 较大的问题,可以结合剪枝技术优化搜索。
-
复杂度分析:
- 通过预处理与验证分开,简化代码逻辑并提升运行效率。