Алгоритм вычисления числа делителей заданного числа

Каким будет наиболее оптимальный алгоритм (по производительности) для вычисления числа делителей заданного числа?

Было бы здорово, если бы вы могли предоставить псевдокод или ссылку на какой-то пример.

EDIT: все ответы были очень полезными, спасибо. Я реализую Сито Аткина, а затем я собираюсь использовать что-то похожее на то, что указал Джонатан Леффлер. Ссылка, размещенная Джастином Бозоньером, содержит дополнительную информацию о том, что я хотел.

Ответ 1

Дмитрий прав, что вы хотите, чтобы сито Аткина сформировало премьер-лист, но я не верю, что он заботится обо всем этом. Теперь, когда у вас есть список простых чисел, вам нужно будет увидеть, сколько из этих простых чисел действует как делитель (и как часто).

Здесь некоторый питон для алгоритма Посмотрите здесь и найдите "Subject: math - нужен алгоритм делителей". Просто подсчитайте количество элементов в списке, а не возвращайте их.

Здесь д-р Math, который объясняет, что именно вам нужно делать математически.

По существу это сводится к тому, что ваш номер n:
 n = a^x * b^y * c^z
(где a, b и c - n простых делителей, а x, y и z - количество раз, когда делитель повторяется) то общий счетчик для всех делителей:
(x + 1) * (y + 1) * (z + 1).

Edit: BTW, чтобы найти a, b, c и т.д., вы захотите сделать то, что составляет жадный алгоритм, если я правильно понимаю это. Начните с вашего наибольшего простого делителя и умножьте его на себя, пока дальнейшее умножение не превысит число n. Затем переходите к следующему наименьшему коэффициенту и умножьте предыдущее простое число раз, умноженное на текущее правое и продолжайте умножать на простое, пока следующее не превысит n... и т.д. Следите за тем, сколько раз вы умножаете число делители вместе и применяют эти числа в формулу выше.

Не 100% уверены в описании моего алгоритма, но если это не похоже на что-то подобное.

Ответ 2

Существует более много методов для факторинга, чем сито Аткина. Например, предположим, что мы хотим, чтобы коэффициент 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, которая уже имеет высокоэффективное решение. С этим я могу определить случайное 40-значное число, например, 124321342332143213122323434312213424231341 примерно через 0,05 секунды. (Его факторизация, если вы задаетесь вопросом, составляет 29 * 439 * 1321 * 157907 * 284749 * 33843676813 * 4857795469949. Я вполне уверен, что он не понял этого, используя сито Аткина...)

Ответ 3

@Yasky

Функция ваших делителей имеет ошибку в том, что она не работает правильно для идеальных квадратов.

Try:

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;
}

Ответ 4

Я не согласен с тем, что сито Аткина - это путь, потому что он может потребовать больше времени, чтобы проверить каждое число в [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 для решения этой проблемы.

Ответ 5

Этот интересный вопрос намного сложнее, чем кажется, и на него не ответил. Вопрос может быть рассмотрен на 2 очень разных вопроса.

1 при N, найдите список L из N простых множителей

2, если L, вычислить количество уникальных комбинаций

Все ответы, которые я вижу до сих пор, относятся к № 1 и не упоминают, что он не поддается огромным цифрам. Для умеренных размеров N, даже 64-битных номеров, это легко; для огромного N проблема факторинга может занять "навсегда". От этого зависит шифрование с открытым ключом.

Вопрос №2 нуждается в дополнительном обсуждении. Если L содержит только уникальные числа, это простой расчет, используя формулу комбинации для выбора k объектов из n элементов. На самом деле вам нужно суммировать результаты применения формулы при изменении k от 1 до sizeof (L). Однако L обычно содержит несколько случаев множественных простых чисел. Например, L = {2,2,2,3,3,5} - факторизация N = 360. Теперь эта проблема довольно сложная!

Перестановка # 2, данная коллекция C, содержащая k элементов, такая, что элемент a имеет "дубликаты", а элемент b имеет дубликаты b и т.д., сколько уникальных комбинаций от 1 до k-1 элементов? Например, {2}, {2,2}, {2,2,2}, {2,3}, {2,2,3,3} должны встречаться один раз и только один раз, если L = {2,2, 2,3,3,5}. Каждая такая уникальная подсекция является уникальным делителем N путем умножения элементов в подсетевой.

Ответ 6

Ответ на ваш вопрос сильно зависит от размера целого числа. Методы для небольших чисел, например, менее 100 бит, и для чисел ~ 1000 бит (такие как используемые в криптографии) совершенно разные.

Ответ 7

Вот прямой алгоритм 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
    count+=(1 if i**2==n else 0)
    return count  

Ответ 8

ТОЛЬКО одна строка
Я очень тщательно продумал ваш вопрос и постарался написать очень эффективный и производительный фрагмент кода. Чтобы вывести на экран все делители заданного числа, нам понадобится всего одна строка кода! (используйте опцию -std = c99 при компиляции через gcc)

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;
}

