与えられた数の約数を計算するのに最も最適なアルゴリズムは何ですか(パフォーマンスの観点から)?
疑似コードやいくつかの例へのリンクを提供できればすばらしいでしょう。
編集:すべての回答は非常に役に立ちました、ありがとうございます。私はアトキンのふるいを実装し、その後、ジョナサンレフラーが示したものと同様のものを使用します。Justin Bozonierによって投稿されたリンクには、私が欲しかったものに関する詳細情報があります。
与えられた数の約数を計算するのに最も最適なアルゴリズムは何ですか(パフォーマンスの観点から)?
疑似コードやいくつかの例へのリンクを提供できればすばらしいでしょう。
編集:すべての回答は非常に役に立ちました、ありがとうございます。私はアトキンのふるいを実装し、その後、ジョナサンレフラーが示したものと同様のものを使用します。Justin Bozonierによって投稿されたリンクには、私が欲しかったものに関する詳細情報があります。
回答:
Dmitriyは、AtkinのSieveが素数リストを生成することを望んでいますが、それが問題全体を処理するとは思いません。これで素数のリストが作成されたので、それらの素数のうちいくつが除数として機能するか(およびその頻度)を確認する必要があります。
これはアルゴ用のpythonです。 ここを見て「Subject:math-need divisors algorithm」を検索してください。ただし、返すのではなく、リスト内の項目の数を数えるだけです。
これが数学的に何をする必要があるかを正確に説明するDr. Mathです。
基本的に、それはあなたの数n
が:(
n = a^x * b^y * c^z
ここで、a、b、およびcはnの素数であり、x、y、およびzはその約数が繰り返される回数です)である場合、すべての約数の合計数は次のようになります。
(x + 1) * (y + 1) * (z + 1)
。
編集:ところで、私がこれを正しく理解している場合、a、b、cなどを見つけるには、貪欲なアルゴに相当することを実行する必要があります。最大の素数から始めて、それ以上掛けてnを超えるまでそれを掛けます。次に、次に低い因子に移動し、前の素数に現在の素数を掛けた回数を掛け、次がnを超えるまで素数を掛け続けます...など。掛けた回数を追跡します一緒に約数を取り、それらの数値を上記の数式に適用します。
私のアルゴの説明について100%確信はありませんが、それがそうでなければ、それは似たようなものです。
n = (a^x * b^y * c^z)-(x + 1) * (y + 1) * (z + 1)
ルールがある
アトキンのふるいよりも因数分解のテクニックはたくさんあります。たとえば、5893を因数分解したいとします。そのsqrtは76.76です...次に、5933を二乗の積として書こうとします。よく(77 * 77-5893)= 36は6の2乗なので、5893 = 77 * 77-6 * 6 =(77 + 6)(77-6)= 83 * 71です。それがうまくいかなかった場合、78 * 78-5893が完全な正方形であるかどうかを確認しました。等々。この手法を使用すると、個々の素数をテストするよりもはるかに速く、nの平方根に近い因子をすばやくテストできます。大きな素数を除外するこの手法をふるいと組み合わせると、ふるいだけの場合よりもはるかに優れた因数分解法が得られます。
そして、これは開発された数多くのテクニックの1つにすぎません。これはかなり単純なものです。たとえば、楕円曲線に基づく因数分解手法を理解するのに十分な数の理論を学ぶには、長い時間がかかります。(私はそれらが存在することを知っています。私はそれらを理解していません。)
したがって、小さな整数を扱っていない限り、私はその問題を自分で解決しようとはしません。代わりに、非常に効率的なソリューションが既に実装されているPARIライブラリのようなものを使用する方法を見つけようと思います。これで、約.05秒で124321342332143213122323434312213424231341のようなランダムな40桁の数値を因数分解できます。(ご参考までに、その因数分解は29 * 439 * 1321 * 157907 * 284749 * 33843676813 * 4857795469949です。アトキンのふるいを使用してこれを理解できなかったと確信しています...)
@ヤスキー
除数関数には、完全な正方形に対して正しく機能しないというバグがあります。
試してください:
int divisors(int x) {
int limit = x;
int numberOfDivisors = 0;
if (x == 1) return 1;
for (int i = 1; i < limit; ++i) {
if (x % i == 0) {
limit = x / i;
if (limit != i) {
numberOfDivisors++;
}
numberOfDivisors++;
}
}
return numberOfDivisors;
}
アトキンのふるいが進むべき道であることに私は同意しません。なぜなら、素数について[1、n]のすべての数をチェックすることは、除算によって数を減らすよりも簡単に時間がかかるからです。
少しハッカーではありますが、一般的にはるかに高速なコードを次に示します。
import operator
# A slightly efficient superset of primes.
def PrimesPlus():
yield 2
yield 3
i = 5
while True:
yield i
if i % 6 == 1:
i += 2
i += 2
# Returns a dict d with n = product p ^ d[p]
def GetPrimeDecomp(n):
d = {}
primes = PrimesPlus()
for p in primes:
while n % p == 0:
n /= p
d[p] = d.setdefault(p, 0) + 1
if n == 1:
return d
def NumberOfDivisors(n):
d = GetPrimeDecomp(n)
powers_plus = map(lambda x: x+1, d.values())
return reduce(operator.mul, powers_plus, 1)
psこれは、この問題を解決するためのpythonコードです。
以下は、単純なO(sqrt(n))アルゴリズムです。私はこれを使用して、プロジェクトのオイラーを解決しました
def divisors(n):
count = 2 # accounts for 'n' and '1'
i = 2
while i ** 2 < n:
if n % i == 0:
count += 2
i += 1
if i ** 2 == n:
count += 1
return count
この興味深い質問は見た目よりもはるかに難しく、答えられていません。質問は2つの非常に異なる質問に分解できます。
私がこれまでに見たすべての回答は#1を参照しており、膨大な数の場合は扱いにくいとは言えません。適度なサイズのN、64ビットの数値でも簡単です。巨大なNの場合、因数分解の問題は「永遠に」かかります。公開鍵の暗号化はこれに依存しています。
質問2については、さらに議論が必要です。Lに一意の数値のみが含まれている場合、これは、n個のアイテムからk個のオブジェクトを選択するための組み合わせ式を使用した単純な計算です。実際には、kを1からsizeof(L)まで変化させながら、式を適用した結果を合計する必要があります。ただし、Lには通常、複数の素数が複数出現します。たとえば、L = {2,2,2,3,3,5}はN = 360の因数分解です。この問題は非常に困難です。
#2を繰り返します。コレクションAにk個のアイテムが含まれ、アイテムaにはa 'の重複があり、アイテムbにはb'の重複があるなどです。1〜k-1アイテムの一意の組み合わせはいくつありますか?たとえば、{2}、{2,2}、{2,2,2}、{2,3}、{2,2,3,3}は、L = {2,2の場合、それぞれ1回だけ出現する必要があります、2,3,3,5}。このような一意のサブコレクションはそれぞれ、サブコレクション内のアイテムを乗算することにより、Nの一意の約数です。
p_i
はk_i
多重度を持つ数の素因数であり、その数の約数の総数は(k_1+1)*(k_2+1)*...*(k_n+1)
です。もうご存知だと思いますが、ここにランダムリーダーがいる場合のために、これを書き留めておきます。
あなたの質問への答えは、整数のサイズに大きく依存します。100ビット未満などの小さい数の方法と、(暗号化で使用されるような)1000ビット以下の数の方法は完全に異なります。
小さいn
参照といくつかの有用な参照の値:A000005:d(n)(tau(n)またはsigma_0(n)とも呼ばれる)、nの約数。
実例:整数の因数分解
ちょうど1行私はあなたの質問を非常に慎重に考え、非常に効率的でパフォーマンスの高いコードを書き込もうとしました。画面に指定された数値のすべての約数を印刷するには、1行のコードが必要です!(gccを介したコンパイル中にオプション-std = c99を使用)
for(int i=1,n=9;((!(n%i)) && printf("%d is a divisor of %d\n",i,n)) || i<=(n/2);i++);//n is your number
除数の数を見つけるには、次の非常に高速な関数を使用できます(1と2を除くすべての整数に対して正しく機能します)
int number_of_divisors(int n)
{
int counter,i;
for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
return counter;
}
または与えられた数を除数として扱う場合(1と2を除くすべての整数に対して正しく機能します)
int number_of_divisors(int n)
{
int counter,i;
for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
return ++counter;
}
注:上記の2つの関数は、1と2を除くすべての正の整数に対して正しく機能するため、2より大きいすべての数に対して機能しますが、1と2をカバーする必要がある場合は、次の関数のいずれかを使用できます(少しもっとゆっくり)
int number_of_divisors(int n)
{
int counter,i;
for(counter=0,i=1;(!(n%i) && (counter++)) || i<=(n/2);i++);
if (n==2 || n==1)
{
return counter;
}
return ++counter;
}
または
int number_of_divisors(int n)
{
int counter,i;
for(counter=0,i=1;(!(i==n) && !(n%i) && (counter++)) || i<=(n/2);i++);
return ++counter;
}
小さな美しいです:)
アトキンのふるいは、与えられた整数までのすべての素数を与えるエラトステネスのふるいの最適化されたバージョンです。詳細については、これをグーグルできるはずです。
そのリストを取得したら、それを正確な除数であるかどうか(つまり、剰余がゼロであるかどうか)を確認するために、各素数で数値を除算するのは簡単なことです。
数値(n)の約数を計算する基本的な手順は次のとおりです[これは実際のコードから変換された疑似コードなので、エラーが発生していないことを願っています]:
for z in 1..n:
prime[z] = false
prime[2] = true;
prime[3] = true;
for x in 1..sqrt(n):
xx = x * x
for y in 1..sqrt(n):
yy = y * y
z = 4*xx+yy
if (z <= n) and ((z mod 12 == 1) or (z mod 12 == 5)):
prime[z] = not prime[z]
z = z-xx
if (z <= n) and (z mod 12 == 7):
prime[z] = not prime[z]
z = z-yy-yy
if (z <= n) and (x > y) and (z mod 12 == 11):
prime[z] = not prime[z]
for z in 5..sqrt(n):
if prime[z]:
zz = z*z
x = zz
while x <= limit:
prime[x] = false
x = x + zz
for z in 2,3,5..n:
if prime[z]:
if n modulo z == 0 then print z
あなたはこれを試すかもしれません。少しハックですが、かなり高速です。
def factors(n):
for x in xrange(2,n):
if n%x == 0:
return (x,) + factors(n/x)
return (n,1)
これは効率的なソリューションです。
#include <iostream>
int main() {
int num = 20;
int numberOfDivisors = 1;
for (int i = 2; i <= num; i++)
{
int exponent = 0;
while (num % i == 0) {
exponent++;
num /= i;
}
numberOfDivisors *= (exponent+1);
}
std::cout << numberOfDivisors << std::endl;
return 0;
}
除数は見事な何かを行います:彼らは完全に分割します。除数の数をチェックしたい場合n
は、スペクトル全体をスパンすることは明らかに冗長1...n
です。私はこれについて詳細な調査を行っていませんが、三角形の数に関するProject Eulerの問題12を解決しました。500を超える除数テストに対する私のソリューションは、309504マイクロ秒(約0.3秒)実行されました。ソリューションのためにこの除数関数を書きました。
int divisors (int x) {
int limit = x;
int numberOfDivisors = 1;
for (int i(0); i < limit; ++i) {
if (x % i == 0) {
limit = x / i;
numberOfDivisors++;
}
}
return numberOfDivisors * 2;
}
どのアルゴリズムにも、弱点があります。これは素数に対して弱いと思いました。しかし、三角形の数字は印刷されないため、その目的を完全に果たしました。私のプロファイリングから、それはかなりうまくいったと思います。
幸せな休日。
numberOfDivisors
し、イテレータを1にします。これにより、ゼロ除算エラーが解消されます
ここで説明されているアトキンのふるいが必要です。http://en.wikipedia.org/wiki/Sieve_of_Atkin
素数法はここで非常に明確です。P []はsq = sqrt(n)以下の素数のリストです。
for (int i = 0 ; i < size && P[i]<=sq ; i++){
nd = 1;
while(n%P[i]==0){
n/=P[i];
nd++;
}
count*=nd;
if (n==1)break;
}
if (n!=1)count*=2;//the confusing line :D :P .
i will lift the understanding for the reader .
i now look forward to a method more optimized .
数論の教科書は、除数を数える関数をタウと呼びます。最初の興味深い事実は、それが乗法的であるということです。τ(ab)=τ(a)τ(b)(aとbに共通因子がない場合)。(証明:aとbの約数の各ペアはabの個別の約数を与える)。
ここで、pa素数の場合、τ(p ** k)= k + 1(pのべき乗)であることに注意してください。したがって、因数分解からτ(n)を簡単に計算できます。
ただし、大きな数の因数分解は遅くなる可能性があります(RSA crytopraphyのセキュリティは、2つの大きな素数の積が因数分解しにくいことに依存します)。これは、この最適化されたアルゴリズムを示唆しています
以下は、与えられた数の約数を見つけるCプログラムです。
上記のアルゴリズムの複雑さはO(sqrt(n))です。
このアルゴリズムは、完全な正方形ではない数値だけでなく、完全な正方形ではない数値でも正しく機能します。
アルゴリズムが最も効率的になるように、ループの上限は数値の平方根に設定されていることに注意してください。
上限を別の変数に格納すると時間も節約できることに注意してください。forループの条件セクションでsqrt関数を呼び出さないでください。これにより、計算時間が節約されます。
#include<stdio.h>
#include<math.h>
int main()
{
int i,n,limit,numberOfDivisors=1;
printf("Enter the number : ");
scanf("%d",&n);
limit=(int)sqrt((double)n);
for(i=2;i<=limit;i++)
if(n%i==0)
{
if(i!=n/i)
numberOfDivisors+=2;
else
numberOfDivisors++;
}
printf("%d\n",numberOfDivisors);
return 0;
}
上記のforループの代わりに、次のループを使用することもできます。これにより、数値の平方根を見つける必要がなくなるため、さらに効率的です。
for(i=2;i*i<=n;i++)
{
...
}
これが私が書いた関数です。最悪の時間の複雑さはO(sqrt(n))です。一方、最良の時間はO(log(n))です。それはあなたにその発生回数とともにすべての素数を与えます。
public static List<Integer> divisors(n) {
ArrayList<Integer> aList = new ArrayList();
int top_count = (int) Math.round(Math.sqrt(n));
int new_n = n;
for (int i = 2; i <= top_count; i++) {
if (new_n == (new_n / i) * i) {
aList.add(i);
new_n = new_n / i;
top_count = (int) Math.round(Math.sqrt(new_n));
i = 1;
}
}
aList.add(new_n);
return aList;
}
これは、数の除数を計算する最も基本的な方法です。
class PrintDivisors
{
public static void main(String args[])
{
System.out.println("Enter the number");
// Create Scanner object for taking input
Scanner s=new Scanner(System.in);
// Read an int
int n=s.nextInt();
// Loop from 1 to 'n'
for(int i=1;i<=n;i++)
{
// If remainder is 0 when 'n' is divided by 'i',
if(n%i==0)
{
System.out.print(i+", ");
}
}
// Print [not necessary]
System.out.print("are divisors of "+n);
}
}
ケンドール
私はあなたのコードをテストしていくつかの改善を行いましたが、今ではさらに高速になっています。私も@هومنجاویدپورコードでテストしましたが、これは彼のコードよりも高速です。
long long int FindDivisors(long long int n) {
long long int count = 0;
long long int i, m = (long long int)sqrt(n);
for(i = 1;i <= m;i++) {
if(n % i == 0)
count += 2;
}
if(n / m == m && n % m == 0)
count--;
return count;
}
これは数を因数分解するだけの問題ではありませんか?数のすべての因数を決定しますか?次に、1つ以上の要素のすべての組み合わせが必要かどうかを決定できます。
したがって、1つの可能なアルゴリズムは次のようになります。
factor(N)
divisor = first_prime
list_of_factors = { 1 }
while (N > 1)
while (N % divisor == 0)
add divisor to list_of_factors
N /= divisor
divisor = next_prime
return list_of_factors
その後、残りの答えを決定するために要因を組み合わせるのはあなた次第です。
これは私がジャスティンの答えに基づいて思いついたものです。最適化が必要な場合があります。
n=int(input())
a=[]
b=[]
def sieve(n):
np = n + 1
s = list(range(np))
s[1] = 0
sqrtn = int(n**0.5)
for i in range(2, sqrtn + 1):
if s[i]:
s[i*i: np: i] = [0] * len(range(i*i, np, i))
return filter(None, s)
k=list(sieve(n))
for i in range(len(k)):
if n%k[i]==0:
a.append(k[i])
a.sort()
for i in range(len(a)):
j=1
while n%(a[i]**j)==0:
j=j+1
b.append(j-1)
nod=1
for i in range(len(b)):
nod=nod*(b[i]+1)
print('no.of divisors of {} = {}'.format(n,nod))
これはあなたが探しているものだと思います。それをコピーし、メモ帳に貼り付けます。*。bat.Run.Enter Number。
Plsは、CMD変数は999999999を超える値をサポートできないことに注意してください
@echo off
modecon:cols=100 lines=100
:start
title Enter the Number to Determine
cls
echo Determine a number as a product of 2 numbers
echo.
echo Ex1 : C = A * B
echo Ex2 : 8 = 4 * 2
echo.
echo Max Number length is 9
echo.
echo If there is only 1 proces done it
echo means the number is a prime number
echo.
echo Prime numbers take time to determine
echo Number not prime are determined fast
echo.
set /p number=Enter Number :
if %number% GTR 999999999 goto start
echo.
set proces=0
set mindet=0
set procent=0
set B=%Number%
:Determining
set /a mindet=%mindet%+1
if %mindet% GTR %B% goto Results
set /a solution=%number% %%% %mindet%
if %solution% NEQ 0 goto Determining
if %solution% EQU 0 set /a proces=%proces%+1
set /a B=%number% / %mindet%
set /a procent=%mindet%*100/%B%
if %procent% EQU 100 set procent=%procent:~0,3%
if %procent% LSS 100 set procent=%procent:~0,2%
if %procent% LSS 10 set procent=%procent:~0,1%
title Progress : %procent% %%%
if %solution% EQU 0 echo %proces%. %mindet% * %B% = %number%
goto Determining
:Results
title %proces% Results Found
echo.
@pause
goto start
これらの線に沿って何かを試してください:
int divisors(int myNum) {
int limit = myNum;
int divisorCount = 0;
if (x == 1)
return 1;
for (int i = 1; i < limit; ++i) {
if (myNum % i == 0) {
limit = myNum / i;
if (limit != i)
divisorCount++;
divisorCount++;
}
}
return divisorCount;
}
最も効率的な方法はわかりませんが、次のようにします。
動作するはずです\ o /
必要に応じて、明日Cで何かをコーディングして説明することができます。