我将此代码翻译成处理与原始代码有何不同?

问题描述

我已将主要测试代码this paperhere 是指向原始代码链接)翻译成处理。在测试时,我发现它适用于 10,000,000 以下的数字,但它会跳过一些高于此的素数。

这是我的翻译(除了表格是相同的)。

boolean is_SPRP(int n,int a) {
  int d = n-1,s = 0;
  while ((d&1)==0) { s++; d>>>=1; }
  long cur = 1,pw = Integer.toUnsignedLong(d);
  while (pw!=0) {
    if ((pw&1)!=0) { cur = Long.remainderUnsigned((cur*a),n); }
    a = int(Long.remainderUnsigned((long)a*a,n));
    pw >>>= 1;
  }
  if (Long.compareUnsigned(cur,1)==0) { return(true); }
  for (int r=0; r<s; r++) {
    if (Long.compareUnsigned(cur,n-1)==0) { return(true); }
    cur = Long.remainderUnsigned((cur*cur),n);
  }
  return(false);
}
boolean isPrime(int x) {
  if (x==2 || x==3 || x==5 || x==7) { return(true); }
  if (x%2==0 || x%3==0 || x%5==0 || x%7==0) { return(false); }
  if (x<121) { return(x>1); }
  long h = x;
  h = ((h >>> 16) ^ h) * 0x45d9f3b;
  h = ((h >>> 16) ^ h) * 0x45d9f3b;
  h = ((h >>> 16) ^ h) & 255;
  return is_SPRP(x,bases[int(h)]);
}

编辑:我发现了问题。处理的 int(long) 转换为 float,然后转换为 int,这会导致舍入错误。使用 (int)long 解决了这个问题。 这是代码的工作(稍微优化)版本。

int bases[]={15591,2018,166,7429,8064,16045,10503,4399,1949,1295,2776,3620,560,3128,5212,2657,2300,2021,4652,1471,9336,4018,2398,20462,10277,8028,2213,6219,620,3763,4852,5012,3185,1333,6227,5298,1074,2391,5113,7061,803,1269,3875,422,751,580,4729,10239,746,2951,556,2206,3778,481,1522,3476,2487,3266,5633,488,3373,6441,3344,17,15105,1490,4154,2036,1882,1813,467,3307,14042,6371,658,1005,903,737,1887,7447,1888,2848,1784,7559,3400,951,13969,4304,177,41,19875,3110,13221,8726,571,7043,6943,1199,352,6435,165,1169,3315,978,233,3003,2562,2994,10587,10030,2377,1902,5354,4447,1555,263,27027,2283,305,669,1912,601,6186,429,1930,14873,1661,524,3577,236,2360,6146,2850,55637,1753,4178,8466,222,2579,2743,2031,2226,2276,374,2132,813,23788,1610,4422,5159,1725,3597,3366,14336,579,1375,10018,12616,9816,1371,536,1867,10864,857,5788,434,8085,17618,727,3639,1595,4944,2129,2029,8195,8344,6232,9183,8126,1870,3296,7455,8947,25017,541,19115,368,566,5674,411,522,1027,8215,2050,6544,10049,614,774,2333,3007,35201,4706,1152,1785,1028,1540,3743,493,4474,2521,26845,8354,864,18915,5465,2447,42,4511,1660,1249,6259,2553,304,272,7286,73,6554,899,2816,5197,13330,7054,2818,3199,811,922,350,7514,4452,3449,2663,4708,418,1621,1171,3471,88,11345,412,1559,194};

boolean is_SPRP(int n,int a) {
  int d = (n-1)>>>1,s = 1;
  while ((d&1)==0) { s++; d>>>=1; }
  long cur = 1;
  while (d!=0) {
    if ((d&1)!=0) { cur = Long.remainderUnsigned(cur*a,n); }
    a = (int)Long.remainderUnsigned((long)a*a,n);
    d >>>= 1;
  }
  if (cur==1) { return(true); }
  for (int r=0; r<s; r++) {
    if (cur==n-1) { return(true); }
    cur = Long.remainderUnsigned((cur*cur),n);
  }
  return(false);
}

