首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > JAVA > Java相关 >

分享几个素数算法。解决办法

2012-01-29 
分享几个素数算法。。。如题,分享几个自己精心收集并改进的素数算法:public class PrimeTool{/*** 判断是否为

分享几个素数算法。。。
如题,分享几个自己精心收集并改进的素数算法:
public class PrimeTool
{
  /**
  * 判断是否为素数
  * 
  * @param number
  * @return
  */
  public static boolean isPrime(int number)
  {
  if (number < 2)
  return false;
  if (number == 2)
  return true;
  if (number % 2 == 0)
  return false;

  for (int i = 3, j = (int) Math.sqrt(number); i <= j; i += 2)
  {
  if (number % i == 0)
  {
  return false;
  }
  }

  return true;
  }

  /**
  * 获取不大于number的所有素数
  * 
  * @param number
  * @return int数组
  */
  public static int[] makePrimes3(int number)
  {
  if (number < 2)
  return null;
  if (number == 2)
  return new int[] { 2 };
  if (number == 3)
  return new int[] { 2, 3 };

  int[] primes = new int[number / 2 + 1];
  primes[0] = 2;
  primes[1] = 3;
  primes[2] = 5;

  int cnt = 3;

  for (int i = 7; i <= number; ++i)
  {
  if (i % 2 == 0 || i % 3 == 0 || i % 5 == 0)
  {
  continue;
  }
  if (isPrime(i))
  {
  primes[cnt++] = i;
  }
  }

  int[] array = new int[cnt];
  System.arraycopy(primes, 0, array, 0, cnt);

  return array;
  }

  /**
  * 构造素数序列primes[]
  * 
  * 原理:线行筛选
  * 
  * @param primes
  * @param number
  * @param flag
  * true则返回所有不大于number的素数,false则返回前number个素数
  * @return primes数组实际长度
  */
  private static int makePrimesnumberber(int[] primes, int number,
  boolean flag)
  {
  if (number < 2)
  return 0;

  primes[0] = 2;
  if (number == 2)
  return 1;

  primes[1] = 3;

  boolean isPrimes;
  int i, j, cnt;

  for (i = 5, cnt = 2; (flag ? i : cnt) < number; i += 2) //基本类型无法引用,不知道有没有更好地实现
  {
  isPrimes = true;
  for (j = 1; primes[j] * primes[j] <= i; ++j)
  {
  if (i % primes[j] == 0)
  {
  isPrimes = false;
  break;
  }
  }
  if (isPrimes)
  primes[cnt++] = i;
  }

  return cnt;
  }

  /**
  * 获取不大于number的所有素数
  * 
  * @param number
  * @return int数组
  */
  public static int[] makePrimes2(int number)
  {
  int[] primes = new int[number / 2 + 1];

  int total = makePrimesnumberber(primes, number, true);

  int[] array = new int[total];
  System.arraycopy(primes, 0, array, 0, total);

  return array;
  }

  /**


  * 获取前number个素数
  * 
  * @param number
  * @return int数组
  */
  public static int[] makeNumPrimes(int number)
  {
  int[] primes = new int[number];

  int total = makePrimesnumberber(primes, number, false);

  int[] array = new int[total];
  System.arraycopy(primes, 0, array, 0, total);

  return array;
  }

  /**
  * 获取不大于number的所有素数
  * 
  * 原理: 能被素数整除的则为非素数(排除能被2,3,5,7,11...整除的数,最后只剩下素数) 数组序号: 0 1 2 3 4 5 6 7
  * 8... *2+1 即可得到代表的数值 isPrime代表奇数数组: 1 3 5 7 9 10 11 13 15...
  * //若被确认为非素数,则标示为false linkNext代表连接顺序:1 2 3 4 5 6 7 8 9...
  * //指向下一个未被标示为false的数
  * 
  * @param number
  * @return int数组
  */
  public static int[] makePrimes(int number)
  {
  int primeSize = number / 2 + (number % 2 == 0 ? 0 : 1);
  boolean[] isPrime = new boolean[primeSize]; // 直接排除能被2整除的数
  for (int i = 0; i < primeSize; i++)
  {
  isPrime[i] = true;
  }

  int linkSize = number / 6 + 2; // 去掉能被2和3整除的数
  int[] linkNext = new int[linkSize];
  for (int i = 0; i < linkSize; i++)
  {
  linkNext[i] = i + 1;
  }

  int total = primeSize;// 统计未被排除的数,最后即为素数个数
  int primeNumber = 3; // 用来被整除的素数,从3开始
  int multiplier; // 乘子,与primeNumber相乘即可获得要排除的数,>=primeNumber
  int result, index, next;

  while (primeNumber * primeNumber <= number)
  {
  multiplier = primeNumber;
  result = multiplier * primeNumber;
  while (result <= number)
  {
  isPrime[(result - 1) / 2] = false;
  --total;

  index = (multiplier - 1) / 2;
  next = linkNext[index];

  if (isPrime[next] == false)
  linkNext[index] = linkNext[next];

  multiplier = next * 2 + 1;
  result = multiplier * primeNumber;
  }
  primeNumber = linkNext[(primeNumber - 1) / 2] * 2 + 1; // 下一个素数
  }

  int[] array = new int[total];
  int cnt = 0;
  array[cnt++] = 2;
  for (int i = 1; i < primeSize; i++)
  {
  if (isPrime[i])
  {
  array[cnt++] = (i * 2 + 1); // 恢复出素数
  }
  }

  return array;
  }
}

