找回密码
 欢迎注册
楼主: 无心人

[原创] 10^28开始的10^14个整数的素性概率性测试算法实践报告

[复制链接]
发表于 2020-11-30 15:40:30 | 显示全部楼层
无心人 发表于 2020-11-26 16:18
用这种方式,测试的每10^7需要34.87s
10^14需要4035天,还需要优化~~~

你用的是什么软件?郭先强的软件?

点评

你没看到前面贴出的代码吗?  发表于 2020-11-30 16:55
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2020-11-30 17:04:13 | 显示全部楼层
100亿20并发测试开始,明天看结果
1000万4并发10秒

1479秒100亿,20并发,无结果~

点评

我应该干到了600亿。我的测试是前100亿有155111392个通过BPSW跟两次Miller-Rabin算法的PRP,0个伪素数  发表于 2020-11-30 18:15
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-11-30 17:06:46 | 显示全部楼层
离成功优化powermod大概只差一步了
  1. fn pow_mod(mut base:u128,mut exp:u128,module:u128)->u128{
  2.     // assert!(module < 1<<94 && module&1==1 && exp>0)
  3.     let mut acc = 1;
  4.     //let m64=(module>>64) as u64;
  5.     let m126=(1<<126)%module;
  6.     let m157=(m126<<31)%module;
  7.     let mulmod=|mut x,mut y|{
  8.         let [lx,ly,mut hx,hy]=[x&9223372036854775807,y&9223372036854775807,x>>63,y>>63];
  9.         x=lx*hy+ly*hx;
  10.         y=lx*ly+((x&9223372036854775807)<<63);x>>=63;//y=low_mid, x=high_mid<<126
  11.         //lx*ly+y+x<<126+hx*hy<<126
  12.         x+=hx*hy;
  13.         //lx*ly+y+x<<126
  14.         hx=x&2147483647;
  15.         x>>=31;
  16.         //(y+hx*m126+x*m157)%module
  17.         y+=hx*m126+x*m157;
  18.         //y-(((y>>64)as u64)/m64) as u128*module
  19.         y%module
  20.         //lx.max(ly) < 1<<63 ==> lx*ly < 1<<126
  21.         // y < 9223372036854775807<<63 < 1<<126
  22.         //hx <2147483647*module< 1<<31+94 = 1<<126
  23.         // x <(module*module>>(63*2+31))*module < 1<<(94*2-63*2-31+94)=1<<125
  24.         //Thus, lx*ly+y+hx*m126+x*m157 < 1<<128, safe to add them together and executing % ops.
  25.     };
  26.     while exp > 1 {
  27.         if (exp & 1) == 1 {
  28.             acc = mulmod(acc,base);
  29.             base = mulmod(base,base);
  30.         }else{
  31.             base = mulmod(base,base);
  32.         }
  33.         exp >>= 1;
  34.     }
  35.     mulmod(acc,base)
  36. }
  37. fn main(){
  38.     const CONSTARR:[u128;7]=[1u128<<1,1u128<<2,1u128<<4,1u128<<8,1u128<<16,1u128<<32,1u128<<64];
  39.     let now=std::time::Instant::now();
  40.     let arguments: Vec<String> = std::env::args().collect();
  41.     let begin=arguments.get(2).and_then(|x|Some(x.parse::<u128>().unwrap_or(0))).unwrap_or(0);
  42.     let len=arguments.get(1).and_then(|x|Some(x.parse::<u128>().unwrap_or(10000000))).unwrap_or(10000000);
  43.     let mut am1=10000000000000000000000000000u128+begin*len;
  44.     let mut a=10000000000000000000000000001u128+begin*len;
  45.     println!("from={},len={}",a,len);
  46.     let mut b=2;
  47.     let mut am1d=0;
  48.     let mut prime=0u64;
  49.     let mut psp=0u64;
  50.     for _i in 0..len>>1{
  51.         let aa=am1.trailing_zeros().min(6);
  52.         am1d=&am1>>aa;
  53.         if {b=CONSTARR[aa as usize]; pow_mod(b,am1d,a)==1u128}{//Fermat
  54.             psp+=1;/*
  55.             if {b.assign(2u8);//Miller-Rabin
  56.                 let aa=a.find_one(0).unwrap();
  57.                 am1d.assign(&am1>>aa);
  58.                 let _=b.pow_mod_mut(&am1d,&a);
  59.                 (b==1 || b==am1 )||{
  60.                     let mut f=false;
  61.                     for _ in 0..aa{
  62.                         let _=b.pow_mod_mut(&Integer::from(2),&a);
  63.                         if b==am1{f=true;break}
  64.                     }
  65.                     f
  66.                 }
  67.             }{
  68.                 if a.is_probably_prime(25)==rug::integer::IsPrime::No{
  69.                     println!("{} is SPSP(2)",a)
  70.                 }else{prime+=1}
  71.             }else{println!("{} is PSP(2)",a)}*/
  72.         }
  73.         a+=2;
  74.         am1+=2;
  75.     }
  76.     println!("{}.{},{},{:?}",a,prime,psp-prime,now.elapsed());
  77. }
复制代码
这个程序是gmp的1/2,但如果拿掉取模步,速度是gmp的两倍多。
另外,或许应该直接写C调GMP,GMP写了Miller-Rabin测试,不知速度如何,但小改一下应该比Fermat快。
毕竟Miller-Rabin可以少算好几次乘法。

点评

我改了Primes.jl,正准备看看能不能直接窃用GMP的Miller-Rabin算法  发表于 2020-11-30 19:08
还有就是,我换成C,要重新实现二次域幂模n,想起来就头大,那个NTL应该有二次域,但是不会用  发表于 2020-11-30 19:04
其实,算PSP,要比算SPSP多不少工作,主要是统计的需要吧  发表于 2020-11-30 18:55
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-11-30 18:13:22 | 显示全部楼层
本帖最后由 .·.·. 于 2020-11-30 19:06 编辑
无心人 发表于 2020-11-30 17:04
100亿20并发测试开始,明天看结果
1000万4并发10秒

