티스토리 뷰

알고리즘/백준

백준 13926 gcd(n,k)=1

4567은 소수 2024. 1. 6. 04:28

https://www.acmicpc.net/problem/13926

 

13926번: gcd(n, k) = 1

자연수 n이 주어졌을 때, gcd(n, k) = 1을 만족하는 자연수 1 ≤ k ≤ n 의 개수를 구하는 프로그램을 작성하시오.

www.acmicpc.net

 

주어진 정수 n에 대해서 오일러 피 함수 값을 구하는 문제입니다. 

( __int128 을 안 써서 시간초과가 뜬 거라고는 예상하지 못했다..... )

 

n의 최대 입력이 10^18 이므로 약 2^60 정도됩니다. 따라서 곱셈 연산 등에서는 __int128 타입으로 계산을 진행해야 합니다.

 

로직은 다음과 같습니다.

 

1. n에 대해 폴라드 로 알고리즘으로 소인수를 구한다.

2. 해당 소인수가 prime인지 여부를 밀러 라빈 소수 판정법으로 계산한다.

3. prime으로 판정되면 해당 소인수를 이용하여 오일러 피 함수를 계산한다.

 

폴라드 로 알고리즘 참고

https://www.geeksforgeeks.org/pollards-rho-algorithm-prime-factorization/

 

Pollard's Rho Algorithm for Prime Factorization - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

참고 : 위 코드를 처음 참고하여 작성하였을 때, d == n인 케이스에서 동일하게 재귀를 호출하였지만, 될 때도 있고, 안 될 때도 있었다. 이건 rand seed 문제인 것 같아 재귀 호출이 아닌, 다시 rand를 호출하도록 구성하였다. (아직 왜 그런 지는 잘 모르겠다.....)

 

밀러 라빈 테스트 참고

https://www.geeksforgeeks.org/primality-test-set-3-miller-rabin/

 

Primality Test | Set 3 (Miller–Rabin) - GeeksforGeeks

A Computer Science portal for geeks. It contains well written, well thought and well explained computer science and programming articles, quizzes and practice/competitive programming/company interview Questions.

www.geeksforgeeks.org

 

참고 : 폴라드 로 알고리즘으로 소인수로 판정된 값에 대해 위 코드 적용 시 일부 케이스에서 합성수도 소수로 판정하게 되었다. 기저 사례 몇 가지를 추가하여 해결하였다. (다른 분들 코드를 보니 37까지 사용한 케이스가 많아 37까지만 체크하도록 구성)

 

코드는 다음과 같습니다.

#include <iostream>
#include <vector>
#include <algorithm>
#include <cstdlib>
#include <ctime>

using namespace std;

// n=1 : 답 1 
// n : miller rabin prime test 로 소수인지 판단
// 소수면 답 n-1 
// 아니면 pollard rho 로 소인수분해 후 euler phi function 계산 

// x^y mod p 
long long power(__int128 x, __int128 y, __int128 p)
{
    __int128 res = 1;
    x %= p;
    
    while(y != 0) {
        if(y & 1)
            res = (res * x) % p;
        y >>= 1;
        x = (x * x) % p;
    }
    
    return (long long)res;
}

// miller rabin test 
// check n 
bool miller_rabin_test(long long d, long long n)
{
    srand(time(NULL));
    
    long long a = (rand() % (n-2)) + 2;
    long long x = power(a, d, n);
    
    if(x == 1 || x == (n-1))
        return true;
    
    while(d != (n-1)) {
        x = power(x, 2, n);
        d <<= 1;
        
        if(x == 1)
            return false;
        if(x == n-1)
            return true;
    }
    
    return false;
}

// is n prime ?
// k : miller rabin test loop 
bool prime_check(long long n, int k)
{
    if(n == 1)
        return false;
        
    long long basePrimes[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
    for(auto i : basePrimes) {
        if(n == i)
            return true;
        if(n % i == 0)
            return false;
    }
    
    long long d = n - 1;
    while(d & 1 == 0)
        d >>= 1;
    
    for(int i = 0; i < k; i++) {
        if(miller_rabin_test(d, n) == false)
            return false;
    }
    
    return true;
}

// gcd(a, b)
long long gcd(long long a, long long b)
{
    if(b == 0)
        return a;
    return gcd(b, a % b);
}

// pollard rho alg 
long long pollard_rho(long long n, int k)
{
    if(n == 1)
        return n;
    if(n & 1 == 0)
        return 2;
        
    srand(time(NULL));
    
    long long x = (rand() % (n-2)) + 2;
    long long y = x;
    long long c = (rand() % (n-1)) + 1;
    long long d = 1;
    
    while(d == 1) {
        x = (power(x, 2, n) + c) % n;
        y = (power(y, 2, n) + c) % n;
        y = (power(y, 2, n) + c) % n;
        d = gcd(abs(x - y), n);
        
        if(d == n) {
            x = (rand() % (n-2)) + 2;
            y = x;
            c = (rand() % (n-1)) + 1;
            d = 1;
        }
    }
    
    if(prime_check(d, k))
        return d;
    else 
        return pollard_rho(d, k);
}

// factorize from pollard rho 
vector<long long> calc_factors(long long n, int k)
{
    vector<long long> res;
    
    if(n % 2 == 0) {
        res.push_back(2);
        while(n % 2 == 0) 
            n >>= 1;
    }
    
    while(n != 1) {
        if(prime_check(n, k)) {
            res.push_back(n);
            break;
        }
        
        long long factor = pollard_rho(n, k);
        
        res.push_back(factor);
        
        while((n % factor) == 0)
            n /= factor;
    }
    
    return res;
}

// euler phi function
// phi(n) = n * (multiply i=1 to r (1-1/pi))
// k1, k2, .., kr are the number of factors
long long euler_phi(long long n, int k)
{
    if(n == 1)
        return 1;
    if(prime_check(n, k))
        return n-1;
    
    vector<long long> factors = calc_factors(n, k);
    
    long long res = n;
    for(auto f : factors) {
        res -= (res / f);
    }
    
    return res;
}

int main()
{
    long long n;
    cin >> n;
    
    int k = 10;
    
    cout << euler_phi(n, k);
}

'알고리즘 > 백준' 카테고리의 다른 글

백준 22289 큰 수 곱셈 (3) C++  (1) 2024.01.11
백준 11025 요세푸스 문제 3  (0) 2024.01.06
백준 12781 PIZZA ALVOLOC C++  (0) 2023.12.22
백준 1111 IQ Test C++  (0) 2023.12.16
백준 1240 노드사이의 거리 C++  (2) 2023.11.20
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
TAG
more
«   2025/03   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31
글 보관함