boolean isPrime(int x) {
  if (x==2 || x==3 || x==5 || x==7) { return(true); }
  if (x%2==0 || x%3==0 || x%5==0 || x%7==0) { return(false); }
  if (x<121) { return(x>1); }
  long h = x;
  h = ((h >>> 16) ^ h) * 0x45d9f3b;
  h = ((h >>> 16) ^ h) * 0x45d9f3b;
  h = ((h >>> 16) ^ h) & 255;
  return is_SPRP(x,bases[(int)h]);
}

此版本仅适用于有符号整数。由于某种原因简单地像这样修改它会导致剩余操作失败。

boolean is_SPRPUnsigned(int n,int a) { //broken (i think)
  int d = (n-1)>>>1,n);
    d >>>= 1;
  }
  if (cur==1) { return(true); }
  for (int r=0; r<s; r++) {
    if ((int)cur==n-1) { return(true); }
    cur = Long.remainderUnsigned((cur*cur),n);
  }
  return(false);
}

boolean isPrimeUnsigned(int x) { //not broken (i think)
  if (x==2 || x==3 || x==5 || x==7) { return(true); }
  if (Integer.remainderUnsigned(x,2)==0 || Integer.remainderUnsigned(x,3)==0 || Integer.remainderUnsigned(x,5)==0 || Integer.remainderUnsigned(x,7)==0) { return(false); }
  if (Integer.compareUnsigned(x,121)<0) { return(Integer.compareUnsigned(x,1)>0); }
  long h = Integer.toUnsignedLong(x);
  h = ((h >>> 16) ^ h) * 0x45d9f3b;
  h = ((h >>> 16) ^ h) * 0x45d9f3b;
  h = ((h >>> 16) ^ h) & 255;
  return is_SPRPUnsigned(x,bases[(int)h]);
}

但是像这样进一步修改它可以解决这个问题。

boolean is_SPRPUnsigned(int n,int a) {
long ln=Integer.toUnsignedLong(n);
  int d = (n-1)>>>1,s = 1;
  while ((d&1)==0) { s++; d>>>=1; }
  long cur = 1;
  int debug=0;
  while (d!=0) { println(debug); debug++;
    long la=Integer.toUnsignedLong(a);
    if ((d&1)!=0) { cur = Long.remainderUnsigned(cur*la,ln); println("do"); }
    a = (int)Long.remainderUnsigned(la*la,ln);
    d >>>= 1;
    println(cur,a,Integer.toUnsignedString(a));
  }
  if (cur==1) { return(true); }
  for (int r=0; r<s; r++) {
    if ((int)cur==n-1) { return(true); }
    cur = Long.remainderUnsigned((cur*cur),ln);
  }
  return(false);
}

最终编辑:我现在看到将其转换为使用无符号整数的错误在于乘法。要将 int 和 long 相乘,必须将 int 转换为 long。转换是自动完成的,但它假定它是一个有符号整数。手动转换可防止这种情况发生。

解决方法

正如@dxiv 已经指出的,即使 long 是 64 位,它是有符号的,所以它的最大值是 9,223,372,036,854,775,807 而 uint64 的最大值是 18,446,744,073,709,5,551 因此你赢了大值的预期结果。

您可以尝试使用 BigInteger 代替 long

import java.math.BigInteger;

