关于New_Online_Judge_1081_哥德巴赫猜想的思考

题目表述:
输入一个不小于6的正整数n,将其拆分成三个素数之和,输出任意一解即可。
输入格式
输入存在多组测试数据,每组测试数据输入一行包含一个正整数n(6<=n<=20000)
输出格式
# 看到这个题目,首先想到的是写一个判断素数的函数
def isPrime(n):
    for i in range(2,int(n**0.5)+1):
        if n % i == 0:
            return False
    else: # 当for循环没有被break打断时,返回是素数
        return True

# 然后通过三重循环嵌套暴力枚举可能结果
# 大概是这样的

def solution1(n):
    for i in range(2,n):
        for j in range(2,n):
            for k in range(2,n):
                if i + j + k == n and all([isPrime(x) for x in (i,j,k)]):
                    return(i,j,k)

if __name__ == '__main__':
    print(solution1(20000)) # 方案1超时

# 好勒,接下来就开始这个问题求解的慢慢长征路了

"""分析:
    方案1,有两个耗费时间的地方:
        第一个是isPrime()函数,复杂度是O(n**05)
        第二个是三重for循环的嵌套,复杂度是O(n**3)
"""

# 从isPrime()着手改进,这是万恶的一步,带我走入深渊.不过如果你能经受住深渊的凝视,深渊必定有所回馈.☻
# 搜索资料发现求素数有两个更加高明的算法,分别叫做埃氏筛和欧拉筛.

# 1 埃氏筛
"""
原理:
    请参看:https://blog.csdn.net/holly_Z_P_F/article/details/85063174?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165215199716780357271614%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165215199716780357271614&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-85063174-null-null.142^v9^pc_search_result_cache,157^v4^control&utm_term=%E5%9F%83%E6%B0%8F%E7%AD%9B&spm=1018.2226.3001.4187
"""
# 实现代码:
def eratosthenes(n):
    lis = list(range(n))
    for i in range(2,int(n**0.5)+1):
        k = i*i
        while k <= n-1:
            lis[k] = 0
            k += i
    return lis
# 埃氏筛的时间复杂度是O(n*log(log n))

# 2在埃氏筛的基础上还能再改进,也就是欧拉筛,也即线性筛,因为它的时间复杂度是线性的O(n)
# 欧拉筛原理请参看:https://blog.csdn.net/qq_39763472/article/details/82428602?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522165215228416781683989481%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=165215228416781683989481&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-1-82428602-null-null.142^v9^pc_search_result_cache,157^v4^control&utm_term=%E6%AC%A7%E6%8B%89%E7%AD%9B&spm=1018.2226.3001.4187
# 代码如下:
def ola(n):
    '''欧拉筛求素数'''
    lis = [True]*n
    lis[0] = lis[1] = False
    prime = []
    for i in range(2,n):
        if lis[i]:
            prime.append(i)
        for p in prime:
            if p*i >= n:
                break
            lis[i*p] = False
            if i % p == 0:
                break
    return prime
# 写一个装饰器简单统计一下用时,直观地看看暴力枚举,埃氏筛,欧拉筛的运行时间对比
# 代码如下:
import time

def isPrime(n):
    for i in range(2,int(n**0.5)+1):
        if n % i == 0:
            return False
    else: # 当for循环没有被break打断时,返回是素数
        return True

