web-dev-qa-db-ja.com

C ++これらのベースのPがQの10進表現で終わるようなすべてのベースを検索します

10進数で2つの数値PとQが与えられます。それらのベースのPが10進数のQ表現で終わるようなallベースを見つけます。

_#include <bits/stdc++.h>

using namespace std;

void convert10tob(int N, int b)
{
     if (N == 0)
        return;
     int x = N % b;
     N /= b;
     if (x < 0)
        N += 1;
     convert10tob(N, b);
     cout<< x < 0 ? x + (b * -1) : x;
     return;
}

int countDigit(long long n) 
{ 
    if (n == 0) 
        return 0; 
    return 1 + countDigit(n / 10); 
} 

int main()
{
    long P, Q;
    cin>>P>>Q;
    n = countDigit(Q);
    return 0;
}

_

私の考えは、Pを他のベースに変換し、P % pow(10, numberofdigits(B)) == Bがtrueかどうかを確認することでした。

ええと、私はいくつかの有限の数の塩基をチェックできますが、どこで(どの塩基の後に)チェックを停止するかを知るにはどうすればよいですか。ここで行き詰まりました。

より明確にするために、ここに例を示します:_P=71,Q=13_の場合、回答は_68_および_4_である必要があります

5
VIVID

コーナーケース_P < 10_および_P == Q_に無限の基底ソリューションがあることを回避するために、関心があるのは基底_B <= P_のみであると想定します。

最後の桁に正しい値を設定するには、_P % B == Q % 10_が必要です。

_B divides P - (Q % 10)
_

この事実を使用して、より効率的なものを作成しましょう。

_#include <vector>

std::vector<size_t> find_divisors(size_t P) {
    // returns divisors d of P, with 1 < d <= P
    std::vector<size_t> D{P};
    for(size_t i = 2; i <= P/i; ++i)
        if (P % i == 0) {
            D.Push_back(i);
            D.Push_back(P/i);
        }
    return D;
}

std::vector<size_t> find_bases(size_t P, size_t Q) {
    std::vector<size_t> bases;
    for(size_t B: find_divisors(P - (Q % 10))) {
        size_t p = P, q = Q;
        while (q) {
            if ((p % B) != (q % 10)) // checks digits are the same
                break;
            p /= B;
            q /= 10;
        }
        if (q == 0) // all digits were equal
            bases.Push_back(B);
    }
    return bases;
}

#include <cstdio>

int main(int argc, char *argv[]) {
    size_t P, Q;
    sscanf(argv[1], "%zu", &P);
    sscanf(argv[2], "%zu", &Q);
    for(size_t B: find_bases(P, Q))
        printf("%zu\n", B);
    return 0;
}
_

複雑さはP - (Q%10)のすべての約数を見つけることと同じですが、Qが1桁の場合、それらは正確な解決策であるため、これ以上期待することはできません。

小さなベンチマーク:

_> time ./find_bases 16285263 13
12
4035
16285260
0.00s user 0.00s system 54% cpu 0.005 total
_

大きい数:

_> time ./find_bases 4894432871088700845 13
6
42
2212336518
4894432871088700842
25.80s user 0.04s system 99% cpu 25.867 total
_

さらに、64ビット数のすべての除数を見つけるための、より複雑ですがより高速な実装を使用します。

_#include <cstdio>
#include <map>
#include <numeric>
#include <vector>

std::vector<size_t> find_divisors(size_t P) {
    // returns divisors d of P, with 1 < d <= P
    std::vector<size_t> D{P};
    for(size_t i = 2; i <= P/i; ++i)
        if (P % i == 0) {
            D.Push_back(i);
            D.Push_back(P/i);
        }
    return D;
}

size_t mulmod(size_t a, size_t b, size_t mod) {
    return (__uint128_t)a * b % mod;
}

size_t modexp(size_t base, size_t exponent, size_t mod)
{
    size_t x = 1, y = base;
    while (exponent) {
        if (exponent & 1)
            x = mulmod(x, y, mod);
        y = mulmod(y, y, mod);
        exponent >>= 1;
    }
    return x % mod;
}

