LAMMP 4.2.0
Lamina High-Precision Arithmetic Library
载入中...
搜索中...
未找到
nCr.c
浏览该文件的文档.
1/**
2 * Copyright (C) 2026 HJimmyK(Jericho Knox)
3 *
4 * This file is part of LAMMP.
5 *
6 * LAMMP is free software: you can redistribute it and/or modify it under
7 * the terms of the GNU Lesser General Public License (LGPL) as published
8 * by the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed WITHOUT ANY WARRANTY.
12 *
13 * See <https://www.gnu.org/licenses/>.
14 */
15
16#include "../../../include/lammp/impl/ele_mul.h"
17#include "../../../include/lammp/impl/inlines.h"
18#include "../../../include/lammp/impl/lglg.h"
19#include "../../../include/lammp/impl/longlong.h"
20#include "../../../include/lammp/impl/mparam.h"
21#include "../../../include/lammp/impl/prime_table.h"
22#include "../../../include/lammp/numth.h"
23
24
26 lmmp_param_assert(r <= n / 2);
29 if (r < 4 || n < ODD_FACTORIAL_SIZE) {
30 rn = 3;
31 } else if (n == MP_UINT_MAX) {
32 uint mean = n - r / 2 + 1;
33 uint64_t l1, l2;
35 l2 = log2_fac_floor(r);
36 l2 /= LIMB_BITS;
37 rn = l1 - l2;
38 } else {
39 uint64_t l1, l2, l3;
41 l2 = log2_fac_floor(r);
42 if (n - r < ODD_FACTORIAL_SIZE)
43 l3 = 0;
44 else
45 l3 = log2_fac_floor(n - r);
46 rn = l1 - l2 - l3;
47 rn = (rn + LIMB_BITS - 1) / LIMB_BITS;
48 }
49 (*bits) = n - lmmp_limb_popcnt_(n);
50 (*bits) -= r - lmmp_limb_popcnt_(r);
51 (*bits) -= n - r - lmmp_limb_popcnt_(n - r);
52 return rn + 2; // more 2 limb
53}
54
55// 无分支,尽管可能导致溢出
56#define mul_1(dst, rn, v) \
57 do { \
58 mp_limb_t _c_ = lmmp_mul_1_(dst, dst, rn, v); \
59 dst[rn] = _c_; \
60 rn += _c_ > 0; \
61 } while (0)
62
63#define div_1(dst, rn, v) \
64 do { \
65 mp_limb_t _dinv_ = lmmp_binvert_ulong_(v); \
66 lmmp_divexact_1_(dst, dst, rn, v, _dinv_); \
67 rn -= dst[rn - 1] == 0; \
68 } while (0)
69
71 /*
72 我们可以知道,nCr的大小一定不会超过B^rn,因此,B^rn的可以含有的质因数数量即为nCr可以含有的质因数数量的上限。
73 同时,我们这里只计算的是奇数部分,比如我们可以用B^rn可以含有的3的质因数个数来估计nCr的质因数种类数,
74 这是一个绝对上界,同时在不平衡时比pi(n)这个平方上界要紧得多。当然即使是这个上界,实际的质因数个数也可能远远
75 小于这个上界。一个改进想法是,我们使用更大一点的质数,对于n>0xffff,我们选取这个质数为251,
76 而log(B)/log(251)约等于8.02855802854906,我们近似视为8,这也是这里的常数的由来,当然,此估计可能存在低估,
77 但是经过大量的校验,我们未发现任何低估的反例。
78 为了同时处理不平衡与不平衡的情况,我们这里对两个估计进行比较,取较小的一个作为最终结果。不平衡时,approx1要更紧一些。
79 */
80 // 此处假定了LIMB_BITS为64
81 ulong approx1 = rn * 8;
84 return approx1 < approx2 ? approx1 : approx2;
85}
86
88 /*
89 经过大量的校验,*8即使是在ushort输入下,也未见低估,但是为了留有冗余,我们还是选择*10,大致相当于质数83。
90 */
91 // 此处假定了LIMB_BITS为64
92 uint approx1 = rn * 10;
94 return (ushort)approx1;
95}
96
98 uint pn = n;
99 uint e = 0;
100 ulong inv = MP_ULONG_MAX / p + 1;
101 while (pn > 0) {
103 e += pn;
104 }
105 pn = r;
106 while (pn > 0) {
108 e -= pn;
109 }
110 pn = nr;
111 while (pn > 0) {
113 e -= pn;
114 }
115 if (e > 0) {
116 fac[nfactors].f = p;
117 fac[nfactors++].j = e;
118 }
119 return nfactors;
120}
121
122/**
123 * @brief 使用累乘函数计算nPr(奇数部分)
124 */
126 TEMP_DECL;
127 ulongp restrict limbs = TALLOC_TYPE(r / 2 + 1, ulong);
128 mp_size_t limbn = 0;
129 ulong t = 1;
130 uint v;
131 mp_bitcnt_t cnt = 0;
132 for (uint i = n - r + 1; i <= n; ++i) {
133 ctz_shr_u32(v, i, cnt);
134 t *= v;
135 if (t > MP_UINT_MAX) {
136 limbs[limbn++] = t;
137 t = 1;
138 }
139 }
140 if (t != 1)
141 limbs[limbn++] = t;
142
143 if (rn >= limbn) {
146 } else {
149 lmmp_copy(dst, tp, rn);
150 }
151 TEMP_FREE;
152 return rn;
153}
154
156 if (n <= NPR_SHORT_LIMIT)
157 return lmmp_odd_nPr_ushort_(dst, rn, n, n);
158 else
160}
161
162typedef struct {
163 mp_size_t nPr_n; // nPr的limb数量
164 mp_size_t fac_n; // r! 的limb数量
165 mp_bitcnt_t nPr_bits; // nPr的bit数量
166 mp_bitcnt_t fac_bits; // r! 的bit数量
170
172 lmmp_param_assert(rn > 0 && dst != NULL);
173 TEMP_DECL;
174
176 mp_size_t shw1 = ctx->nPr_bits / LIMB_BITS;
177 mp_size_t nPr_n = lmmp_odd_nPr_product_(nPr, ctx->nPr_n - shw1, ctx->n, ctx->r);
178
179 mp_size_t shw2 = ctx->fac_bits / LIMB_BITS;
181 mp_size_t fac_n = lmmp_odd_factorial_(fac, ctx->fac_n - shw2, ctx->r);
182
183 lmmp_debug_assert(rn >= nPr_n - fac_n + 1);
184 lmmp_divexact_(dst, nPr, nPr_n, fac, fac_n);
185 rn = nPr_n - fac_n + 1;
186 rn -= dst[rn - 1] == 0;
187 TEMP_FREE;
188 return rn;
189}
190
193 lmmp_param_assert(rn > 0 && dst != NULL);
194 lmmp_param_assert(r <= n / 2);
195 if (r < ODD_FACTORIAL_SIZE) {
197 mp_limb_t t = 0;
198 lmmp_odd_nPr_ushort_(&t, 1, r, r);
199 div_1(dst, rn, t);
200 return rn;
201 } else if (rn < BINOMIAL_RN_BASECASE_THRESHOLD) {
202 if (r <= 4 || n > 0xfff) {
203 dst[0] = 1;
204 rn = 1;
205 ulong t, v;
207 for (ulong i = 1; i <= r; ++i) {
208 t = n - i + 1;
209 ctz_shr_u64(v, t, cnt);
210 mul_1(dst, rn, v);
211 ctz_shr_u64(v, i, cnt);
212 div_1(dst, rn, v);
213 }
214 return rn;
215 } else {
216 dst[0] = 1;
217 rn = 1;
218 ulong i = 1;
219 ulong t = 1, d = 1, v;
221 for (; i <= (ulong)r - 4; i += 4) {
222 // 相邻四个数必含有3这个公共因子
223 d = i * (i + 1) * (i + 2) * (i + 3);
224 d /= 3;
225 t = (n - i + 1) * (n - i) * (n - i - 1) * (n - i - 2);
226 t /= 3;
227 ctz_shr_u64(v, t, cnt);
228 mul_1(dst, rn, v);
229 ctz_shr_u64(v, d, cnt);
230 div_1(dst, rn, v);
231 }
232 for (; i <= r; ++i) {
233 t = n - i + 1;
234 ctz_shr_u64(v, t, cnt);
235 mul_1(dst, rn, v);
236 ctz_shr_u64(v, i, cnt);
237 div_1(dst, rn, v);
238 }
239 return rn;
240 }
241 } else {
242 TEMP_DECL;
247 ushort nr = n - r;
248 nfactors = 0;
249 for (ushort i = 1; i < primen; ++i) {
252 }
253
255
256 TEMP_FREE;
257 return rn;
258 }
259}
260
262 lmmp_param_assert(r <= (n / 2));
263 lmmp_param_assert(rn > 0 && dst != NULL);
264 if (r <= 3 || (n > 0xfffffff && rn < BINOMIAL_RN_BASECASE_THRESHOLD)) {
265 dst[0] = 1;
266 rn = 1;
267 ulong t, v;
269 for (ulong i = 1; i <= r; ++i) {
270 t = n - i + 1;
271 ctz_shr_u64(v, t, cnt);
272 mul_1(dst, rn, v);
273 ctz_shr_u64(v, i, cnt);
274 div_1(dst, rn, v);
275 }
276 return rn;
277 } else if (rn < BINOMIAL_RN_BASECASE_THRESHOLD) {
278 dst[0] = 1;
279 rn = 1;
280 ulong i = 1;
281 ulong t = 1, d = 1, v;
283 for (; i <= (ulong)r - 2; i += 2) {
284 d = i * (i + 1);
285 t = (n - i + 1) * (n - i);
286 ctz_shr_u64(v, t, cnt);
287 mul_1(dst, rn, v);
288 ctz_shr_u64(v, d, cnt);
289 div_1(dst, rn, v);
290 }
291 for (; i <= r; ++i) {
292 t = n - i + 1;
293 ctz_shr_u64(v, t, cnt);
294 mul_1(dst, rn, v);
295 ctz_shr_u64(v, i, cnt);
296 div_1(dst, rn, v);
297 }
298 return rn;
299 } else {
301 ctx.nPr_n = lmmp_nPr_size_(n, r, &ctx.nPr_bits);
302 ctx.fac_n = lmmp_factorial_size_(r, &ctx.fac_bits);
303 if (50 * ctx.nPr_n > 89 * ctx.fac_n) {
304 /* 这个调优值是在近似忽略了质数表的初始化开销,主要瓶颈集中在质数表的遍历的情况下测得的 */
305 /* 因此,当质数表未初始化时,这个调优值将无法代表真实性能边界 */
306 ctx.n = n;
307 ctx.r = r;
308 return lmmp_odd_nCr_div_(dst, rn, &ctx);
309 } else {
314 uint nr = n - r;
315
316 nfactors = 0;
319 while (cache.is_end == 0) {
321 for (uint i = 0; i < cache.size; ++i) {
323 }
324 }
326
328
330 return rn;
331 }
332 }
333}
334
336 lmmp_param_assert(r <= (n / 2));
340
341 bits %= LIMB_BITS;
342 lmmp_zero(dst, shw);
343
344 if (n <= NCR_SHORT_LIMIT)
345 rn = lmmp_odd_nCr_ushort_(dst + shw, rn - shw, n, r);
346 else
347 rn = lmmp_odd_nCr_uint_(dst + shw, rn - shw, n, r);
348
349 if (bits > 0) {
350 dst[shw + rn] = lmmp_shl_(dst + shw, dst + shw, rn, bits);
351 rn += shw + 1;
352 rn -= dst[rn - 1] == 0;
353 } else {
354 rn += shw;
355 }
356 return rn;
357}
mp_size_t lmmp_elem_mul_ulong_(mp_ptr dst, const ulongp limbs, mp_size_t n, mp_ptr tp)
计算limbs数组的累乘积
mp_size_t lmmp_factors_mul_ushort_(mp_ptr dst, mp_size_t rn, fac_ptr fac, ushort nfactors)
计算因子的累乘,并将结果放入dst中
mp_size_t lmmp_factors_mul_(mp_ptr dst, mp_size_t rn, fac_ptr fac, uint nfactors)
计算因子的累乘,并将结果放入dst中
#define lmmp_limb_popcnt_
Definition inlines.h:163
static uint64_t log2_fac_ceil(uint32_t n)
计算 log2(n!)的ceil值
Definition lglg.h:241
static uint64_t log2_fac_floor(uint32_t n)
计算 log2(n!)的floor值
Definition lglg.h:260
mp_limb_t * mp_ptr
Definition lmmp.h:80
#define lmmp_copy(dst, src, n)
Definition lmmp.h:367
#define lmmp_zero(dst, n)
Definition lmmp.h:369
size_t mp_bitcnt_t
Definition lmmp.h:82
uint64_t mp_size_t
Definition lmmp.h:77
#define lmmp_debug_assert(x)
Definition lmmp.h:390
uint64_t mp_limb_t
Definition lmmp.h:76
#define LIMB_BITS
Definition lmmp.h:86
#define lmmp_param_assert(x)
Definition lmmp.h:401
mp_limb_t lmmp_shl_(mp_ptr dst, mp_srcptr numa, mp_size_t na, mp_size_t shl)
大数左移操作 [dst,na] = [numa,na]<<shl,dst的低shl位填充0
Definition shl.c:19
#define _udiv32by32_q_preinv(q, n0, dinv)
Definition longlong.h:457
#define ctz_shr_u64(r, x, cnt)
Copyright (C) 2026 HJimmyK(Jericho Knox)
Definition longlong.h:74
#define ctz_shr_u32(r, x, cnt)
Definition longlong.h:147
#define NPR_SHORT_LIMIT
Definition mparam.h:151
#define BINOMIAL_RN_BASECASE_THRESHOLD
Definition mparam.h:116
#define MP_UINT_MAX
Definition mparam.h:136
#define MP_USHORT_MAX
Definition mparam.h:135
#define MP_ULONG_MAX
Definition mparam.h:137
#define NCR_SHORT_LIMIT
Definition mparam.h:154
#define ODD_FACTORIAL_SIZE
Definition mparam.h:149
#define t
#define tp
#define n
mp_size_t lmmp_odd_nCr_ushort_(mp_ptr restrict dst, mp_size_t rn, uint n, uint r)
Definition nCr.c:191
uint r
Definition nCr.c:168
#define mul_1(dst, rn, v)
Definition nCr.c:56
mp_bitcnt_t nPr_bits
Definition nCr.c:165
static uint factor_size_int(mp_size_t rn, uint n)
Definition nCr.c:70
static mp_size_t lmmp_odd_nCr_div_(mp_ptr restrict dst, mp_size_t rn, bino_choose_t *restrict ctx)
Definition nCr.c:171
static mp_size_t lmmp_odd_nPr_product_(mp_ptr restrict dst, mp_size_t rn, uint n, uint r)
使用累乘函数计算nPr(奇数部分)
Definition nCr.c:125
#define div_1(dst, rn, v)
Definition nCr.c:63
mp_size_t fac_n
Definition nCr.c:164
mp_size_t lmmp_nCr_size_(uint n, uint r, mp_bitcnt_t *restrict bits)
Copyright (C) 2026 HJimmyK(Jericho Knox)
Definition nCr.c:25
static ushort factor_size_short(mp_size_t rn)
Definition nCr.c:87
mp_size_t nPr_n
Definition nCr.c:163
static uint count_factors(fac_ptr fac, uint nfactors, uint n, uint r, uint nr, uint p)
Definition nCr.c:97
uint n
Definition nCr.c:167
static mp_size_t lmmp_odd_factorial_(mp_ptr restrict dst, mp_size_t rn, uint n)
Definition nCr.c:155
mp_size_t lmmp_odd_nCr_uint_(mp_ptr restrict dst, mp_size_t rn, uint n, uint r)
Definition nCr.c:261
mp_bitcnt_t fac_bits
Definition nCr.c:166
mp_size_t lmmp_nCr_(mp_ptr restrict dst, mp_bitcnt_t bits, mp_size_t rn, uint n, uint r)
Definition nCr.c:335
mp_size_t lmmp_odd_factorial_uint_(mp_ptr dst, mp_size_t rn, uint n)
计算 n! 阶乘的奇数部分
mp_size_t lmmp_pow_1_size_(mp_limb_t base, ulong exp)
计算幂次方需要的limb缓冲区长度 base ^ exp
Definition pow.c:25
void lmmp_divexact_(mp_ptr dst, mp_srcptr np, mp_size_t nn, mp_srcptr dp, mp_size_t dn)
精确除法([dst,nn]=[np,nn]/[dp,dn],且余数必须为0)
mp_size_t lmmp_odd_nPr_ushort_(mp_ptr dst, mp_size_t rn, ulong n, ulong r)
计算 nPr 排列数的奇数部分
uint64_t * ulongp
Definition numth.h:41
uint32_t uint
Definition numth.h:31
mp_size_t lmmp_factorial_size_(uint n, mp_bitcnt_t *bits)
计算 n! 阶乘的 limb 缓冲区长度
uint16_t ushort
Definition numth.h:29
mp_size_t lmmp_nPr_size_(ulong n, ulong r, mp_bitcnt_t *bits)
计算 nPr 排列数的 limb 缓冲区长度
uint64_t ulong
Definition numth.h:32
void lmmp_prime_cache_free_(prime_cache_t *cache)
释放素数表缓存
void lmmp_prime_cache_next_(prime_cache_t *cache)
素数表缓存更新(从小到大遍历全局质数表)
static ulong lmmp_prime_size_(ulong n)
估计 n 范围内的素数数量
Definition prime_table.h:57
const ushort prime_short_table[6542]
void lmmp_prime_int_table_init_(uint n)
初始化全局素数表
Definition prime_table.c:99
ushort lmmp_prime_cnt16_(ushort n)
计算小于等于 n 的素数数量
void lmmp_prime_cache_init_(prime_cache_t *cache, uint n)
初始化素数表缓存
#define TEMP_DECL
Definition tmp_alloc.h:131
#define TEMP_FREE
Definition tmp_alloc.h:150
#define TALLOC_TYPE(n, type)
Definition tmp_alloc.h:148
#define TEMP_B_DECL
Definition tmp_alloc.h:132
#define BALLOC_TYPE(n, type)
Definition tmp_alloc.h:146
#define TEMP_B_FREE
Definition tmp_alloc.h:159