def zhuangshiqi(func):
    def b(x):
        start = time.time()
        for i in range(100):
            func(x)
        end = time.time()
        print("程序执行100次平均用时:{:.4f}".format((end-start)*1000//100),"毫秒")
    return b

@zhuangshiqi
def baoli(n):
    lis = []
    for i in range(2,n+1):
        if isPrime(i):
            lis.append(i)

@zhuangshiqi
def eratosthenes(n):
    lis = list(range(n))
    for i in range(2,int(n**0.5)+1):
        k = i*i
        while k <= n-1:
            lis[k] = 0
            k += i
    return lis

@zhuangshiqi
def ola(n):
    '''欧拉筛求素数'''
    lis = [True]*n
    lis[0] = lis[1] = False
    prime = []
    for i in range(2,n):
        if lis[i]:
            prime.append(i)
        for p in prime:
            if p*i >= n:
                break
            lis[i*p] = False
            if i % p == 0:
                break
    return prime

if __name__ == '__main__':

    print("开始执行暴力枚举")
    baoli(10**6)
    print("开始执行埃氏筛")
    eratosthenes(10**6)
    print("开始执行欧拉筛")
    ola(10**6)

"""结果如下:
开始执行暴力枚举
程序执行100次平均用时:3189.0000 毫秒
开始执行埃氏筛
程序执行100次平均用时:589.0000 毫秒
开始执行欧拉筛
程序执行100次平均用时:201.0000 毫秒
"""
# 好嘛,用时有很大的提升,虽然欧拉筛并不是直接用来判断某个数是不是素数,而是用来找小于n的所有素数
# 但还是值得一试,所以有了第二种解决方案
def solution2(n):
    # 用欧拉筛先找出所有小于n的素数
    lis = ola(n)
    for i in lis:
        for j in lis:
            for k in lis:
                if i + j + k == n:
                    return(i,j,k)

# 非常遗憾,还是一样超时
# 那么接下来就得从找出三个数的和等于n这里着手了.
# 继续查资料,然后我找到了双指针求三数和的妙招
# 关于双指针算法的详细分析请参看
# https://blog.csdn.net/weixin_42521211/article/details/88189536
# 代码如下:
def three_sum(lis,n):
    '''双指针求数组中三数和等于n的元组'''
    leng = len(lis)
    for i in range(leng):
        l = i
        r = leng - 1
        while l <= r:
            if lis[i] + lis[l] + lis[r] == n:
                return (lis[i], lis[l], lis[r])
            elif lis[i] + lis[l] + lis[r] < n:
                l += 1
            else:
                r -= 1
# 于是有了下面的解决方案
def solution3(n):
    # 用欧拉筛先找出所有小于n的素数
    lis = ola(n)
    res = three_sum(lis,n)
    for i in res:
        print(i,end=' ')
# 你肯定猜到了,这个方案仍然超时!!!!


 这之后,又试过其他的方法,比如在欧拉筛中直接嵌套三数求和的函数,但这是一个大坑,时间性能非常差,也可能是我的代码有问题,记录如下:

# 这个版本尝试将双指针直接插入到欧拉筛中  发现时间更多了????


def three_sum(lis,n):
    '''双指针求数组中三数和等于n的元组'''
    leng = len(lis)
    for i in range(leng):
        l = i
        r = leng - 1
        while l <= r:
            if lis[i] + lis[l] + lis[r] == n:
                return (lis[i], lis[l], lis[r])
            elif lis[i] + lis[l] + lis[r] < n:
                l += 1
            else:
                r -= 1

def ola(n):
    '''欧拉筛求素数'''
    lis = [True]*n
    lis[0] = lis[1] = False
    prime = []
    for i in range(2,n):
        if lis[i]:
            prime.append(i)
        if three_sum(prime,n):
            return three_sum(prime,n)
        for p in prime:
            if p*i >= n:
                break
            lis[i*p] = False
            if i % p == 0:
                break
    # return prime

def main():
    while 1:
        n = int(input())
        if n < 6:
            break
        res = ola(n)
        for i in res:
            print(i,end=" ")

if __name__ == '__main__':
    main()

 然后,考虑过不使用列表,改用字典,提高查找性能?

很很很遗憾,这些方案统统没通过.


# 最后的最后,参看了以下方案,问题得以解决
''''在做这道时得需要对哥德巴赫猜想有一定的了解,从而对题目进行一定的变形,
要不然就得超时。

今日常见的猜想陈述为欧拉的版本,即任一大于2的偶数都可写成两个素数之和,亦称为“强哥德巴赫猜想”或“关于偶数的哥德巴赫猜想”。
从关于偶数的哥德巴赫猜想,可推出:任何一个大于7的奇数都能被表示成三个奇质数的和。后者称为“弱哥德巴赫猜想”或“关于奇数的哥德巴赫猜想”。

先对输入数据为偶数的进行处理:
对输入数据减去二,剩下的值依然是个偶数,然后再通过暴力把剩下的两个素数求出来;

当输入数据为奇数时:
对输入数据减去3,剩下的值便是偶数,仍是通过暴力把剩下的两个素数求出来;
————————————————
版权声明:本文为CSDN博主「渐入佳境!!」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/m0_46557490/article/details/119220492'''
def isPrime(n):
    for i in range(2,int(n**0.5)+1):
        if n % i == 0:
            return False
    else:
        return True


def main():
    while 1:
        n = int(input())
        if n < 6:
            break
        if n % 2 == 0:# 偶数的情况
            for i in range(2,n-2):
                if isPrime(i) and isPrime(n-2-i):
                    print(2,i,n-2-i)
                    break
        else:
            for i in range(2,n-3):
                if isPrime(i) and isPrime(n-3-i):
                    lis = [i,3,n-3-i]
                    lis.sort()
                    for i in lis:
                        print(i,end=' ')
                    break


if __name__ == '__main__':
    main()

# 结果是暴力枚举 完胜 欧拉筛 + 双指针 哈哈哈哈哈哈哈哈哈哈

使用以上代码通过,然后复制了CSDN博主「渐入佳境!!」的c++代码做对比

 c++还是性能之王啊

可是C++好难学!!!

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值