contributed by < BennyWang1007 >
作業書寫規範:
[TOC]
: 筆記左上方已有 Table of Contents (TOC) 功能,不需要畫蛇添足c=
或 cpp=
變更為 c
或 cpp
。行號只在後續討論明確需要行號時,才要出現,否則維持精簡的展現。可留意「你所不知道的 C 語言: linked list 和非連續記憶體」裡頭程式碼展現的方式diff
標示),可附上對應 GitHub commit 的超連結,列出程式碼是為了「檢討」和「便於他人參與討論」:::info
, :::success
, :::warning
等標示,儘量用清晰的文字書寫。:::danger
則僅限授課教師作為批注使用〈
和 〉
,非「小於」和「大於」符號$ gcc --version
gcc (Ubuntu 13.3.0-6ubuntu2~24.04) 13.3.0
$ lscpu
架構: x86_64
CPU 作業模式: 32-bit, 64-bit
Address sizes: 39 bits physical, 48 bits virtual
Byte Order: Little Endian
CPU(s): 20
On-line CPU(s) list: 0-19
供應商識別號: GenuineIntel
Model name: 12th Gen Intel(R) Core(TM) i7-12700H
CPU 家族: 6
型號: 154
每核心執行緒數: 2
每通訊端核心數: 14
Socket(s): 1
製程: 3
CPU(s) scaling MHz: 23%
CPU max MHz: 4700.0000
CPU min MHz: 400.0000
BogoMIPS: 5376.00
Virtualization features:
虛擬: VT-x
Caches (sum of all):
L1d: 544 KiB (14 instances)
L1i: 704 KiB (14 instances)
L2: 11.5 MiB (8 instances)
L3: 24 MiB (1 instance)
NUMA:
NUMA 節點: 1
NUMA node0 CPU(s): 0-19
AAAA = (n+d-1)/d
BBBB = 0x7fffffff
CCCC = n+m
DDDD = 1
EEEE = mpi_gcd(rop,op2,r)
static size_t ceil_div(size_t n, size_t d)
{
// return AAAA;
return (n+d-1)/d;
}
除法取上高斯等同於加上(除數-1)然後相除取下高斯:
// #define INTMAX BBBB
#define INTMAX 0x7fffffff
有號整數的最大值為 0x7fffffff
在 mpi_mul_naive
中,首先對每一對 31-bit 的 limb 相乘,會產生一個最大 62-bit 的中間結果,接下來進行加總與進位處理(res[n+m] += a[n] * b[m]):
// for (size_t k = CCCC; c || r; ++k)
for (size_t k = n+m; c || r; ++k)
最後用位元運算操作加法以及進位。
mpi_fdiv_qr
模擬二進位長除法(從高位開始往低位掃)。
用 mpi_testbit() 檢查 n 的每一位,把 r
左移一位,加上該位。
若 r ≥ d,則減去 d
,並把 q
的那一位設為 1。
mpi_gcd
等價於輾轉相除法:
if (op2 == 0)
return op1;
else
gcd(op2, op1 % op2)
因此 EEEE = mpi_gcd(rop, op2, r)
;
將程式碼改成避免遞迴
void mpi_gcd(mpi_t rop, const mpi_t op1, const mpi_t op2)
{
...
while (mpi_cmp_u32(b, 0) != 0) {
mpi_fdiv_qr(NULL, t, a, b); // t = a % b, don't care the quotient
mpi_set(a, b); // a = b
mpi_set(b, t); // b = t
}
...
可以使用「Stein's GCD(Binary GCD)」:
以下為最終程式碼
int mpi_even(const mpi_t op)
{
return (op->capacity == 0) || ((op->data[0] & 1) == 0);
}
void mpi_rshift1(mpi_t rop)
{
uint32_t carry = 0;
for (size_t i = rop->capacity; i-- > 0;) {
uint32_t new_carry = rop->data[i] & 1;
rop->data[i] = (rop->data[i] >> 1) | (carry << 30);
carry = new_carry;
}
mpi_compact(rop);
}
void mpi_lshift(mpi_t rop, const mpi_t op, size_t k)
{
mpi_set(rop, op);
while (k--)
mpi_lshift1(rop);
}
void mpi_swap(mpi_t a, mpi_t b)
{
mpi_t tmp;
mpi_init(tmp);
mpi_set(tmp, a);
mpi_set(a, b);
mpi_set(b, tmp);
mpi_clear(tmp);
}
void mpi_gcd(mpi_t rop, const mpi_t op1, const mpi_t op2)
{
mpi_t a, b;
mpi_init(a);
mpi_init(b);
mpi_set(a, op1);
mpi_set(b, op2);
if (mpi_cmp_u32(a, 0) == 0) {
mpi_set(rop, b);
mpi_clear(a);
mpi_clear(b);
return;
}
if (mpi_cmp_u32(b, 0) == 0) {
mpi_set(rop, a);
mpi_clear(a);
mpi_clear(b);
return;
}
// Find common power of 2 (d)
size_t d = 0;
while (mpi_even(a) && mpi_even(b)) {
mpi_rshift1(a);
mpi_rshift1(b);
++d;
}
// Divide a by 2 until it becomes odd
while (mpi_even(a))
mpi_rshift1(a);
do {
while (mpi_even(b))
mpi_rshift1(b);
if (mpi_cmp(a, b) > 0)
mpi_swap(a, b); // Ensure a <= b
mpi_sub(b, b, a); // b = b - a
} while (mpi_cmp_u32(b, 0) != 0);
// Restore common factors of 2
if (d > 0)
mpi_lshift(a, a, d);
mpi_set(rop, a);
mpi_clear(a);
mpi_clear(b);
}
測驗二:
\
AAAA = ENV_RUNNABLE
BBBB = attempts+1
CCCC = coro_schedule
DDDD = coro_yield
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up