2
\$\begingroup\$

I usedstd::multiset to determine how many times that factor appears.I'd like to know if it can be improved; I've focused on the algorithm rather than the code.

https://en.wikipedia.org/wiki/Pollard%27s_rho_algorithm

std::multiset<std::int64_t> pollards_rho(std::int64_t n){    std::multiset<std::int64_t> factors{};    if (n == 0 || n == 1)    {        return factors;    }    if (n < 0)    {        factors.insert(-1);        n = -n;    }    // If n is even, add 2 and get rid of all even factors.    if (n % 2 == 0)    {        factors.insert(2);        while (n % 2 == 0)        {            n /= 2;        }        if (n == 1)        {            return factors;        }    }    // Select an x_0 uniformly at random from [2, n - 1].    // x_0 is chosen as 2 for simplicity.    //    // Floyd's cycle-finding algorithm.    // x => x_i    // y => x_i+1    std::int64_t x{2}, y{x}, d{1};    // f(x_i+1) = x_i^2 + 1 mod n    auto f = [n](const std::int64_t num) noexcept    {        return (num * num + 1) % n;    };    // If we obtain a factor greater than 1, we are done.    while (d == 1)    {        x = f(x);        y = f(f(y));        d = std::gcd(std::abs(x - y), n);    }    /**     * if (d == n)     * {     *     return std::nullopt;     * }     * else     * {     *     return d;     * }     */    // The algorithm normally returns either a non-trivial factor d or failure,    // but we need to find all prime factors.    if (d == n)    {        // If d and n are the same, we have a prime number.        factors.insert(n);    }    else    {        // Look for other factors.        factors.merge(pollards_rho(d));        factors.merge(pollards_rho(n / d));    }    return factors;}
J_H's user avatar
J_H
43.3k3 gold badges38 silver badges158 bronze badges
askedAug 18, 2024 at 13:26
ozan's user avatar
\$\endgroup\$

1 Answer1

5
\$\begingroup\$

I usedstd::multiset to determine how many times that factor appears

It's an option. You could also use a plain old map to store a factor (key) and its exponent (value):

  • In case we only care about factors and not their exponents, the exponents are easier to ignore that way.
  • In case we do care about the exponents of factors, we just get them, instead of having to count how many times a factor appears in a multiset.
  • "High" exponents don't cost as much duplication, but really an exponent can never be high, at worst you could have 63 copies of2 in your multiset.
  • amap is a less rare/unusual data structure thanmultiset.
// If n is even, add 2 and get rid of all even factors.if (n % 2 == 0){    factors.insert(2);    while (n % 2 == 0)    {        n /= 2;    }    if (n == 1)    {        return factors;    }}

This does not determine how many times the factor of 2 appears, it's only added once. By the way with C++20 you could usestd::countr_zero instead of a loop, that combines well with returning a map instead of a multiset.

// f(x_i+1) = x_i^2 + 1 mod nauto f = [n](const std::int64_t num) noexcept{    return (num * num + 1) % n;};

This does not handle the case whennum * num overflows, which it can easily do. You could fix this by restricting the input to fit in anint32_t (but still do this arithmetic inf withint64_t - the square of a 32-bit integer fits in 64 bits so it works), or implement a proper 64-bit modular multiplication, that's a bit tricky but possible.

// The algorithm normally returns either a non-trivial factor d or failure,// but we need to find all prime factors.

Careful: Pollard's Rho can also spuriously fail to find a factor when its input is not prime.

// If d and n are the same, we have a prime number.

So this is a bug. You can getd == n whenn is actually composite, but your code would decline to factor it.

answeredAug 18, 2024 at 14:28
user555045's user avatar
\$\endgroup\$
3
  • \$\begingroup\$"You can get d == n when n is actually composite, but your code would decline to factor it." Is this the limitation of the algorithm or did I implemented it badly?\$\endgroup\$CommentedAug 18, 2024 at 15:11
  • 1
    \$\begingroup\$@ozan the algorithm inherently works that way\$\endgroup\$CommentedAug 18, 2024 at 15:20
  • \$\begingroup\$I see, I also fixed adding factors of 2 you mentioned, looking for the other issues. Thanks.\$\endgroup\$CommentedAug 18, 2024 at 16:06

You mustlog in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.