結果
問題 | No.3030 ミラー・ラビン素数判定法のテスト |
ユーザー | sakikuroe |
提出日時 | 2024-03-05 13:21:10 |
言語 | Rust (1.77.0 + proconio) |
結果 |
AC
|
実行時間 | 54 ms / 9,973 ms |
コード長 | 5,651 bytes |
コンパイル時間 | 12,556 ms |
コンパイル使用メモリ | 378,708 KB |
実行使用メモリ | 6,820 KB |
最終ジャッジ日時 | 2024-09-29 17:55:32 |
合計ジャッジ時間 | 12,815 ms |
ジャッジサーバーID (参考情報) |
judge3 / judge4 |
(要ログイン)
テストケース
テストケース表示入力 | 結果 | 実行時間 実行使用メモリ |
---|---|---|
testcase_00 | AC | 1 ms
6,816 KB |
testcase_01 | AC | 0 ms
6,816 KB |
testcase_02 | AC | 1 ms
6,820 KB |
testcase_03 | AC | 1 ms
6,820 KB |
testcase_04 | AC | 43 ms
6,816 KB |
testcase_05 | AC | 42 ms
6,816 KB |
testcase_06 | AC | 34 ms
6,816 KB |
testcase_07 | AC | 35 ms
6,816 KB |
testcase_08 | AC | 35 ms
6,816 KB |
testcase_09 | AC | 54 ms
6,820 KB |
ソースコード
// use proconio::input; use std::ops; #[allow(non_snake_case)] /// Montgomery 乗算のための構造体 pub struct Montgomery { pub N: u64, // mod, odd N_prime: u64, // N * N_prime == -1 mod R R2: u64, // R^{2} mod N } #[allow(non_snake_case)] impl Montgomery { const LOG_R: u64 = 64; // R = 2**log_R const R: u128 = 1_u128 << Montgomery::LOG_R; pub fn new(N: u64) -> Self { assert!(N % 2 == 1); fn extended_gcd(a: i128, b: i128) -> (i128, i128) { let (mut s, mut xs, mut ys) = (a, 1, 0); let (mut t, mut xt, mut yt) = (b, 0, 1); while s % t != 0 { let q = s / t; let (u, xu, yu) = (s - q * t, xs - q * xt, ys - q * yt); (s, t) = (t, u); (xs, ys, xt, yt) = (xt, yt, xu, yu); } if t < 0 { (xt, yt) = (-xt, -yt); } (xt, yt) } let N_prime = { let (_, b) = extended_gcd(Montgomery::R as i128, N as i128); if b <= 0 { (-b) as u64 } else { (-b + Montgomery::R as i128) as u64 } }; let R2 = ((Montgomery::R % N as u128) * (Montgomery::R % N as u128) % N as u128) as u64; Montgomery { N, N_prime, R2 } } /// Montgomery reduction /// /// Returns: /// T * R^{-1} mod N fn montgomery_reduction(&self, T: u128) -> u64 { let t = { ((T + (((T as u64).wrapping_mul(self.N_prime)) as u128 * self.N as u128)) >> Montgomery::LOG_R) as u64 }; if t >= self.N { t - self.N } else { t } } /// Returns: /// T * R mod N fn montgomery(&self, T: u64) -> u64 { self.montgomery_reduction(T as u128 * self.R2 as u128) } /// Returns: /// (A + B) * R mod N /// /// Constraints: /// - ar < N /// - br < N fn montgomery_add(&self, ar: u64, br: u64) -> u64 { let t = ar + br; if t < self.N { t } else { t - self.N } } /// Returns: /// A * B * R mod N /// /// Constraints: /// - ar < N /// - br < N fn montgomery_mul(&self, ar: u64, br: u64) -> u64 { self.montgomery_reduction(ar as u128 * br as u128) } } #[derive(Clone)] pub struct MontgomeryModInt<'a> { ar: u64, pub montgomery: &'a Montgomery, } impl<'a> MontgomeryModInt<'a> { pub fn new(a: u64, montgomery: &'a Montgomery) -> MontgomeryModInt<'a> { let ar = montgomery.montgomery(a); MontgomeryModInt { ar, montgomery } } pub fn val(&self) -> u64 { self.montgomery.montgomery_reduction(self.ar as u128) } pub fn pow(&self, mut n: usize) -> MontgomeryModInt<'a> { let mut res = MontgomeryModInt::new(1, self.montgomery); let mut x = self.clone(); while n > 0 { if n % 2 == 1 { res = res * x.clone(); } x = x.clone() * x; n /= 2; } res } } impl<'a> ops::Add for MontgomeryModInt<'a> { type Output = MontgomeryModInt<'a>; fn add(self, other: Self) -> Self { MontgomeryModInt { ar: self.montgomery.montgomery_add(self.ar, other.ar), montgomery: self.montgomery, } } } impl<'a> ops::Mul for MontgomeryModInt<'a> { type Output = MontgomeryModInt<'a>; fn mul(self, other: Self) -> Self { MontgomeryModInt { ar: self.montgomery.montgomery_mul(self.ar, other.ar), montgomery: self.montgomery, } } } /// Returns: /// if n is prime number: /// true /// else: /// false /// /// Algorithm: /// Miller-Rabin /// /// References: /// - [Deterministic variants of the Miller-Rabin primality test. Miller-Rabin SPRP bases records](https://miller-rabin.appspot.com/) /// - [64bit数の素数判定](https://zenn.dev/mizar/articles/791698ea860581) pub fn is_prime(n: u64) -> bool { if n == 0 || n == 1 { return false; } if n == 2 { return true; } if n % 2 == 0 { return false; } // n - 1 == 2**s * d (d: odd) let s = (n - 1).trailing_zeros(); let d = (n - 1) >> s; let mont = Montgomery::new(n); let maybe_prime = |a| { let a = a % n; if a == 0 { return true; } let a = MontgomeryModInt::new(a, &mont); let mut ad = a.pow(d as usize); if ad.val() == 1 || ad.val() == n - 1 { return true; } for _ in 1..s { ad = ad.clone() * ad; if ad.val() == n - 1 { return true; } } false }; [2, 325, 9375, 28178, 450775, 9780504, 1795265022] .into_iter() .all(maybe_prime) } fn main() { // input! { // n: usize, // } let n; { let mut s = String::new(); std::io::stdin().read_line(&mut s).unwrap(); let mut ws = s.split_whitespace(); n = ws.next().unwrap().parse::<usize>().unwrap(); } for _ in 0..n { // input! { // x: usize, // } let x; { let mut s = String::new(); std::io::stdin().read_line(&mut s).unwrap(); let mut ws = s.split_whitespace(); x = ws.next().unwrap().parse::<usize>().unwrap(); } println!("{} {}", x, if is_prime(x as u64) { 1 } else { 0 }); } }