我们知道任意正整数 $a \in \Z^+$ 可以通过质因分解式表达成质数序列 $\gamma_1=2$,$\gamma_2=3$,$\gamma_3=5$……的自然数幂:
$$ a = \prod_{i=1}^{\infty} \gamma_i^{\alpha_i}, \quad \forall i \in \N^+, \, \alpha_i \in \N $$
指数序列构成有限支撑 (finite support) 的自然数序列,即序列属于 $\N^{(\infty)}$:
$$ \N^{(\infty)} := \{ (\alpha_i)_{i \ge 1} \mid \alpha_i \in \N, \, {\rm 且只有有限个} \, \alpha_i \neq 0 \} $$
其中,「只有有限个 $\alpha_i \neq 0$」的条件就是所谓的「有限支撑的 (finite support)」。
容易证明这是一个 $a \in \Z^+$ 到 $(\alpha_i){i \ge 1} \in \N^{(\infty)}$ 之间的双射。因而 $(\alpha_i){i \ge 1}$ 也被称为 $a$ 的「指数向量」。
问题:要如何枚举指数向量,能够兼顾向量的长度(最大质数索引)、L1 模长,或甚至是对应的正整数域的值的大小(但我当然不希望大量进行因数分解)?
潜在思路:采用类似于嵌套 Cantor 配对函数的枚举方法?
解答:使用类似于 zig-zag 的方法,先枚举「长度 + L1 模长」综合量,再将综合量分配给长度和模长,然后再分配模长给各个维度(注意最后一个维度需要特殊对待,因为它不能是 0)。见下。
from itertools import combinations, count
def weak_compositions(s, k):
"""
所有 k 元弱组合:a_i >= 0 且 sum a_i = s
stars and bars: 选 k-1 个隔板位置
"""
for bars in combinations(range(s + k - 1), k - 1):
bars = (-1,) + bars + (s + k - 1,)
parts = tuple(bars[i+1] - bars[i] - 1 for i in range(k))
yield parts
def enum_alpha_by_len_l1():
"""
产出有限元组 alpha=(α1,...,αk);其余分量默认 0
顺序:按 t = len + L1 对角线增长
"""
yield () # 全 0,表示 1
for t in count(1): # t = length + L1
for k in range(1, t + 1): # k = length
s = t - k # s = L1
if s < 1:
continue # length>0 时必须 L1>=1
for ak in range(1, s + 1): # ak 即 alpha_k, 强制枚举这一项的大小使其 >0
for prefix in weak_compositions(s - ak, k - 1):
yield prefix + (ak,)
def first_primes(n):
ps = []
x = 2
while len(ps) < n:
ok = True
for p in ps:
if p*p > x: break
if x % p == 0:
ok = False; break
if ok: ps.append(x)
x += 1 if x == 2 else 2
return ps
def alpha_to_int(alpha):
ps = first_primes(len(alpha))
a = 1
for p, e in zip(ps, alpha):
a *= pow(p, e)
return a
问题:如果在计算机中用指数向量表达正整数,我们知道计算整数域乘法只需要对向量进行相加即可,但计算整数域加法将变得异常困难,一般的做法是从指数向量返回正整数域再进行四则运算,再重新计算结果的指数向量(涉及进行质因分解)。如果确实想要在指数向量表达的基础上,大量地计算两个数或者三个数之间的整数域加法,工程上比较现实的方案有哪些?
潜在思路:涉及 cache、多线程并行的方案?展望 gpu 上的并行计算方案?
思路如下。
first_primes() 求出并缓存起来。alpha 和 cofactor 两个值,记为 $a = \lang (\alpha_1, \alpha_2, \cdots, \alpha_B), C \rang$,这就是我们所提的混合表示:
alpha :又称 factors 。用长度为 $B$ 的向量 $(\alpha_1, \alpha_2, \cdots, \alpha_B)$,存储我们维护的质数列表上的指数。
cofactor :一个大整数,表示除了 factors 外的剩余乘项 $C$。显然这个数不能被我们所维护的任何质数整除。
(可选但推荐) value :大整数,表示整个数的数值 $a$,可使用之前提到的 alpha_to_int(alpha) * cofactor 求出。在有需要的时候 lazy 求值,并缓存结果。
$$ a = C \cdot \prod_{i=1}^B \gamma_i^{\alpha_i} $$
(可选但推荐) residue :整数向量,表示整个数在模 $\gamma_1, \gamma_2, \cdots, \gamma_B$ 意义下的数值。在 GPU 上进行向量求余即可。显然总是需要先计算 value ,再从 value 得到 residue 。在有需要的时候 lazy 求值,并缓存结果。
$$ r_i(a) = a \, {\rm mod} \, \gamma_i $$