如何解决我将此代码翻译成处理与原始代码有何不同?
我已将主要测试代码从 this paper(here 是指向原始代码的链接)翻译成处理。在测试时,我发现它适用于 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 之间进行接口/桥接即可。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。