在本人电脑上测试输出1000000以内的素数,三个算法分别用时:
makePrimes :29595327ns
makePrimes2:88697645ns
makePrimes3:180091190ns

欢迎大家分享各种高效算法^ ^

[解决办法]
把 makePrimesnumberber 优化了一下,更快一些。

Java code
private static int makePrimesnumberber(int[] primes, int number, boolean flag) {    int cnt = 0;    if (number < 2) {        return cnt;    }    primes[cnt++] = 2;    if (number == 2) {        return cnt;    }    primes[cnt++] = 3;    if (flag) {        for (int i = 5; i < number; cnt += find(primes, i, cnt), i += 2);    } else {        for (int i = 5; cnt < number; cnt += find(primes, i, cnt), i += 2);    }    return cnt;}private static int find(int[] primes, int n, int count) {    if (n % 3 == 0) {        return 0;    }    for (int i = 1; primes[i] * primes[i] <= n; i++) {        if (n % primes[i] == 0) {            return 0;        }    }    primes[count] = n;    return 1;} 


[解决办法]
测试某一个数是否为素数(合数)的 Miller-Rabin 算法,效率远比根号验证法快。

Java code
public class MillerRabin {    public static void main(String[] args) {        long t0, t1;        t0 = System.nanoTime();        boolean b = !isComposite(479001599);        boolean c = !isComposite(456789012);        t1 = System.nanoTime();        System.out.println(t1 - t0);        System.out.println(b + " " + c);    }    /**     * <p>Miller-Rabin 测试某一个数是否是合数</p>     *     * @param n 需要测试的数     * @return true: 该数为合数;false: 该数为素数     */    public static boolean isComposite(int n) {        if (n < 2) {            throw new IllegalArgumentException("number must greater than or equals 2");        }        // 排除 2、3、5、7 以加速测试        if (n == 2 || n == 3 || n == 5 || n == 7) {            return false;        }        // 偶数        if ((n & 1) == 0) {            return true;        }        // 排除 3、5、7 的倍数,以加速测试        if (n % 3 == 0) {            return true;        }        if (n % 5 == 0) {            return true;        }        if (n % 7 == 0) {            return true;        }        // 寻找 s 和 d 以满足 n = 2^s * d + 1        int s = 0, d = n - 1;        while ((d & 1) == 0) {            d >>= 1;            s++;        }        // 对于各种数值需要进行 Miller-Rabin 基准测试的素数值        // 参考:http://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test#Deterministic_variants_of_the_test        // if n < 1,373,653, it is enough to test a = 2 and 3;        // if n < 9,080,191, it is enough to test a = 31 and 73;        // if n < 4,759,123,141, it is enough to test a = 2, 7, and 61;        // if n < 2,152,302,898,747, it is enough to test a = 2, 3, 5, 7, and 11;        // if n < 3,474,749,660,383, it is enough to test a = 2, 3, 5, 7, 11, and 13;        // if n < 341,550,071,728,321, it is enough to test a = 2, 3, 5, 7, 11, 13, and 17.        if (n < 1373653) {            if (loopMillerRabin(s, d, n, 2, 3)) {                return true;            }        } else if (n < 9080191) {            if (loopMillerRabin(s, d, n, 31, 73)) {                return true;            }        } else {            // 4,759,123,141 已经超过 int 的最大值,因此大于等于 9080191 就采用 4,759,123,141 的基准测试            if (loopMillerRabin(s, d, n, 2, 7, 61)) {                return true;            }        }        return false;    }    /**     * <p>循环 Miller-Rabin 测试</p>     *     * @param s n = 2^s * d + 1 中的 s 值     * @param d n = 2^s * d + 1 中的 d 值     * @param n 需要测试的数     * @param t 测试的基准素数     */    private static boolean loopMillerRabin(int s, int d, int n, int... t) {        for (int i = 0; i < t.length; i++) {            if (testMillerRabin(t[i], s, d, n)) {                return true;            }        }        return false;    }    /**     * <p>Miller-Rabin 基本测试</p>     *     * @param a 素性测试基准素数     * @param s n = 2^s * d + 1 中的 s 值     * @param d n = 2^s * d + 1 中的 d 值     * @param n 需要测试的数     * @return 测试某一数是否是合数。true: 该数是合数;false: 该数可能是素数。若返回 false     * 需要进行多基准的联合测试才能判断该数确实是素数     */    private static boolean testMillerRabin(int a, int s, int d, int n) {        if (montgomery(a, d, n) != 1) {            int e = 1;            for (int i = 0; i < s; i++) {                if (montgomery(a, d * e, n) + 1 == n) {                    return false;                }                e <<= 1;            }            return true;        }        return false;    }    /**     * <p>使用 Montgomery 算法计算 (base ^ exp) % mod 的值。</p>     *      * <p>由于 Java 中 int 的运算速度远远大于 long 的运算速度,因此该算法需要改进!</p>     *     * @param base 基数     * @param exp 指数     * @param mod 模数     */    private static int montgomery(int base, int exp, int mod) {        if (base > 46340 || mod > 46340) {            long temp = 1;            long prod = base % mod;            while (exp > 1) {                if ((exp & 1) != 0) {                    temp = (temp * prod) % mod;                }                prod = (prod * prod) % mod;                exp >>= 1;            }            return (int) ((temp * prod) % mod);        } else {            int temp = 1;            int prod = base % mod;            while (exp > 1) {                if ((exp & 1) != 0) {                    temp = (temp * prod) % mod;                }                prod = (prod * prod) % mod;                exp >>= 1;            }            return (temp * prod) % mod;        }    }    /**     * <p>     * 根据复化辛普生法则计算高斯 Li 函数。Li(x) 近似于素数个数函数 π(x)     * </p>     *      * @param x 数值范围     * @return 该值以内的素数个数的估计值     */    private static double gaussLi(int x) {        int n = x;        double s = 2;        double h = (x - s) / n;        double a = f(s + h / 2);        double b = 0;        for (int k = 1; k < n; k++) {            a += f(s + k * h + h / 2);            b += f(s + k * h);        }        return (h / 6) * (f(s) + 4 * a + 2 * b + f(x));    }    /**     * <p>     * Guass Li(x) 积分函数项     * </p>     *      * @param x     * @return     */    private static double f(double x) {        return 1 / Math.log(x);    }} 

热点排行