маленький красивый :)

Ответ 9

Сито Аткина - это оптимизированная версия решета Эратосфена, которая дает все простые числа до заданного целого числа. Вы должны иметь возможность google для более подробной информации.

Как только у вас есть этот список, просто разделить число на каждое число, чтобы увидеть, является ли он точным делителем (т.е. остаток равен нулю).

Основные шаги вычисления дивизоров для числа (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

Ответ 10

Вы можете попробовать это. Это немного хаки, но это достаточно быстро.

def factors(n):
    for x in xrange(2,n):
        if n%x == 0:
            return (x,) + factors(n/x)
    return (n,1)

Ответ 11

Как только у вас есть основная факторизация, существует способ найти число делителей. Добавьте один к каждому экспоненте на каждом отдельном множителе, а затем умножьте экспоненты вместе.

Например: 36 Первичная факторизация: 2 ^ 2 * 3 ^ 2 Разделители: 1, 2, 3, 4, 6, 9, 12, 18, 36 Количество делителей: 9

Добавьте один к каждому экспоненту 2 ^ 3 * 3 ^ 3 Показатели умножения: 3 * 3 = 9

Ответ 12

Прежде чем приступить к решению, считайте, что подход Сита не может быть хорошим ответом в типичном случае.

В то время назад был главный вопрос, и я проверил время - для 32-битных целых чисел, по крайней мере определяющих, было ли это простое, было медленнее, чем грубая сила. Существует два фактора:

1) В то время как человек занимает некоторое время, чтобы сделать подразделение, они очень быстр на компьютере - аналогично стоимости поиска ответа.

2) Если у вас нет главной таблицы, вы можете создать цикл, который полностью работает в кеше L1. Это делает его быстрее.

Ответ 13

Это эффективное решение:

#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;
}

Ответ 14

Дивизоры делают что-то захватывающее: они полностью разделяют. Если вы хотите проверить количество делителей на число, n, это явно избыточно, чтобы охватить весь спектр, 1...n. Я не сделал для этого углубленных исследований, но я решил "Проблема Эйлера" 12 по треугольным номерам. Мое решение для теста более 500 делителей продолжалось 309504 микросекунды (~ 0,3 с). Я написал эту функцию divisor для решения.

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;
}

Для каждого алгоритма существует слабая точка. Я думал, что это слабое отношение к простым цифрам. Но поскольку треугольные числа не печатаются, он безупречно выполняет свою задачу. Из моего профилирования я думаю, что это было очень хорошо.

Счастливые праздники.

Ответ 16

метод простых чисел здесь очень ясен. 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  .

Ответ 17

Учебники теории чисел называют функцию подсчета делителей tau. Первый интересный факт состоит в том, что он мультипликативен, т.е. τ (ab) = τ (a) τ (b), когда a и b не имеют общего коэффициента. (ДОКАЗАТЕЛЬСТВО: каждая пара делителей а и Ь дает отдельный дивизор ab).

Теперь заметим, что для p простое, τ (p ** k) = k + 1 (степени p). Таким образом, вы легко можете вычислить τ (n) из своей факторизации.

Однако факторизация больших чисел может быть медленной (безопасность криптографии RSA зависит от того, что произведение двух больших простых чисел сложно разложить). Это говорит об этом оптимизированном алгоритме

Ответ 18

Ниже приведена программа C для поиска числа делителей заданного числа.

Сложность алгоритма выше: O (sqrt (n)).

