web-dev-qa-db-ja.com

Nの約数の合計を計算する最も効率的な方法(1≤N≤1 000 000 000)

数値自体を除いて、数値N(1≤N≤1 000 000 000)の約数の合計を計算するコードを書きたいのですが。

しかし、それを行うための効率的な方法が必要です。 1秒未満で回答を印刷するとします。私はそれがハードウェアなどに依存することを知っています。しかし、私はi7マシンを持っていますが、コードが不十分なため、1秒以上かかります(最悪のシナリオでは1000000000です)。

したがって、より理解しやすくするために:

EX:サンプル入力:10->出力は:8(1 + 2 + 5 = 8であるため)

EX:入力:20->出力を想定:22(1 + 2 + 4 + 5 + 10 = 22)

これまでのところ私はこれを書いた:

public class Main
{
public static void main(String[] args)
{
    @SuppressWarnings("resource")
    Scanner read = new Scanner(System.in);

    System.out.print("How many times you would like to try: ");
    int len = read.nextInt();

    for(int w = 0; w < len; w++)
    {
        System.out.print("Number: ");
        int input = read.nextInt();

        int remains = 1;
        int sum = remains;

        /* All I know we only need to check half of the given number as 
           I learned ages ago. I mean (input / 2) :D */

        for(int i = 2; i <= input / 2; i++)
        {
            if(input % i == 0) sum += i;
        }

        System.out.print("Result: " + sum);
    }

}
}

それで問題は、このソリューションをどのように改善するかです。それをより効率的にする方法、または私がそれをこのように置く方法それをより効率的にする方法はありますか?

5
Jon Snow

これは、時間の制約をおそらく満たす簡単な改善です。 iの除数Nごとに、対応する除数N/iもあります。したがって、除数のすべてのペアを見つけるには、1から平方根Nまでループするだけで済みます。

    int maxD = (int)Math.sqrt(input);
    int sum=1;
    for(int i = 2; i <= maxD; i++)
    {
        if(input % i == 0)
        {
           sum += i;
           int d = input/i;
           if(d!=i)
              sum+=d;
        }
    }

「素因数分解」方法が他の解答方法から、アルゴリズムの複雑さを見えない因数分解方法の背後に隠すことに注意してください。そして、「試行除算」のような素因数分解の単純な形式を実装すると、上記のように非常によく似たアルゴリズムで終わることになります。したがって、素因数分解法mayは、多くの小さな素因数を持つNの値を高速化しますが、N自体が素数である場合(少なくとも、非常に洗練された素因数分解法)。

9
Doc Brown

はるかに効率的な方法は、数を素因数に因数分解することです(私は1つだけは知りませんが、既存のライブラリを使用します)。

因数分解はHashMap<Integer,Integer> factorization各素因数がその多重度にマッピングされている場合、次のように合計を計算できます。

int N = 84684684; //your integer
HashMap<Integer,Integer> primeFactorization = factor(N);
long total = 1;
for (int p:primeFactorization.keySet()) {
    int factor = 1;
    for (int i=0;i<primeFactorization.get(p);i++) {
        factor*=p;
        factor+=1;
    }
    total*=factor;
}
return total-N;

結果は主に積として計算されるため、これははるかに高速になります。たとえば、10の場合、10 = 2 * 5となるため、結果は(1 + 2)(1 + 5)-10 = 8になります。 20の場合、結果は(1 + 2 + 4)(1 + 5)-20 = 22になります。

1 000 000 000 = 2 ^ 9 * 5 ^ 9の場合、結果は(1 + 2 + 4 + 8 + ... + 512)*(1 + 5 + 25 + ... + 5 ^ 9)になります。 -1 000 000 000 =1497558338。i7プロセッサで1秒を必要とするのではなく、手動でこの計算を行いました。

4
user1111929

あなたはコードから始めているようです。それは完全に間違っています。あなたは数学の問題を解いているので、まず数学を解く必要があります。

7 x 127 = 889という数値を考えてみます。7と127は素数です。 2つの主要な要素を挙げたときに、889の約数をすべて見つける方法を考えられますか?答え:889のすべての約数を取得するには、数値(1、7)の1つを取り、数値(1、127)のいずれかを掛けます。それはあなたが数えなかった7 x 127 = 889を含む4つの異なる製品です。すべての約数を見つけることなく、すべての約数の合計は何ですか? (1、7)の各数と(1、127)の各数を組み合わせたので、すべての除数の合計は(1 + 7)x(1 + 127)= 8 x 128 = 1024で、889あなたは数えませんでした。

16 x 31 = 496の数値をとった場合、可能な製品は、数値の1つ(1、2、4、8、16)に数値の1つ(1、31)を掛けて、すべての合計積は(1 + 2 + 4 + 8 + 16)x(1 + 31)です。

したがって、ある数の約数を計算するには、それを個別の素数の積に因数分解してから、上記の数学をコードに変換する方法を見つけます。

2
gnasher729

以下は、除数の数を数え、それらを合計して正しい出力を出すための効率的なアルゴリズムの1つです。

import Java.math.BigInteger;
import Java.util.Scanner;

class ProductDivisors {

public static BigInteger modulo=new BigInteger("1000000007");
public static BigInteger solve=new BigInteger("1");
public static BigInteger two=new BigInteger("2");
public static void main(String[] args) {
    // TODO Auto-generated method stub
    Scanner sc=new Scanner(System.in);
    int N=sc.nextInt();
    BigInteger prod=new BigInteger("1");
    while(N-->0){
        prod=sc.nextBigInteger();
        solve=solve.multiply(prod);
    }
    BigInteger x = new BigInteger("2");
    BigInteger total = new BigInteger("0");
    BigInteger totalFactors =new BigInteger("1");
    BigInteger sum=new BigInteger("0");
    while (x.multiply(x).compareTo(solve) <= 0) {
        int power = 0;
        while (solve.mod(x).equals(BigInteger.ZERO)) {
            power++;
            sum=sum.add(x);
            solve = solve.divide(x);
        }
        total = new BigInteger(""+(power + 1));
        totalFactors=totalFactors.multiply(total);
        x = x.add(BigInteger.ONE);
    }
    if (!(solve.equals(BigInteger.ONE))) {
        totalFactors =totalFactors.multiply(two);
    }
    System.out.println(sum);
}

}

上記のコードでは、除数のカウントも行われているため、その数の除数を取得できます。あれば提案があります。

0