计算BigInteger []

时间:2015-07-22 17:37:07

标签: java algorithm biginteger multiplication

背景信息:我尝试使用Java中的BigInteger类计算非常大的n的阶乘(n> 100,000),到目前为止我正在做的事情:

  • 使用Sieve of Erasthones生成小于或等于n的所有素数

  • 找出他们将被提升的权力。

  • 将所有数字提升为相应的权力。

  • 使用除法和征服递归方法将它们全部相乘。

从我在互联网上所做的研究来看,这比简单地将所有k乘以n更快地渐进。然而,我注意到我实现的最慢部分是我乘以所有主要权力的部分。我的问题是:

  • 有更快的方法来计算大量数字的乘积吗?
  • 我的实施能否得到改善?

代码:

public static BigInteger product(BigInteger[] numbers) {
    if (numbers.length == 0)
        throw new ArithmeticException("There is nothing to multiply!");
    if (numbers.length == 1)
        return numbers[0];
    if (numbers.length == 2)
        return numbers[0].multiply(numbers[1]);

    BigInteger[] part1 = new BigInteger[numbers.length / 2];
    BigInteger[] part2 = new BigInteger[numbers.length - numbers.length / 2];
    System.arraycopy(numbers, 0, part1, 0, numbers.length / 2);
    System.arraycopy(numbers, numbers.length / 2, part2, 0, numbers.length - numbers.length / 2);

    return product(part1).multiply(product(part2));
}
  • 请注意,BigInteger使用karatsuba算法进行乘法。
  • 我知道有很多关于计算阶乘的问题。但我的是计算BigIntegers的产品,因为资源不多。 (我已经看到有人说"使用分而治之法"但我不记得在哪里,而且我还没有看到任何实施。

3 个答案:

答案 0 :(得分:2)

提高性能的一种方法是执行以下操作:

  1. 对需要相乘的数字数组进行排序
  2. 创建两个新列表:ab
  3. 对于您需要乘以的输入列表中的每个数字,它可能会出现多次。我们说v_i号出现n_i次。然后将v_i添加到a n_i / 2次(向下舍入)。如果n_i为奇数,请将v_i一次添加到b
  4. 要计算结果,请执行以下操作:
  5. BigInteger A = product(a);
    BigInteger B = prudoct(b);
    return a.multiply(a).multiply(b);
    

    要了解它的工作原理,请考虑输入数组是[2,2,2,2,3,3,3]。所以,有4个2和3个3。数组ab将相应地

    a = [2, 2, 3]
    b = [3]
    

    然后,您将递归调用以计算这些产品。请注意,我们将要乘以的数字的数量从7减少到4,几乎减少了两倍。这里的诀窍是,对于多次出现的数字,我们只计算其中一半的乘积,然后将其提高到2的幂。非常类似于在O(log n)时间内如何计算数字的幂。

答案 1 :(得分:1)

我提出另一个想法,pow算法非常快,你可以使用指数计算所有素数,如下所示:

11! -> {2^10, 3^5, 5^2, 7^1, 11^1}

你可以计算所有素数幂,然后使用除法和征服来乘以所有素数。 实施:

private static BigInteger divideAndConquer(List<BigInteger> primesExp, int min, int max){
    BigInteger result = BigInteger.ONE;
    if (max - min == 1){
        result = primesExp.get(min);
    } else if (min < max){
        int middle = (max + min)/2;
        result = divideAndConquer(primesExp, min, middle).multiply(divideAndConquer(primesExp, middle, max));
    }
    return result;
}

public static BigInteger factorial(int n) {
    // compute pairs: prime, exp
    List<Integer> primes = new ArrayList<>();
    Map<Integer, Integer> primeTimes = new LinkedHashMap<>();
    for (int i = 2; i <= n; i++) {
        int sqrt = Math.round((float) Math.sqrt(i));
        int value = i;
        Iterator<Integer> it = primes.iterator();
        int prime = 0;
        while (it.hasNext() && prime <= sqrt && value != 0) {
            prime = it.next();
            int times = 0;
            while (value % prime == 0) {
                value /= prime;
                times++;
            }
            if (times > 0) {
                primeTimes.put(prime, times + primeTimes.get(prime));
            }
        }
        if (value > 1) {
            Integer times = primeTimes.get(value);
            if (times == null) {
                times = 0;
                primes.add(value);
            }
            primeTimes.put(value, times + 1);
        }
    }
    // compute primes power:
    List<BigInteger> primePows = new ArrayList<>(primes.size());
    for (Entry<Integer,Integer> e: primeTimes.entrySet()) {
        primePows.add(new BigInteger(String.valueOf(e.getKey())).pow(e.getValue()));
    }
    // it multiply all of them:
    return divideAndConquer(primePows, 0, primePows.size());
}

答案 2 :(得分:0)

可能是最快的方法:

<强> Sequence.java

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public final class Sequence {

    private final List<BigInteger> elements;

    private Sequence(List<BigInteger> elements) {
        this.elements = elements;
    }

    public List<BigInteger> getElements() {
        return elements;
    }

    public int size() {
        return elements.size();
    }

    public Sequence subSequence(int startInclusive, int endExclusive) {
        return subSequence(startInclusive, endExclusive, false);
    }

    public Sequence subSequence(int startInclusive, int endExclusive, boolean sync) {
        return Sequence.of(elements.subList(startInclusive, endExclusive), sync);
    }

    public void addLast(BigInteger element) {
        elements.add(element);
    }

    public BigInteger removeLast() {
        return elements.remove(size() - 1);
    }

    public BigInteger sum() {
        return sum(false);
    }

    public BigInteger sum(boolean parallel) {
        return parallel
                ? elements.parallelStream().reduce(BigInteger.ZERO, BigInteger::add)
                : elements.stream().reduce(BigInteger.ZERO, BigInteger::add);
    }

    public BigInteger product() {
        return product(false);
    }

    public BigInteger product(boolean parallel) {
        return parallel
                ? elements.parallelStream().reduce(BigInteger.ONE, BigInteger::multiply)
                : elements.stream().reduce(BigInteger.ONE, BigInteger::multiply);
    }

    public static Sequence range(int startInclusive, int endExclusive) {
        return range(startInclusive, endExclusive, false);
    }

    public static Sequence range(int startInclusive, int endExclusive, boolean sync) {
        if (startInclusive > endExclusive) {
            throw new IllegalArgumentException();
        }
        final List<BigInteger> elements = sync ? Collections.synchronizedList(new ArrayList<>()) : new ArrayList<>();
        for (; startInclusive < endExclusive; startInclusive++) {
            elements.add(BigInteger.valueOf(startInclusive));
        }
        return new Sequence(elements);
    }

    public static Sequence of(List<BigInteger> elements) {
        return of(elements, false);
    }

    public static Sequence of(List<BigInteger> elements, boolean sync) {
        return new Sequence(sync ? Collections.synchronizedList(elements) : elements);
    }

    public static Sequence empty() {
        return empty(false);
    }

    public static Sequence empty(boolean sync) {
        return of(new ArrayList<>(), sync);
    }

}

<强> FactorialCalculator.java

import java.math.BigInteger;
import java.util.LinkedList;
import java.util.List;

public final class FactorialCalculator {

    private static final int CHUNK_SIZE = Runtime.getRuntime().availableProcessors();

    public static BigInteger fact(int n) {
        return fact(n, false);
    }

    public static BigInteger fact(int n, boolean parallel) {
        if (n < 0) {
            throw new IllegalArgumentException();
        }
        if (n <= 1) {
            return BigInteger.ONE;
        }
        Sequence sequence = Sequence.range(1, n + 1);
        if (!parallel) {
            return sequence.product();
        }
        sequence = parallelCalculate(splitSequence(sequence, CHUNK_SIZE * 2));
        while (sequence.size() > CHUNK_SIZE) {
            sequence = parallelCalculate(splitSequence(sequence, CHUNK_SIZE));
        }
        return sequence.product(true);
    }

    private static List<Sequence> splitSequence(Sequence sequence, int chunkSize) {
        final int size = sequence.size();
        final List<Sequence> subSequences = new LinkedList<>();
        int index = 0, targetIndex;
        while (index < size) {
            targetIndex = Math.min(index + chunkSize, size);
            subSequences.add(sequence.subSequence(index, targetIndex, true));
            index = targetIndex;
        }
        return subSequences;
    }

    private static Sequence parallelCalculate(List<Sequence> sequences) {
        final Sequence result = Sequence.empty(true);
        sequences.parallelStream().map(s -> s.product(true)).forEach(result::addLast);
        return result;
    }

}

测试:

public static void main(String[] args) {
    // warm up
    for (int i = 0; i < 100; i++) {
        FactorialCalculator.fact(10000);
    }
    int n = 1000000;
    long start = System.currentTimeMillis();
    FactorialCalculator.fact(n, true);
    long end = System.currentTimeMillis();
    System.out.printf("Execution time = %d ms", end - start);
}

结果:

Execution time = 3066 ms
  • 操作系统:Win 10 Pro 64位
  • CPU:Intel Core i7-4700HQ @ 2.40GHz 2.40GHz