Этот алгоритм будет корректно работать для числа, которое является идеальным квадратом, а также чисел, которые не являются идеальным квадратом.

Обратите внимание, что верхний предел цикла устанавливается в квадратный корень числа, чтобы алгоритм был наиболее эффективным.

Обратите внимание, что сохранение верхнего предела в отдельной переменной также экономит время, вы не должны вызывать функцию sqrt в секции условия цикла for, это также экономит ваше вычислительное время.

#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(i=2;i*i<=n;i++)
{
    ...
}

Ответ 19

Вот функция, которую я написал. худшая временная сложность - 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;
}

Ответ 20

Это самый простой способ вычисления числа разделителей:

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);

    }
}

Ответ 21

@Kendall

Я проверил ваш код и внес некоторые улучшения, теперь он еще быстрее. Я также тестировал код @هومن جاویدپور, это также быстрее, чем его код.

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;
}

Ответ 22

Разве это не вопрос факторинга числа, определяющего все факторы числа? Затем вы можете решить, нужны ли вам все комбинации одного или нескольких факторов.

Таким образом, одним из возможных алгоритмов будет:

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

Тогда вам нужно объединить факторы, чтобы определить остальную часть ответа.

Ответ 23

Это то, что я придумал на основе ответа Джастина. Это может потребовать некоторой оптимизации.

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))

Ответ 24

Я думаю, что это то, что вы ищете. Я делаю именно то, что вы просили. Скопируйте и вставьте его в блокнот. Сохраните как *.bat.Run.Enter Number.Multiply процесс на 2, и это число делителей. Я сделал это специально, чтобы он быстрее определял делители:

Обратите внимание на то, что значения поддержки 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

Ответ 25

Я думаю, что это будет удобно и точно

script.pyton

>>>factors=[ x for x in range (1,n+1) if n%x==0] print len(factors)

Ответ 26

Попробуйте что-то в этом направлении:

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;
}

Ответ 27

Вы можете предварительно вычислить простые числа вплоть до квадратного корня максимально возможного N и вычислить показатель степени каждого простого множителя числа. Число делителей n (n = p1 ^ a p2 ^ b p3 ^ c...) равно (a + 1) (b + 1) (c + 1), потому что оно равно числу способов объединения простых чисел. числа этого фактора (и это будет считать количество делителей). Это очень быстро, если вы предварительно вычислите простые числа

Более подробная информация об этом методе:

https://mathschallenge.net/library/number/number_of_divisors

https://www.math.upenn.edu/~deturck/m170/wk2/numdivisors.html

http://primes.utm.edu/glossary/xpage/tau.html

#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;

int divisors_count(const vector<int>& primes, int n)
{
    int divisors = 1;
    for (int i = 0; i < primes.size(); ++i) {
        int factor = primes[i];
        int factor_exponent = 0;
        while (n % factor == 0) {
            ++factor_exponent;
            n /= factor;
        }
        divisors *= (factor_exponent + 1);
    }
    if (n > 1) 
        return 2*divisors; // prime factor > sqrt(MAX_N)
    return divisors;
}

int main()
{
    const int MAX_N = 1e6;
    int max_factor = sqrt(MAX_N);

    vector<char> prime(max_factor + 1, true);
    for (int i = 3; i <= max_factor; i += 2) {
        if (prime[i]) {
            for (int j = 3*i; j <= max_factor; j += 2*i) {
                prime[j] = false;
            }   
        }
    }

    vector<int> primes;
    primes.reserve(max_factor/2);
    primes.push_back(2);
    for (int i = 3; i <= max_factor; i += 2) {
        if (prime[i]) {
            primes.push_back(i);
        }
    }

    int n;
    while (cin >> n) {
        cout << divisors_count(primes, n) << endl;
    }
}

Ответ 28

Я не знаю эффективный метод MOST, но я бы сделал следующее:

  • Создайте таблицу простых чисел, чтобы найти все простые числа, меньшие или равные квадратному корню из числа (Лично я бы использовал Сито Аткина)
  • Подсчитайте все простые числа, меньшие или равные квадратному корню из числа, и умножьте их на два. Если квадратный корень из числа является целым числом, то вычтите его из переменной count.

Должно работать \o/

Если вам нужно, я могу что-то сделать завтра в C, чтобы продемонстрировать.