Вычисление суммы суммарной функции до 10 ^ 16 - PullRequest
4 голосов
/ 30 апреля 2019

Повторное размещение его в понятной и понятной форме без какого-либо сложного MathJax, который не отображается должным образом:

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

Пусть P(n) = sum{1<=k<=n} φ(k)

Найти P(10^16)

Я долго об этом искал и пробовал разные подходы:

  1. Используя формулу для φ(n)= n * product{1<=i<=k} (Pi-1)/Pi, я попытался вычислить φ(n) в диапазоне, но это становится очень неэффективным для больших n. Я мог бы добраться до 10^7 с таким подходом. Помимо этого, он просто становится слишком медленным.

  2. Я попробовал другой, более прямой. Википедия и Wolfram Alpha предлагают аналогичные формулы для прямого вычисления P(n):

P(n) = sum {1<=k<=n} φ(k)= 0.5⋅(1+∑{1<=k<=n} μ(k)⋅⌊n/k⌋^2)

Эта формула казалась намного более перспективной. Я попробовал это и смог продвинуться намного дальше 10^7, но все еще далеко от цели С предварительным расчетом сита функции Мёбиуса я мог получить чуть меньше 10^9. Моей памяти было недостаточно, и я не мог больше вычислять значения в сите. И даже если бы я мог, это все еще занимает много времени и очень далеко от 10^16.

Вот часть кода, который я использовал для второго подхода, написанного на Java:

public static BigInteger PhiSummatoryFunction (long limit)
{
    BigInteger sum = BigInteger.ZERO;
    int [] m = MoebiusSieve(limit);
    for (int i=1;i<m.length;i++)
        sum=sum.add(BigInteger.valueOf((long) (m[i]*Math.floor(limit/i)*Math.floor(limit/i))));
    return sum.add(BigInteger.ONE).divide(BigInteger.ONE.add(BigInteger.ONE));
}

Где MoebiusSieve - это функция, которая вычисляет значения функции Мебиуса до определенного предела в сите с использованием метода, подобного эратосфену.

  1. После понимания и реализации нового рекурсивного метода для этого вычисления, который я нашел в Интернете:

P (n) = n (n + 1) / 2 − ∑ {2

Я могу вычислять значения до P(10^11), и с максимальным выделением памяти, предварительно вычисляя столько φ (n), сколько возможно, и, следовательно, все P(n), которые я могу для запоминания, я могу вычислить P(10^12) всего за более 20 минут. Значительное улучшение, но все еще немного далеко от P(10^16). Это нормально, если вычисление займет немного больше времени, но я боюсь, что P(10^16) займет экспоненциально больше времени, судя по «скачку» во времени вычислений между P(10^11) и P(10^12). Моя память позволяет мне «сохранять» до 350,000,000 φ(n) values ИЛИ до 700,000,000 μ(k) values. Возможно, есть способ выполнить суммирование, используя значения μ (k), а не φ (n) ?.

Все мои вычисления предполагают и показывают, что моя рекурсия является выдающимся потребителем времени. Это очевидно, но я уверен, что это займет больше времени, чем следовало бы. Я публикую ниже код рекурсии с некоторой документацией. Мне кажется, это правильный способ сделать это вычисление, но моя реализация не оптимальна.

public static BigInteger phiR (long limit, long [] s) // limit is 10^t, s is the sieve of precomputed values of `P(n)`. Can store maximum 350,000,000 values
    {                                                                                                                                                       
        if (limit<s.length)                                 
            return BigInteger.valueOf(s[(int) limit]);
        BigInteger sum = BigInteger.valueOf(limit).multiply(BigInteger.valueOf(limit).add(BigInteger.ONE)).divide(BigInteger.valueOf(2)); // this corresponds to the n'th triangular number
        BigInteger midsum1=BigInteger.ZERO; // the first sum
        BigInteger midsum2=BigInteger.ZERO; // the second sum
        long m = 2;
        while (limit/m != limit/(m+1) && m*m<=limit) // computing the first sum, first for changing floor(limit/m) values
        {
            midsum1=midsum1.add(phiR((long) Math.floor(limit/m),s));
            m++;
        }
        for (long k = m;k*k<=limit;k++) // once the floors become constant for some values,-->
        {                               //  can check how many times the value appears, and multiply accordingly,--> 
            BigInteger midPhi = phiR((long) Math.floor(limit/k),s);  // rather than compute the Phi every time
            long q = 1;
            while (limit/k==limit/(k+1)&&k*k<=limit)
            {
                q++;
                k++;
            }
            k--;
            midPhi=midPhi.multiply(BigInteger.valueOf(q));
            midsum1=midsum1.add(midPhi);
        }
        for (long d=1;d*d<=limit;d++) // computing the second sum
            if ((double)d!=Math.floor(limit/d))
                midsum2=midsum2.add(BigInteger.valueOf((long) (Math.floor(limit/d)-Math.floor(limit/(d+1)))).multiply(phiR(d,s)));
        sum=sum.subtract(midsum1).subtract(midsum2);
        return sum;
    }

Мне предложили использовать dictinaries , в дополнение к массиву, для больших значений n, но я ничего не знаю об этом. Можно ли сделать еще одно улучшение, чтобы установить временные рамки на день или около того?

1 Ответ

0 голосов
/ 30 апреля 2019

Если вы хотите узнать число одного числа n , лучший способ найти его - это множитель n и взять произведение на 1 меньше, чем каждый фактор; например, 30 = 2 * 3 * 5, и вычитая 1 из каждого фактора, а затем умножая, дает коэффициент 1 * 2 * 4 = 8. Но если вы хотите найти элементы всех чисел меньше заданного n , лучший подход, чем факторинг каждого из них - просеивание. Идея проста: настроить массив X от 0 до n , сохранить i в каждом X_i , а затем выполнить массив начиная с 0 и всякий раз, когда X_i = i зацикливается на кратные числа i , умножая каждое на ( i - 1) / я . Вы можете вычислить сумму в конце или накапливать ее на ходу. Поскольку ваше сито будет большим, вам нужно будет сегментировать его.

Вот несколько полезных страниц из моего блога: Просеивание для детей и Сегментированное сито эратосфена . Если вы будете там ковыряться, то можете найти и другие интересные вещи.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...