bool deterministic_isprime(size_t p)
{
    static const unsigned char bases[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
    // https://en.wikipedia.org/wiki/Miller%E2%80%93Rabin_primality_test#Testing_against_small_sets_of_bases
    if (p < 2)
        return false;
    if (p != 2 && p % 2 == 0)
        return false;
    size_t s = (p - 1) >> __builtin_ctz(p-1);
    for (size_t i = 0; i < sizeof(bases); i++) {
        size_t a = bases[i], temp = s;
        size_t mod = modexp(a, temp, p);
        while (temp != p - 1 && mod != 1 && mod != p - 1) {
            mod = mulmod(mod, mod, p);
            temp *= 2;
        }
        if (mod != p - 1 && temp % 2 == 0)
            return false;
    }
    return true;
}

size_t abs_diff(size_t x, size_t y) {
    return (x > y) ? (x - y) : (y - x);
}

size_t pollard_rho(size_t n, size_t x0=2, size_t c=1) {
    auto f = [n,c](size_t x){ return (mulmod(x, x, n) + c) % n; };
    size_t x = x0, y = x0, g = 1;
    while (g == 1) {
        x = f(x);
        y = f(f(y));
        g = std::gcd(abs_diff(x, y), n);
    }
    return g;
}

std::vector<std::pair<size_t, size_t>> factorize_small(size_t &P) {
    std::vector<std::pair<size_t, size_t>> factors;
    if ((P & 1) == 0) {
        size_t ctz = __builtin_ctzll(P);
        P >>= ctz;
        factors.emplace_back(2, ctz);
    }
    size_t i;
    for(i = 3; i <= P/i; i += 2) {
        if (i > (1<<22))
            break;
        size_t multiplicity = 0;
        while ((P % i) == 0) {
            ++multiplicity;
            P /= i;
        }
        if (multiplicity)
            factors.emplace_back(i, multiplicity);
    }
    if (P > 1 && i > P/i) {
        factors.emplace_back(P, 1);
        P = 1;
    }
    return factors;
}

std::vector<std::pair<size_t, size_t>> factorize_big(size_t P) {
    auto factors = factorize_small(P);
    if (P == 1)
        return factors;
    if (deterministic_isprime(P)) {
        factors.emplace_back(P, 1);
        return factors;
    }
    std::map<size_t, size_t> factors_map;
    factors_map.insert(factors.begin(), factors.end());
    size_t some_factor = pollard_rho(P);
    for(auto i: {some_factor, P/some_factor})
        for(auto const& [p, expo]: factorize_big(i))
            factors_map[p] += expo;
    return {factors_map.begin(), factors_map.end()};
}

std::vector<size_t> all_divisors(size_t P) {
    std::vector<size_t> divisors{1};
    for(auto const& [p, expo]: factorize_big(P)) {
        size_t ppow = p, previous_size = divisors.size();
        for(size_t i = 0; i < expo; ++i, ppow *= p)
            for(size_t j = 0; j < previous_size; ++j)
                divisors.Push_back(divisors[j] * ppow);
    }
    return divisors;
}

std::vector<size_t> find_bases(size_t P, size_t Q) {
    if (P <= (Q%10))
        return {};
    std::vector<size_t> bases;
    for(size_t B: all_divisors(P - (Q % 10))) {
        if (B == 1)
            continue;
        size_t p = P, q = Q;
        while (q) {
            if ((p % B) != (q % 10)) // checks digits are the same
                break;
            p /= B;
            q /= 10;
        }
        if (q == 0) // all digits were equal
            bases.Push_back(B);
    }
    return bases;
}

int main(int argc, char *argv[]) {
    std::vector<std::pair<size_t, size_t>> tests;
    if (argc > 1) {
        size_t P, Q;
        sscanf(argv[1], "%zu", &P);
        sscanf(argv[2], "%zu", &Q);
        tests.emplace_back(P, Q);
    } else {
        tests.assign({
            {0,0}, {9, 9}, {3, 4}, {4, 0}, {4, 2}, {71, 3}, {71, 13}, 
            {36, 100}, {172448, 12}, {172443, 123},
            {49*25*8*81*11*17, 120}, {4894432871088700845ull, 13}, {18401055938125660803ull, 13},
            {9249004726666694188ull, 19}
        });
    }
    for(auto & [P, Q]: tests) {
        auto bases = find_bases(P, Q);
        if (tests.size() > 1)
            printf("%zu, %zu: ", P, Q);
        if (bases.empty()) {
            printf(" None");
        } else {
            for(size_t B: bases)
                printf("%zu ", B);
        }
        printf("\n");
    }
    return 0;
}
_

私たちは今持っています:

_> time ./find_bases
0, 0:  None
9, 9:  None
3, 4:  None
4, 0: 2 4 
4, 2:  None
71, 3: 4 17 34 68 
71, 13: 4 68 
36, 100: 2 3 6 
172448, 12: 6 172446 
172443, 123: 4 
148440600, 120: 4 
4894432871088700845, 13: 6 42 2212336518 4894432871088700842 
18401055938125660803, 13: 13 17 23 18401055938125660800 
9249004726666694188, 19: 9249004726666694179 9249004726666694179
0.09s user 0.00s system 96% cpu 0.093 total
_

できるだけ速く:)

(注:これは、Bob__からの回答で約10秒になります)

2
One Lyner