??
把Primes.jl改一改吧
改完,1000万不并发也就11秒

刚刚试了一下
如果一定不想改Primes.jl的话
这样启动Julia(linux,windows最好先用set改一下环境变量)
JULIA_NUM_THREADS=6 julia

  1. #不使用Primes,直接手写一个isprime
  2. isprime(x::BigInt) = ccall((:__gmpz_probab_prime_p,:libgmp),Cint, (Ref{BigInt}, Cint), x, 25) > 0
  3. const b = big(10)^28

  4. function fermatProbablePrimeTest2(n)
  5.     a = powermod(2, n-1, n)
  6.     return a == 1
  7. end

  8. function millerRabinProbablePrimeTest( n, b )
  9.     k = trailing_zeros(n-1)
  10.     m = (n-1) >> k
  11.     a = powermod(b, m, n)
  12.     if (a == 1) || (a == (n-1))
  13.         return true
  14.     else
  15.         for i in 1:k-1
  16.             a = powermod(a, 2, n)
  17.             if a == n - 1
  18.                 return true
  19.             end
  20.         end
  21.     end
  22.     return false
  23. end
  24. @time begin
  25.     Threads.@threads for i in range(1, step = 2, stop = 9999999)
  26.         test = b + i
  27.         if fermatProbablePrimeTest2(test)
  28.             if millerRabinProbablePrimeTest(test, 2)
  29.                 if !isprime(test)
  30.                     println("$test is SPSP(2)")
  31.                 end
  32.             else
  33.                 println("$test is PSP(2)")
  34.             end
  35.         end
  36.     end
  37. end
复制代码


点评

现在换Frobenius测试重新扫下10^10  发表于 2020-11-30 19:11
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2020-11-30 21:59:19 | 显示全部楼层
我刚思考了下,用frobenius扫同样的10^10的时间太长了,不太合适(现在还没出结果~~~,看来frobenius测试实在不适合批量测试~~~)
还是用2,3,5,7,11,13六个MR测试代替吧
这种理论上,有1/4^6的会漏掉,大概是0.0244%,实际上很小
下面可能测试下,六次MR测试失败的概率,是不是会低于亿分之一~~~~

因为我们是统计,所以,对于很小概率的错误,是可以允许的
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-11-30 22:40:16 | 显示全部楼层
本帖最后由 .·.·. 于 2020-12-1 00:56 编辑
无心人 发表于 2020-11-30 21:59
我刚思考了下,用frobenius扫同样的10^10的时间太长了,不太合适(现在还没出结果~~~,看来frobenius测试实 ...


……你试试去掉isprime扫前10^7个数字
如果用isprime(x::BigInt) = ccall((:__gmpz_probab_prime_p,:libgmp),Cint, (Ref{BigInt}, Cint), x, 25) > 0
开销也就额外增加1/10,而不是10倍
整个程序我已经给了
难道有BUG不能执行吗?

我昨天扫了600亿,用了大约10300秒,一个Fermat伪素数都没看见(用Rust)
换Julia,6线程大概13000秒?
isprime(x::BigInt) = ccall((:__gmpz_probab_prime_p,:libgmp),Cint, (Ref{BigInt}, Cint), x, 25) > 0
这一行可以带来非常可怕的性能提升

点评

我那个100亿的frobenius还没出结果~~~  发表于 2020-12-1 10:13
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2020-12-1 10:16:49 | 显示全部楼层
我说下我的筛法思路
初始化一个表small,表里是10000内大于3的素数和模2的阶

建立数组b[],长度是s
for i=0 to (s-1)/2
        b[2*i]=0
        b[2*i+1]=1

1、设定初始值为base,则s[i]位置代表的整数是base+i
for (p, d) in small
        if d是奇数
                d = 2*d
        j = base/p
        start=j*p
        if start是偶数
                start += p
                j=j+1
        stopJ=(base+s-1)/p
        while j<=stopJ
                i = start - base
                if (j%d!=1)
                  s[i] = 0
                start=start+2*p
                j=j+2
               

列表里所有的p处理完后
2、遍历s[i]
  for i in 0..s-1
        if s[i] == 1
          s[i] = FermatTest(base+i, 2)
       
3、位标1的,对2做MR测试,没通过的置0,输出PSP
4、位标1的,对3,5,7,11,13做MR测试,没通过的输出SPSP

毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2020-12-1 14:43:10 | 显示全部楼层
有没有人写段汇编的128位模乘啊
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
发表于 2020-12-1 18:54:38 | 显示全部楼层
无心人 发表于 2020-12-1 14:43
有没有人写段汇编的128位模乘啊

我不知道该怎么写128bit的模
但如果不做这次取模,fermat的速度可以乘以2
这大概表示,fermat的优化已经到头了,就算汇编未必比gmp快多少
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
 楼主| 发表于 2020-12-1 19:00:06 | 显示全部楼层
.·.·. 发表于 2020-12-1 18:54
我不知道该怎么写128bit的模
但如果不做这次取模,fermat的速度可以乘以2
这大概表示,fermat的优化已 ...

我感觉模乘不需要除法
毋因群疑而阻独见  毋任己意而废人言
毋私小惠而伤大体  毋借公论以快私情
您需要登录后才可以回帖 登录 | 欢迎注册

本版积分规则

小黑屋|手机版|数学研发网 ( 苏ICP备07505100号 )

GMT+8, 2024-11-21 21:34 , Processed in 0.036016 second(s), 16 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表