주어진 숫자의 제수를 계산하는 가장 최적의 알고리즘 (성능 측면)은 무엇입니까?
의사 코드 또는 예제에 대한 링크를 제공 할 수 있다면 좋을 것입니다.
편집 : 모든 답변이 매우 도움이되었습니다. 감사합니다. Atkin의 체를 구현하고 Jonathan Leffler가 지시 한 것과 비슷한 것을 사용할 것입니다. Justin Bozonier가 게시 한 링크에는 내가 원하는 것에 대한 추가 정보가 있습니다.
주어진 숫자의 제수를 계산하는 가장 최적의 알고리즘 (성능 측면)은 무엇입니까?
의사 코드 또는 예제에 대한 링크를 제공 할 수 있다면 좋을 것입니다.
편집 : 모든 답변이 매우 도움이되었습니다. 감사합니다. Atkin의 체를 구현하고 Jonathan Leffler가 지시 한 것과 비슷한 것을 사용할 것입니다. Justin Bozonier가 게시 한 링크에는 내가 원하는 것에 대한 추가 정보가 있습니다.
답변:
Dmitriy는 Atkin의 체가 주요 목록을 생성하기를 원하지만 그것이 전체 문제를 처리한다고는 믿지 않습니다. 이제 소수 목록이 준비되었으므로 소수의 소수가 제수 역할을하는 빈도 (및 빈도)를 확인해야합니다.
다음은 algo에 대한 파이썬입니다. 여기 를보고 "제목 : 수학-제수 알고리즘 필요"를 검색 하십시오 . 그러나 목록에서 항목 수를 반환하는 대신 계산하십시오.
다음 은 수학적으로해야 할 일을 정확히 설명 하는 Dr. Math 입니다.
기본적으로 숫자 n
가 다음과 같이 요약됩니다
n = a^x * b^y * c^z
(a, b 및 c는 n의 소수입니다 .x, y 및 z는 제수가 반복되는 횟수입니다). 그러면 모든 제수의 총 수는 다음과 같습니다.
(x + 1) * (y + 1) * (z + 1)
.
편집 : BTW, a, b, c 등을 찾으려면 이것을 올바르게 이해하고 있다면 욕심 많은 알고에 상당하는 것을하고 싶을 것입니다. 가장 큰 소수의 제수로 시작하여 추가 곱셈이 숫자 n을 초과 할 때까지 그 값을 곱합니다. 그런 다음 다음 가장 낮은 요소로 이동하고 이전 소수 ^ 현재 소수에 곱한 횟수를 곱하고 다음 소수가 n을 초과 할 때까지 소수를 계속 곱하십시오. 등. 제수를 모으고 위의 수식에 해당 숫자를 적용하십시오.
내 알고리즘 설명에 대해 100 % 확신 할 수는 없지만 그렇지 않은 경우 비슷한 내용입니다.
n = (a^x * b^y * c^z)-(x + 1) * (y + 1) * (z + 1)
규칙도 마찬가지 입니다
있습니다 많은 Atkin의 체보다는 인수에 더 기술. 예를 들어 5893을 고려하고 싶다고 가정하자. sqrt는 76.76이다. 이제 우리는 5893을 제곱의 곱으로 쓰려고한다. 음 (77 * 77-5893) = 36은 6 제곱이므로 5893 = 77 * 77-6 * 6 = (77 + 6) (77-6) = 83 * 71입니다. 그래도 문제가 해결되지 않으면 78 * 78-5893이 완벽한 사각형인지 확인했을 것입니다. 등등. 이 기술을 사용하면 개별 소수를 테스트하는 것보다 n의 제곱근 근처의 요인을 훨씬 빠르게 테스트 할 수 있습니다. 큰 소수를 체와 함께 배제하기 위해이 기술을 결합하면 체만 사용하는 것보다 훨씬 더 나은 팩토링 방법을 사용할 수 있습니다.
그리고 이것은 개발 된 수많은 기술 중 하나 일뿐입니다. 이것은 매우 간단한 것입니다. 타원 곡선을 기반으로하는 팩토링 기술을 이해하기에 충분한 수의 이론을 배우려면 오랜 시간이 걸립니다. (나는 그것들이 존재한다는 것을 안다. 나는 이해하지 못한다.)
따라서 작은 정수를 다루지 않으면 직접 그 문제를 해결하려고하지 않습니다. 대신 이미 이미 매우 효율적인 솔루션이 구현 된 PARI 라이브러리 와 같은 것을 사용하는 방법을 찾으려고 노력했습니다 . 그것으로 약 .05 초에 124321342332143213122323434312213424231341과 같은 임의의 40 자리 숫자를 고려할 수 있습니다. (당신이 궁금한 경우 인수 분해는 29 * 439 * 1132 * 157907 * 284749 * 33843676813 * 4857795469949입니다. Atkin의 체를 사용하여 이것을 파악하지 못했다고 확신합니다.)
Y
제수 함수에는 완벽한 제곱을 위해 올바르게 작동하지 않는 버그가 있습니다.
시험:
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;
}
Atkin의 체는 갈 길이 멀다는 데 동의하지 않습니다. [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)
추신 :이 문제를 해결하기 위해 파이썬 코드를 사용하고 있습니다.
다음은 간단한 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
이 흥미로운 질문은보기보다 훨씬 어렵고 답이 없습니다. 질문은 두 가지 매우 다른 질문으로 구성 될 수 있습니다.
지금까지 내가 본 모든 답변은 # 1을 언급하며 엄청난 숫자에 대해 다루기 힘든 것은 아닙니다. 중간 크기의 N, 심지어 64 비트 숫자의 경우에는 쉽습니다. 거대한 N의 경우, 인수 분해 문제는 "영원히"걸릴 수 있습니다. 공개 키 암호화는 이것에 달려 있습니다.
질문 # 2는 더 많은 토론이 필요합니다. L에 고유 한 숫자 만 포함 된 경우 n 개의 항목에서 k 개의 객체를 선택하기위한 조합 공식을 사용하는 간단한 계산입니다. 실제로 k를 1에서 sizeof (L)까지 변화시키면서 공식을 적용한 결과를 합산해야합니다. 그러나 L에는 일반적으로 여러 번의 여러 소수가 포함됩니다. 예를 들어, L = {2,2,2,3,3,5}는 N = 360의 인수 분해입니다. 이제이 문제는 매우 어렵습니다!
항목 a에 a '중복이 있고, 항목 b에 b'중복이있는 등 k 개의 항목을 포함하는 컬렉션 C에 대해 # 2를 휴식하는 것. 1에서 k-1 개의 항목의 고유 조합은 몇 개입니까? 예를 들어, {2}, {2,2}, {2,2,2}, {2,3}, {2,2,3,3}은 L = {2,2 인 경우 각각 한 번만 발생해야합니다. , 2,3,3,5}. 이러한 각 고유 하위 컬렉션은 하위 컬렉션의 항목을 곱하여 N의 고유 한 제수입니다.
p_i
을 가진 숫자의 주요 요소 인 {p_i, k_i}의 인수 분해의 k_i
경우 해당 숫자의 총 제수는입니다 (k_1+1)*(k_2+1)*...*(k_n+1)
. 나는 당신이 지금까지 이것을 알고 있다고 생각하지만 무작위 독자라면 여기에 이익을 위해 이것을 적어 둡니다.
귀하의 질문에 대한 답변은 정수의 크기에 크게 의존합니다. 100 비트 이하의 작은 숫자와 ~ 1000 비트의 숫자 (암호화에 사용되는)의 방법은 완전히 다릅니다.
작고 n
유용한 참고 자료의 값 : A000005 : d (n) (tau (n) 또는 sigma_0 (n)이라고도 함), n의 제수 수
실제 예 : 정수 분해
그냥 한 줄
나는 귀하의 질문에 대해 매우 신중하게 생각하고 매우 효율적이고 성능이 뛰어난 코드를 작성하려고했습니다. 주어진 숫자의 모든 제수를 화면에 인쇄하려면 한 줄의 코드 만 있으면됩니다! (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;
}
참고 : 위의 두 함수는 숫자 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;
}
작습니다 :)
Atkin의 체는 모든 소수를 주어진 정수까지 제공하는 Eratosthenes 체의 최적화 된 버전입니다. 자세한 내용을 보려면 Google을 검색 할 수 있어야합니다.
일단 당신이 그 목록을 가지고 있다면, 숫자를 각 소수로 나누면 그것이 정확한 제수인지 알아볼 수 있습니다 (즉, 나머지는 0입니다).
숫자 (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
합니다. 나는 이것에 대한 심도있는 연구를하지 않았지만 삼각형 숫자에 관한 프로젝트 오일러의 문제 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에서; 이것은 0으로 나누기 오류를 제거해야합니다
Atkin의 체를 원한다면 여기에 설명되어 있습니다 : 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 .
숫자 이론 교과서는 제수 계산 함수 tau를 호출합니다. 첫 번째 흥미로운 사실은 그것이 곱셈이라는 것입니다. a와 b에 공통 인자가없는 경우 τ (ab) = τ (a) τ (b)입니다. (증명 : a와 b의 각 제수 쌍은 ab의 고유 제수를 제공합니다).
pa 소수의 경우, τ (p ** k) = k + 1 (p의 거듭 제곱)입니다. 따라서 인수 분해에서 τ (n)을 쉽게 계산할 수 있습니다.
그러나 많은 수의 분해는 느릴 수 있습니다 (RSA crytopraphy의 보안은 분해하기 어려운 두 개의 큰 소수의 곱에 달려 있습니다). 이 최적화 된 알고리즘을 제안합니다
다음은 주어진 수의 제수를 찾는 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);
}
}
K
코드를 테스트하고 약간 개선했지만 이제는 더 빠릅니다. 나는 또한 @ هومن جاویدپور 코드로 테스트했는데, 이것은 그의 코드보다 빠릅니다.
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;
}
이것은 숫자를 인수 분해하는 것의 문제가 아닌 숫자의 모든 요소를 결정합니까? 그런 다음 하나 이상의 요인의 모든 조합이 필요한지 여부를 결정할 수 있습니다.
따라서 가능한 알고리즘은 다음과 같습니다.
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))
나는 이것이 당신이 찾고있는 것이라고 생각합니다. 메모장에 복사하여 붙여 넣기 * *로 저장하십시오.
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
가장 효율적인 방법을 모르지만 다음을 수행합니다.
작동해야합니다 \ o /
필요한 경우 내일 C로 코딩하여 시연 할 수 있습니다.