詳細解題報告 - 256位元整數質因數分解實現
本程式實現了對 256 位元無符號整數(u256)的質因數分解功能。輸入任意大整數,輸出其所有質因數(按升序排列)。
struct u256 {
uint64_t d[4]; // 用4個64位元整數表示256位元數字
}
d[0] 存儲最低64位元,d[3] 存儲最高64位元。這種設計使得低位運算更加直觀和高效。u256() { d[0] = d[1] = d[2] = d[3] = 0; }
u256(uint64_t x) { d[0] = x; d[1] = d[2] = d[3] = 0; }
bool operator==(const u256& o) const {
return d[0] == o.d[0] && d[1] == o.d[1] &&
d[2] == o.d[2] && d[3] == o.d[3];
}
逐一比較四個 64 位元分量,確保完全相等。
bool operator<(const u256& o) const {
for (int i = 3; i >= 0; --i) {
if (d[i] != o.d[i]) return d[i] < o.d[i];
}
return false;
}
從最高位元開始比較,實現字典序比較。這確保了大數比較的正確性。
u256 operator+(const u256& o) const {
u256 r;
uint64_t c = 0; // 進位標誌
for (int i = 0; i < 4; ++i) {
uint64_t s = d[i] + c;
c = (s < c); // 檢測進位溢出
s += o.d[i];
c += (s < o.d[i]); // 檢測第二次加法溢出
r.d[i] = s;
}
return r;
}
s < c 則表示發生進位。這種方法避免了複雜的條件判斷,提高了運算效率。u256 operator-(const u256& o) const {
u256 r;
uint64_t c = 0; // 借位標誌
for (int i = 0; i < 4; ++i) {
uint64_t s = d[i] - c;
c = (d[i] < c); // 檢測借位
c += (s < o.d[i]); // 檢測第二次借位
r.d[i] = s - o.d[i];
}
return r;
}
u256 shl1() const {
u256 r;
uint64_t c = 0;
for (int i = 0; i < 4; ++i) {
r.d[i] = (d[i] << 1) | c;
c = d[i] >> 63;
}
return r;
}
移位並處理進位位元,相當於乘以 2。
u256 shr1() const {
u256 r;
uint64_t c = 0;
for (int i = 3; i >= 0; --i) {
r.d[i] = (d[i] >> 1) | c;
c = d[i] << 63;
}
return r;
}
移位並處理借位位元,相當於除以 2。
int bit_len(const u256& a) {
for (int i = 3; i >= 0; --i) {
if (a.d[i]) {
return i * 64 + 64 - __builtin_clzll(a.d[i]);
}
}
return 0;
}
__builtin_clzll 計算前導零個數void divMod(u256 a, u256 b, u256& q, u256& r) {
q = u256(0);
r = a;
if (a < b) return; // 被除數小於除數,商為0
int shift = bit_len(a) - bit_len(b);
u256 sb = b;
// 將除數左移至與被除數同一數量級
for (int i = 0; i < shift; ++i) sb = sb.shl1();
// 二進制長除法
for (int i = 0; i <= shift; ++i) {
q = q.shl1();
if (!(r < sb)) {
r = r - sb;
q.d[0] |= 1; // 商的當前位元設為1
}
sb = sb.shr1();
}
}
u256 gcd(u256 a, u256 b) {
if (a == u256(0)) return b;
if (b == u256(0)) return a;
// 提取公共因數 2
int shift = 0;
while ((a.d[0] & 1) == 0 && (b.d[0] & 1) == 0) {
a = a.shr1();
b = b.shr1();
shift++;
}
// 確保 a 為奇數
while ((a.d[0] & 1) == 0) a = a.shr1();
// 主循環
while (!(b == u256(0))) {
while ((b.d[0] & 1) == 0) b = b.shr1();
if (a < b) {
b = b - a;
} else {
u256 diff = a - b;
a = b;
b = diff;
}
}
// 恢復公共因數 2
for (int i = 0; i < shift; ++i) a = a.shl1();
return a;
}
u256 parse_u256(const std::string& s) {
u256 res(0);
for (char c : s) {
// res = res * 10 + (c - '0')
u256 r1 = res.shl1(); // res * 2
u256 r3 = r1.shl1().shl1(); // res * 8
res = r3 + r1 + u256(c - '0'); // res * 10
}
return res;
}
使用位移操作實現乘以 10:10 = 8 + 2
std::string to_string(u256 a) {
if (a == u256(0)) return "0";
std::string s = "";
u256 e19(10000000000000000000ULL); // 10^19
while (!(a == u256(0))) {
u256 q, r;
divMod(a, e19, q, r);
uint64_t rem = r.d[0];
a = q;
if (a == u256(0)) {
s = std::to_string(rem) + s;
} else {
// 補足前導零
std::string rs = std::to_string(rem);
s = std::string(19 - rs.length(), '0') + rs + s;
}
}
return s;
}
struct Mont {
u256 n; // 模數
uint64_t n_inv; // n 的模逆元(針對 2^64)
u256 r2; // R^2 mod n,用於轉換
u256 one; // 1 在 Montgomery 形式下的表示
}
Mont(u256 n) : n(n) {
// 計算 n_inv = -n^(-1) mod 2^64
n_inv = n.d[0];
for (int i = 0; i < 5; ++i)
n_inv = n_inv * (2ull - n.d[0] * n_inv);
n_inv = -n_inv;
// 計算 R mod n,其中 R = 2^256
u256 r; r.d[0] = 1;
for (int i = 0; i < 256; ++i) {
r = r.shl1();
if (!(r < n)) r = r - n;
}
r2 = r;
// 計算 R^2 mod n
for (int i = 0; i < 256; ++i) {
r2 = r2.shl1();
if (!(r2 < n)) r2 = r2 - n;
}
one = r;
}
u256 mul(const u256& a, const u256& b) const {
uint64_t res[5] = {0};
// 對每個 a 的分量
for (int i = 0; i < 4; ++i) {
// 第一階段:計算 a[i] * b + res
uint64_t carry = 0;
unsigned __int128 p;
for (int j = 0; j < 4; ++j) {
p = (unsigned __int128)a.d[i] * b.d[j] + res[j] + carry;
res[j] = (uint64_t)p;
carry = (uint64_t)(p >> 64);
}
res[4] = carry;
// 第二階段:Montgomery reduction
uint64_t q = res[0] * n_inv;
carry = 0;
for (int j = 0; j < 4; ++j) {
p = (unsigned __int128)q * n.d[j] + res[j] + carry;
if (j > 0) res[j-1] = (uint64_t)p;
carry = (uint64_t)(p >> 64);
}
res[3] = res[4] + carry;
}
u256 r;
r.d[0] = res[0]; r.d[1] = res[1];
r.d[2] = res[2]; r.d[3] = res[3];
if (res[4] || !(r < n)) {
r = r - n;
}
return r;
}
u256 add_mod(const u256& a, const u256& b) const {
u256 r;
uint64_t c = 0;
for (int i = 0; i < 4; ++i) {
uint64_t s = a.d[i] + c;
c = (s < c);
s += b.d[i];
c += (s < b.d[i]);
r.d[i] = s;
}
// 若結果 >= n,則減去 n
if (c || !(r < n)) {
r = r - n;
}
return r;
}
u256 to_mont(const u256& a) const {
return mul(a, r2); // aR mod n
}
u256 from_mont(const u256& a) const {
return mul(a, u256(1)); // a/R mod n
}
bool miller_rabin(u256 n) {
if (n == u256(2) || n == u256(3)) return true;
if (n < u256(2) || (n.d[0] & 0b1) == 0) return false;
// 將 n-1 表示為 2^s * d
u256 d = n - u256(1);
int s = 0;
while ((d.d[0] & 1) == 0) {
d = d.shr1();
s++;
}
Mont mont(n);
u256 one_mont = mont.one;
u256 n_minus_1_mont = mont.to_mont(n - u256(1));
static const uint64_t bases[] = {
2, 3, 5, 7, 11, 13, 17, 19, 23, 29,
31, 37, 41, 43, 47, 53, 59, 61, 67, 71
};
for (uint64_t base : bases) {
u256 a(base);
if (!(a < n)) break;
// 計算 a^d mod n(使用快速冪)
u256 x = one_mont;
u256 base_mont = mont.to_mont(a);
int len = bit_len(d);
for (int i = 0; i < len; ++i) {
if ((d.d[i / 64] >> (i % 64)) & 1)
x = mont.mul(x, base_mont);
base_mont = mont.mul(base_mont, base_mont);
}
if (x == one_mont || x == n_minus_1_mont) continue;
// 進行 s-1 次平方測試
bool composite = true;
for (int r = 1; r < s; ++r) {
x = mont.mul(x, x);
if (x == n_minus_1_mont) {
composite = false;
break;
}
}
if (composite) return false;
}
return true;
}
u256 pollard_rho(u256 n) {
if ((n.d[0] & 0b1) == 0) return u256(2);
Mont mont(n);
// 偽隨機數生成器(Xorshift)
static uint64_t seed = 1337;
auto rand64 = [&]() {
seed ^= seed << 13;
seed ^= seed >> 7;
seed ^= seed << 17;
return seed;
};
while (true) {
u256 c = rand256(); // 隨機常數
u256 c_mont = mont.to_mont(c);
u256 x = mont.to_mont(u256(2));
u256 y = x;
u256 q = mont.one;
int power = 1, lam = 1;
u256 d = u256(1);
while (d == u256(1)) {
// 迭代函數:f(x) = x^2 + c
x = mont.add_mod(mont.mul(x, x), c_mont);
// 累積乘積
u256 diff = (y < x) ? (x - y) : (y - x);
q = mont.mul(q, diff);
// 定期檢查 gcd
if (lam % 128 == 0) {
d = gcd(mont.from_mont(q), n);
if (!(d == u256(1))) break;
}
// Floyd's cycle detection 變體
if (power == lam) {
d = gcd(mont.from_mont(q), n);
if (!(d == u256(1))) break;
y = x;
power <<= 1;
lam = 0;
}
lam++;
}
// 回溯處理
if (!(d == u256(1))) {
if (d == n) {
// 失敗,需要回溯
d = u256(1);
x = y;
while (d == u256(1)) {
x = mont.add_mod(mont.mul(x, x), c_mont);
u256 diff = (y < x) ? (x - y) : (y - x);
d = gcd(mont.from_mont(diff), n);
}
if (d == n) continue; // 換隨機數重試
}
return d;
}
}
}
利用偽隨機序列產生循環,在循環中找到兩個不同的數 x、y 使得 x ≡ y (mod p),其中 p 是 n 的一個因數。
void factorize(u256 n, std::vector& factors) {
if (n == u256(1)) return;
if (miller_rabin(n)) {
factors.push_back(n);
return;
}
u256 d = pollard_rho(n);
factorize(d, factors);
factorize(n / d, factors);
}
若 n = 1,結束遞迴
若 n 為質數,加入結果集
使用 Pollard's Rho 找到因數 d
遞迴分解 d 和 n/d
int main() {
std::ios_base::sync_with_stdio(false);
std::cin.tie(NULL);
std::string s;
while (std::cin >> s) {
u256 n = parse_u256(s);
std::vector factors;
factorize(n, factors);
std::sort(factors.begin(), factors.end());
for (size_t i = 0; i < factors.size(); ++i) {
std::cout << to_string(factors[i])
<< (i + 1 == factors.size() ? "" : " ");
}
std::cout << "\n";
}
return 0;
}
#pragma GCC optimize("O3,unroll-loops")
啟用最高級別優化和循環展開
避免直接除法運算
效率提升 2-3 倍
每 128 次迭代才檢查一次
減少昂貴的 GCD 計算
利用 unsigned __int128
處理乘法溢出更高效
用位移代替乘除 2
大幅提升運算速度
關閉 stdio 同步
加速輸入輸出
| 算法 | 時間複雜度 | 說明 |
|---|---|---|
| u256 加法/減法 | O(1) | 固定 4 次 64 位元運算 |
| u256 乘法 | O(1) | 16 次 64×64→128 位元乘法 |
| u256 除法 | O(n) | n 為位元數差,最多 256 次迭代 |
| Binary GCD | O(n²) | n 為位元數,實際運行快於歐幾里得 |
| Miller-Rabin | O(k log³ n) | k=20 個測試基數 |
| Pollard's Rho | O(n1/4) | 期望時間複雜度 |
| 整體因數分解 | O(n1/4 log n) | 包含質數測試和遞迴分解 |
解決: 使用 Montgomery 模乘,將除法轉換為乘法
解決: 批量累積後一次性計算,減少調用頻率
解決: 使用 Xorshift 快速偽隨機數生成器
解決: 數據結構緊湊,利用 CPU 緩存
本程式實現了一個高效的 256 位元整數因數分解系統,主要特點包括:
C++17 大數運算 Montgomery 乘法 Miller-Rabin Pollard's Rho 高性能優化
© 2024 C++ 大數因數分解程式解題報告
本報告詳細說明了 256 位元整數質因數分解的完整實現