int bases[]={15591,2018,166,7429,8064,16045,10503,4399,1949,1295,2776,3620,560,3128,5212,2657,2300,2021,4652,1471,9336,4018,2398,20462,10277,8028,2213,6219,620,3763,4852,5012,3185,1333,6227,5298,1074,2391,5113,7061,803,1269,3875,422,751,580,4729,10239,746,2951,556,2206,3778,481,1522,3476,2487,3266,5633,488,3373,6441,3344,17,15105,1490,4154,2036,1882,1813,467,3307,14042,6371,658,1005,903,737,1887,7447,1888,2848,1784,7559,3400,951,13969,4304,177,41,19875,3110,13221,8726,571,7043,6943,1199,352,6435,165,1169,3315,978,233,3003,2562,2994,10587,10030,2377,1902,5354,4447,1555,263,27027,2283,305,669,1912,601,6186,429,1930,14873,1661,524,3577,236,2360,6146,2850,55637,1753,4178,8466,222,2579,2743,2031,2226,2276,374,2132,813,23788,1610,4422,5159,1725,3597,3366,14336,579,1375,10018,12616,9816,1371,536,1867,10864,857,5788,434,8085,17618,727,3639,1595,4944,2129,2029,8195,8344,6232,9183,8126,1870,3296,7455,8947,25017,541,19115,368,566,5674,411,522,1027,8215,2050,6544,10049,614,774,2333,3007,35201,4706,1152,1785,1028,1540,3743,493,4474,2521,26845,8354,864,18915,5465,2447,42,4511,1660,1249,6259,2553,304,272,7286,73,6554,899,2816,5197,13330,7054,2818,3199,811,922,350,7514,4452,3449,2663,4708,418,1621,1171,3471,88,11345,412,1559,194};  

// 0x45d9f3b as BigInt
final BigInteger HEX_45d9f3b = new BigInteger(new byte[]{(byte)0x04,(byte)0x5d,(byte)0x9f,(byte)0x3b});
final BigInteger HEX_ff = new BigInteger("255");

boolean isSPRP(int n,int a) {
    int d = n-1,s = 0;
    while ((d & 1) == 0) {  
      ++s; 
      d >>= 1;  
    }
    //uint64_t cur = 1,pw = d;
    BigInteger cur = new BigInteger("1");
    BigInteger pw  = new BigInteger(""+d);
    BigInteger abi = new BigInteger(""+a);
    BigInteger nbi = new BigInteger(""+n);
    while (pw.intValue() > 0) { 
        if (pw.and(BigInteger.ONE).intValue() > 0){
          //cur = (cur*a) % n;
          cur = cur.multiply(abi).mod(nbi);
        }
        //a = ((uint64_t)a*a) % n;
        abi = abi.multiply(abi).mod(nbi);
        //pw >>= 1;
        pw = pw.shiftRight(1);
    }   
    if (cur == BigInteger.ONE) return true;
    for (int r=0; r < s; r++) {
        //if (cur == n-1) return true;
        if(cur.equals(nbi.subtract(BigInteger.ONE))){
          return true;
        }
        //cur = (cur*cur) % n;
        cur = cur.multiply(cur).mod(nbi);
    }
    return false;
}       
    
boolean isPrime(int x) { 
    if (x==2 || x==3 || x==5 || x==7) return true;
    if (x%2==0 || x%3==0 || x%5==0 || x%7==0) return false;
    if (x<121) return (x>1);
    BigInteger h = new BigInteger(""+x);
    //h = ((h >> 16) ^ h) * 0x45d9f3b;
    h = h.shiftRight(16).xor(h).multiply(HEX_45d9f3b);
    //h = ((h >> 16) ^ h) * 0x45d9f3b;
    h = h.shiftRight(16).xor(h).multiply(HEX_45d9f3b);
    //h = ((h >> 16) ^ h) & 255;
    h = h.shiftRight(16).xor(h).and(HEX_ff);
    return isSPRP(x,bases[h.intValue()]);
}   

请注意,以上内容并未经过彻底测试,因此实际上可能存在错误。 希望它说明使用 BigInt 足以继续前进。

值得注意的是,BigInt 有可能有用的 isProbablePrime(int)

此外,您可以使用 JNI 包装原始代码,保持实现/数据类型不变,只需在 C++ 和 Java 之间进行接口/桥接即可。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...