新手级民科

导航

素数与第素数个素数的和能生成$10^{10}$内的所有偶数

1.概念介绍

加性组合中,两个集合 \(A\)\(B\)加法集和集 \(A+B\) 定义为\(A\)中任意元素 \(a_i\)\(B\) 中任意元素 \(b_j\) 之和 \(a_i+b_j\) 构成的集合,用 \(|A|\) 表示集合 \(A\) 中元素的数量,当 \(A\)\(B\) 都不为空集时($ |A|\cdot|B|>0$),有不等式

\[|A|+|B|-1\le |A+B|\le |A|\cdot|B| \]

\(n\)以内素数的数量 \(|P(n)|\simeq\frac{n}{\log n}\),根据哥德巴赫猜想,所有4以上的偶数都可表示为两个素数之和,这至少意味着素数集和集的元素数量 \(|P+P|\simeq n\)。事实上这是一个比较宽松的猜想,即使取素数集中的一个子集就有可能覆盖所有偶数,例如根据杜伯纳猜想,所有4208以上的偶数都能表示成两个孪生素数之和,这意味着\(n\)以内孪生素数的数量至少达到了\(\sqrt{n}\)

那么,能不能用整个素数集与另一个更小的集合求和集,来覆盖偶数集呢?最优情况是选一个 \(n\) 以内元素数量只有 \(\log n\) 的集合(类似等比数列),使得达到和集元素数量的理论上限。但经过部分程序验证,单独的等比数列 \(a_{i+1}=a_iq\) 和类等比递推数列 \(a_{i+1}=a_iq+d\) 是不行的,也许几个等比数列的组合可以,待继续验证。

在探究过程中,一个新的发现是素数集
P=[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43...]
和第素数个素数构成的集合
PP=[3, 5, 11, 17, 31, 41, 59, 67, 83, 109, 127, ...]
的和集 \(P+PP\) 可生成 \([6,10^{10}]\) 的全部偶数,更大范围由于个人电脑性能限制尚未验证。感觉这个发现比较有价值,特发出来让大家一起讨论。

2.代码

本人编程所用的Python代码如下:

from time import time

def getPrimeSet_List_isPrime(n=10**6):
    """
    同时生成n以内的素数集合方便查找、isPrime数组方便o(1)查找、primeList方便遍历
    """
    n=max(2,n+1)
    isPrime=[True]*n
    isPrime[0]=False
    isPrime[1]=False
    for i in range(1,n,1):
        if isPrime[i]:
            for j in range(i*i,n,i):
                isPrime[j]=False
    primeList=[i for i in range(n) if isPrime[i]]
    primeSet=set(primeList)
    return primeSet,primeList,isPrime

def 素数集与第素数个素数的和集能否覆盖n以内所有奇数或偶数(n=10**3,idxOffset=-1):
    """
    验证素数集与第素数个素数的和集能覆盖多少奇数、偶数
    n     idxOffset  奇数未覆盖率  偶数未覆盖率  未覆盖偶数  耗时
    10^7  -1               1          4e-7     [2,4]      18s
    """
    primeSet,primeList,isPrime=getPrimeSet_List_isPrime(n)
    print(f"{n=},{idxOffset=}")
    print(f"primeList={primeList[:50]}")
    pprimes=[primeList[i+idxOffset] for i in primeList if i+idxOffset<len(primeList)]
    print(f"primeprimeList={pprimes[:50]}")
    isCovered=[False]*(n+1)
    print("逐项集合查找法\n未覆盖的偶数:")
    pn=idxv=len(primeList)
    for e in range(2,n+1,2):
        for idx in primeList:
            if idx+idxOffset>=pn or primeList[idx+idxOffset]>e:
                break
            v=e-primeList[idx+idxOffset]
            if v in primeSet:
                isCovered[e]=True
                break
        if not isCovered[e]:
            print(e,end=",")
    print()
    uncoverdOddNums=[i for i in range(1,n+1,2) if isCovered[i]==False]
    uncoverdEvenNums=[i for i in range(2,n+1,2) if isCovered[i]==False]
    print(f"未覆盖偶数:{uncoverdEvenNums[:50]}...{uncoverdEvenNums[-50:]}")
    print(f"奇数未覆盖率{len(uncoverdOddNums)*2/n},偶数未覆盖率{len(uncoverdEvenNums)*2/n}")
    return uncoverdOddNums,uncoverdEvenNums

if __name__=="__main__":
    startTime=time()
    素数集与第素数个素数的和集能否覆盖n以内所有奇数或偶数()
    print(f"运行时间{time()-startTime}s")

刚开始程序采用双重循环暴力遍历,时间复杂度约为\(O\left(\frac{n^2}{(\log n)^3}\right)\),验证到 \(n=10^6\) 时程序运行时间已达2分钟;当前版本改用了逐项遍历偶数,用集合快速查询,时间复杂度没变,但验证完 \(10^7\) 内的偶数只需18秒。后来移植到C++,并采用多线程检查,效率更快,分别在3秒、8分钟内验证完\(10^7\)\(10^9\)内的偶数。C++代码如下:

#include<iostream>
#include<vector>
#include<cmath>
#include<thread>
#include <mutex>
#include <condition_variable>
#include<stdlib.h>
#include<ctime>
#include<future>
#include<algorithm>
using namespace std;
using ull=unsigned long long;
mutex mtx;

