小Q的非素数和排列问题 | 豆包MarsCode AI刷题

61 阅读4分钟

题目解析

问题描述

小C对排列感兴趣,她想知道有多少个长度为 n 的排列满足 任意两个相邻元素之和都不是素数

  • 排列:长度为 n 的数组,包含从 1 到 n 的所有整数,每个数字恰好出现一次。
  • 素数的性质:一个大于 1 的数,如果它仅能被 1 和其本身整除,那么它是素数。

目标是找到符合条件的排列数量。


解题分析

  1. 素数判定函数
    首先需要定义一个函数,判断某数是否为素数。对于每个相邻元素之和,判断其是否为素数,并标记这些和。

  2. 排列生成

    • 利用 Python 中 itertools.permutations,生成从 1 到 n 的所有排列。
    • 对每个排列,验证其相邻元素之和是否符合条件(即不为素数)。
  3. 预处理优化

    • 预先计算 1 到 n 的所有可能相邻元素之和是否为素数,这样在检查排列时可以避免重复计算。
  4. 排列过滤

    • 遍历所有排列,验证每个排列是否满足条件。
    • 若满足条件,则计数加一。

代码详解

完整代码

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

代码解析

  1. 判断素数 (is_prime)

    • 使用 O(num)O(\sqrt{num}) 的方法判断数是否为素数。
    • 如果某个数 num 能被 2 到 num\sqrt{num} 的任何数整除,则它不是素数。
  2. 预处理 (precompute_prime_sums)

    • 为 1 到 n 的所有可能的两数之和构建二维布尔数组 prime_sums
    • 如果 i + j 是素数,则 prime_sums[i][j] 为 True,反之为 False。
    • 这样可以在验证排列时快速查询是否满足条件,避免重复计算。
  3. 生成排列并验证 (solution)

    • 使用 itertools.permutations 生成所有从 1 到 n 的排列。排列的数量为 n!。
    • 对每个排列,从头到尾检查其相邻元素之和是否为素数。如果有任何一个和是素数,则该排列无效。
  4. 计数有效排列

    • 如果一个排列符合条件,计数加一。
    • 最终返回符合条件的排列数量。

测试样例与解释

样例1

输入:

solution(5)

输出:

4

解释
所有长度为 5 的排列中,仅有 4 个排列满足相邻元素之和都不是素数。由于排列数量较大,无法列举全部排列。


样例2

输入:

solution(3)

输出:

0

解释
长度为 3 的排列中,没有任何排列能满足条件。


样例3

输入:

solution(6)

输出:

24

解释
长度为 6 的排列中,有 24 个排列符合条件。


时间与空间复杂度

时间复杂度

  1. 素数预处理

    • 预计算相邻元素之和是否为素数,时间复杂度为 O(n2m)O(n^2 \sqrt{m}),其中 m 是 1 到 2n 范围内的最大数。
  2. 排列生成

    • 生成所有排列的时间复杂度为 O(n!)
  3. 验证排列

    • 对每个排列验证相邻元素之和是否为素数,时间复杂度为 O(n),总计为 O(nn!)O(n \cdot n!)

综合:总时间复杂度为O(n2m+nn!)O(n^2 \sqrt{m} + n \cdot n!)

空间复杂度

  • 用于存储素数预处理的二维数组 prime_sums,空间复杂度为 O(n2)O(n^2)
  • 用于生成排列的临时空间需求,取决于排列的数量,最大空间复杂度为 O(n)(递归深度)。

总空间复杂度为 O(n2)O(n^2)


知识点总结

  1. 素数判断的优化

    • 素数判断可以用 O(n)O(\sqrt{n}) 的方法实现,提升效率。
    • 使用预处理将重复计算的代价转移到一次性计算中。
  2. 排列生成与过滤

    • itertools.permutations 是生成排列的强大工具,但需要注意其性能问题。
    • 对于 nn 较大的问题,可以结合剪枝技术优化搜索。
  3. 复杂度分析

    • 通过预处理与验证分开,简化代码逻辑并提升运行效率。