Нечетная математическая ошибка в Java
Я пишу программу для демонстрации вероятностного тестирования Миллера-Рабина на Java. Код в значительной степени готов...
import java.util.Random;
import java.util.Scanner;
/**
* Program to demonstrate Miller-Rabin primality testing
*
* @author Nick Gilbert
*/
public class MillerRabin
{
public static void main(String[] args)
{
//Setting up for algorithm
Scanner in = new Scanner(System.in);
Random rn = new Random();
int n = 0, k = 0, m = 0, a = 0;
double b = 0;
boolean probablyPrime = false;
//Asking user for an odd n
do
{
System.out.print("Enter an odd number to test for primality: ");
n = in.nextInt();
}
while(n % 2 == 0);
//Calculating k and m
m = n - 1;
while(m % 2 == 0)
{
m /= 2;
k++;
}
//Generating random a
//a = rn.nextInt(n-1);
//Outputting numbers that will be used in algorithm
System.out.println("k = " + k);
System.out.println("m = " + m);
System.out.println();
a = 86;
System.out.println("A = " + a);
//Running the algorithm
//b_{0}
b = Math.pow(a, m) % n;
System.out.println("b0 = " + b);
if(Math.abs(b) == Math.abs(1 % n)) //Dealing with +/- case via absolute value
{
probablyPrime = true;
}
else
{
//b_{1-(k-1)}
for(int i = 1; i < k; i++) //Going to k-1
{
b = Math.pow(b, 2) % n;
System.out.println("b" + i + " = " + b);
if(Math.abs(b) == Math.abs(1 % n)) //Dealing with +/- case via absolute value
{
probablyPrime = true;
break;
}
}
}
//Printing result
if(probablyPrime)
{
System.out.println("Probably Prime");
}
else
{
System.out.println("Definitely Composite");
}
}
}
Я жестко запрограммировал 86 как свою ценность, чтобы продемонстрировать свою проблему. Там, где он вычисляет b впервые, подняв a до m и взяв модуль n, математика неверна. Вместо того, чтобы дать b0 из 86, который является правильным ответом для 86^19 % 153, он дает мне b0, равный 107. Я проверил свои значения в отладчике, и они правы. Я также проверил значение ^m, и это дает мне 86 ^ 19, поэтому проблема возникает с модульной частью. К сожалению, я не знаю, что отбрасывает математику.
3 ответа
double
Точность в Java (и любой системе IEEE) имеет только 15-16 цифр точности. Если вы используете число, которое больше этого, вы получите ошибку представления.
Скорее всего, вам нужно использовать BigInteger, который не только обрабатывает произвольную точность, но и имеет метод, оптимизированный для мощности и модуля.
// 86^19 % 153
BigInteger result = BigInteger.valueOf(86).modPow(BigInteger.valueOf(19), BigInteger.valueOf(153));
System.out.println(result);
печать
86
Здесь Math.pow возвращает двойное значение, поэтому взятие модуля двойного не поможет (Никогда не используйте Мод на двойном, никто не несет ответственности за то, что вы получаете).
и обратите внимание, что (89^19) составляет примерно 2^122, поэтому беззнаковый длинный (2^64-1) не будет содержать эти числа. и double имеет точность 2^53(никогда не используйте double для mod, теория чисел на целых числах). Попробуйте меньшее значение или используйте класс BigInteger.
Примитивные типы данных имеют заданные размеры, которые могут ограничивать их точность.
В Java есть класс BigInteger, который может работать для вашего сценария.