Grade 12 · Math for programmingCitește în română

Smallest-prime-factor sieve and fast factorization

Original: Ciur cu cel mai mic divizor prim și factorizare rapidă

O(log n)C++1770 lines1 example

The idea

A variant of the Sieve of Eratosthenes: instead of only marking 'composite', we store in dp[x] the smallest prime divisor of x. For every prime p (dp[p] still 0) we fill dp[p], dp[2p], dp[3p], ... where it is still 0. Then any x ≤ MAX is factorized very quickly: we repeatedly divide x by dp[x] and count how many times each prime appears. For q queries, the cost is a single sieve plus a few steps per query.

Original description (Romanian)

O variantă a ciurului lui Eratostene: în loc să marcăm doar „compus”, reținem în dp[x] cel mai mic divizor prim al lui x. Pentru fiecare prim p (dp[p] rămas 0) completăm dp[p], dp[2p], dp[3p], ... acolo unde încă e 0. Apoi orice x ≤ MAX se descompune foarte repede: împărțim repetat x la dp[x] și numărăm de câte ori apare fiecare prim. Pentru q întrebări, costul este un singur ciur plus câțiva pași pe întrebare.

Complexity
O(MAX log log MAX) for the sieve, O(log x) per factorization
Grade
Grade 12 (Clasa a XII-a)

Examples

Example 1

Input

4
360 97 1 1000000

Expected output

360 = 2^3 * 3^2 * 5
97 = 97
1 = 1
1000000 = 2^6 * 5^6

The code

Comments are in Romanian, as in the original collection. Edit freely: you can reset at any time.

ciur-cel-mai-mic-divizor-prim.cpp
/*
 * Titlu: Ciur cu cel mai mic divizor prim și factorizare rapidă
 * Clasa: 12
 * Capitol: Matematică pentru programare
 * Descriere: O variantă a ciurului lui Eratostene: în loc să marcăm doar „compus”,
 *            reținem în dp[x] cel mai mic divizor prim al lui x. Pentru fiecare
 *            prim p (dp[p] rămas 0) completăm dp[p], dp[2p], dp[3p], ... acolo unde
 *            încă e 0. Apoi orice x ≤ MAX se descompune foarte repede: împărțim
 *            repetat x la dp[x] și numărăm de câte ori apare fiecare prim. Pentru
 *            q întrebări, costul este un singur ciur plus câțiva pași pe întrebare.
 * Complexitate: O(MAX log log MAX) pentru ciur, O(log x) pentru fiecare factorizare
 *
 * Exemplu
 * Intrare:
 * 4
 * 360 97 1 1000000
 * Ieșire:
 * 360 = 2^3 * 3^2 * 5
 * 97 = 97
 * 1 = 1
 * 1000000 = 2^6 * 5^6
 */
#include <iostream>
#include <vector>
using namespace std;

const int MAX = 1000000;

int main()
{
    vector<int> dp(MAX + 1, 0); // dp[x] = cel mai mic divizor prim al lui x
    for (int i = 2; i <= MAX; i++)
        if (dp[i] == 0) // i este prim
            for (int j = i; j <= MAX; j += i)
                if (dp[j] == 0)
                    dp[j] = i;

    int q;
    cin >> q;
    for (int t = 0; t < q; t++)
    {
        int x;
        cin >> x;
        cout << x << " = ";
        if (x == 1)
        {
            cout << 1 << '\n';
            continue;
        }
        bool primul = true;
        while (x > 1)
        {
            int p = dp[x], e = 0;
            while (x % p == 0)
            {
                x /= p;
                e++;
            }
            if (!primul)
                cout << " * ";
            primul = false;
            cout << p;
            if (e > 1)
                cout << '^' << e;
        }
        cout << '\n';
    }
    return 0;
}

Runs on a real g++ (C++17, -O2) through Compiler Explorer. Keep runs short; there's a time limit.