void printVector(vector<ull>&arr,ull startIdx=0,ull maxLen=50){
    cout<<"{";
    startIdx=fmax(0,startIdx);
    if(startIdx){
        cout<<"...,";
    }
    for(ull i=startIdx;i<fmin(arr.size(),startIdx+maxLen);i++){
        cout<<arr[i]<<",";
    }
    if(startIdx+maxLen<arr.size()){
        cout<<"...";
    }
    cout<<"};"<<endl;
    return;
}

vector<ull>genePrimeList(ull n=10e+6){
    n++;
    vector<bool>isPrime(n,true);
    vector<ull>primeList;
    for(ull i=2;i<n;i++){
        if(isPrime[i]){
                primeList.emplace_back(i);
            for(ull j=i*i;j<n;j+=i){
                isPrime[j]=false;
            }
        }
    }
    return primeList;
}

ull n=1e9;
vector<bool>isCovered(n/2+1,false);//索引[n/2]存偶数n
vector<ull>primeList=genePrimeList(n),pprimes;
ull pn=primeList.size();

bool coverEvenNumInterval(ull sten,ull eden){//二分搜索
    ull idx;
    ull val;
    for(ull e=(sten/2)*2;e<eden;e+=2){
        for(auto pidx:primeList){
            if(pidx>pn||primeList[pidx-1]>e){
                break;
            }
            val=e-primeList[pidx-1];
            idx=lower_bound(primeList.begin(),primeList.end(),val)-primeList.begin();
            if(idx<pn&&primeList[idx]==val){//如果想筛选素数集子序列,可增加一个条件,例 && idx%4==2
                isCovered[e>>1]=true;
                break;
            }
        }
        if(isCovered[e>>1]==false){
            unique_lock<mutex> lock(mtx);//不使用lock时多个线程的打印信息混杂在一起
            cout<<"sten="<<sten<<", uncovered "<<e<<endl;
            lock.unlock();
        }

    }
    return true;
}

vector<ull>primesAndPrimeOfPrimeIndexCoverEvenNumber_multiThread(ull n,int threadNum=8){
    /**
    n是查找范围上限
    indexOffset调整第素数个素数的索引,使变成第(素数+indexOffset)个素数
    */
    cout<<"n="<<n<<",threadNum="<<threadNum<<endl;
    cout<<"primeList="<<endl;
    printVector(primeList);

    vector<ull>intervalNodes;
    ull intervalLen=(n/threadNum+1);
    if(intervalLen&1){
        intervalLen++;
    }
    for(int i=0;i<threadNum;i++){
        intervalNodes.emplace_back(i*intervalLen);
    }
    intervalNodes.emplace_back(n+1);
    for(int i=0;i<threadNum;i++){
        thread a(coverEvenNumInterval,intervalNodes[i],intervalNodes[i+1]);
        if(i<threadNum-1){
            a.detach();
        }else{
            a.join();
        }
    cout<<"thread end"<<endl;
    vector<ull>uncoveredEvenNumber;
    for(ull i=1;i<isCovered.size();i++){
        if(isCovered[i]==false){
            uncoveredEvenNumber.emplace_back(i<<1);
        }
    }
    cout<<"uncoveredEvenNumber size="<<uncoveredEvenNumber.size()<<"\n";
    cout<<"head="<<endl;
    printVector(uncoveredEvenNumber);
    cout<<"end="<<endl;
    printVector(uncoveredEvenNumber,uncoveredEvenNumber.size()-50);
    return uncoveredEvenNumber;
}

int main(){
    primesAndPrimeOfPrimeIndexCoverEvenNumber_multiThread(n);
    return 0;
}

\(n\)每增加一个数量级,内存占用10倍增长,程序运行耗时百倍增长,由于电脑性能与算法复杂度原因,个人验证更大的偶数的空间已经不太大了,希望大家讨论一下有没有时间复杂度更低的算法,或者用更好的电脑帮忙验证一下对于 \(10^{10}\) 以上的大偶数,这个猜想(每个 \(4\) 以上的偶数可以表示为一个素数和一个“第素数个素数”之和)是否仍正确。

也许这个猜想已经有人以前说过了,有知道的的话麻烦说一下;这个猜想表述如此简单,被前人讨论过的几率还是挺大的。

即使最终被验证在更大范围内不正确,也已经是个有趣的巧合了。

3.总结及推广

素数集有序列表Primes=[2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43...]
Primes[idx::d]表示第一项为Primes[idx]、每隔d个元素取一项形成的索引为等差数列的素数子集,
例如Primes[0::2]=[2, 5, 11, 17, 23, 31, 41...]

Primes[idx::d]与第素数个素数集\(PP\)的和集覆盖\([4,10^9]\)内的偶数时,未覆盖的偶数情况:

    素数集子集              未覆盖偶数数量
    primes=primes[0::1]    [4,4] 共 1 个
    primes[0::2]           [4,1552] 共 37 个
    primes[1::2]           [4,992] 共 14 个
    primes[0::4]           [4,5704] 共 123 个
    primes[1::4]           [4,8534] 共 92 个
    primes[0::8]           [4,19654] 共 443 个

因此下一个猜想是:用素数集中的任意等差索引子序列primes[idx::d]与第素数个素数集的和集覆盖偶数集时,只会漏掉有限个偶数

posted on 2024-03-01 00:42  新手级民科  阅读(18)  评论(0编辑  收藏  举报