iki temel gerçekleri istismar ederek çalışmalarını önemli ölçüde azaltabilir: Biz sadece bu son döngü değiştirmek zorunda p
asal, üs ise
n!
'un ana faktörü olarak p
, s_p(n)
tarafından p
temsili (p = 2
, bu popcount için) gösterimde s_p(n)
göstergesinin toplamıdır. Dolayısıyla, choose(n,k)
'un ana faktörizasyonunda p
'un üssü, olup, özellikle k + (n-k)
ilavesinin, p
bazında gerçekleştirildiği zaman taşınmaması durumunda sıfırdır (üs, taşıma sayısıdır).
Wilson'un teoremi
:p
ancak ve ancak (p-1)! ≡ (-1) (mod p)
bile, bir asal olduğunu.
n!
ait factorisation içinde p
üs genellikle
long long factorial_exponent(long long n, long long p)
{
long long ex = 0;
do
{
n /= p;
ex += n;
}while(n > 0);
return ex;
}
p
tarafından choose(n,k)
ait bölünebilme onay kesinlikle gerekli olmadığı hesaplanır, ancak bunun beri, öncelikle bu olması mantıklı olduğunu genellikle olacak ve daha az iş olacak:
long long choose_mod(long long n, long long k, long long p)
{
// We deal with the trivial cases first
if (k < 0 || n < k) return 0;
if (k == 0 || k == n) return 1;
// Now check whether choose(n,k) is divisible by p
if (factorial_exponent(n) > factorial_exponent(k) + factorial_exponent(n-k)) return 0;
// If it's not divisible, do the generic work
return choose_mod_one(n,k,p);
}
Şimdi daha yakın n!
numaralı telefondan. ≤ n
numaralarını p
'un katları vearasındaki coprime sayılarına ayırırız.
n = q*p + r, 0 ≤ r < p
ile p
katları p^q * q!
katkıda bulunur. p
numaralı rakamlar, 0 ≤ j < q
için (j*p + k), 1 ≤ k < p
ürününe ve (q*p + k), 1 ≤ k ≤ r
ürününe katkıda bulunur.
Sayılarla ilgili olarak p
numaralı kurabiyeler için p
katkısı ile ilgileneceğiz. j*p + k, 1 ≤ k < p
tam çalışmalarının her biri (p-1)!
modulo p
ile uyumludur, dolayısıyla hepsi (-1)^q
modulo p
'un bir katkısını üretirler. Son (muhtemelen) tamamlanmamış çalışma, r!
modulo p
üretir.
yüzden
n = a*p + A
k = b*p + B
n-k = c*p + C
biz
cop(m,r)
tüm sayıların ürün
≤ m*p + r
olan
p
için aralarında asal olduğu
choose(n,k) = p^a * a!/ (p^b * b! * p^c * c!) * cop(a,A)/(cop(b,B) * cop(c,C))
olsun yazarsanız.
İki olasılık vardır, a = b + c
ve A = B + C
veya a = b + c + 1
ve A = B + C - p
. Hesaplamada, ikinci olasılığı önceden elimine ettik, fakat bu gerekli değildir. choose(a,b)
gelen İlk durumda
,
p
açık güçler iptal ve biz
choose(n,k) = a!/(b! * c!) * cop(a,A)/(cop(b,B) * cop(c,C))
= choose(a,b) * cop(a,A)/(cop(b,B) * cop(c,C))
ile choose(n,k)
bölen p
Herhangi yetkilerini bırakılır - bizim durumumuzda, orada yok olacak, biz yapmaya başladığımdan beri önce bu durumları ortadan ettik - cop(a,A)/(cop(b,B) * cop(c,C))
bir tamsayı olması gerekmez, ancak ve (-1)
iptal ifade modulo p
dikkate alındığında, cop(m,r)
a = b + c
beri (-1)^m * r!
için azaltır, böylece (örneğin choose(19,9) (mod 5)
düşünün) ve bizkalır İkinci durumda
choose(n,k) ≡ choose(a,b) * choose(A,B) (mod p)
, biz
a = b + c + 1
beri
choose(n,k) = choose(a,b) * p * cop(a,A)/ (cop(b,B) * cop(c,C))
bulabilirsiniz. son hanede bir taşıma, böylece payıdır anlamı, biz modüler ters tarafından bir çarpma ile bölünme yerini alacak şekilde kullanılabilir burada (p
p * cop(a,A)/(cop(b,B) * cop(c,C)) ≡ 0 = choose(A,B)
modülo veya rasyonel bir sayı bağdaşım olarak görüntülemek A < B
demektir p
tarafından bölünebilir. Neyse, biz yine
choose(n,k) ≡ choose(a,b) * choose(A,B) (mod p)
Şimdi
choose(a,b)
bölümü için tekrarlayabilir bulabilirsiniz.
Örnek:
choose(144,6) (mod 5)
144 = 28 * 5 + 4
6 = 1 * 5 + 1
choose(144,6) ≡ choose(28,1) * choose(4,1) (mod 5)
≡ choose(3,1) * choose(4,1) (mod 5)
≡ 3 * 4 = 12 ≡ 2 (mod 5)
choose(12349,789) ≡ choose(2469,157) * choose(4,4)
≡ choose(493,31) * choose(4,2) * choose(4,4
≡ choose(98,6) * choose(3,1) * choose(4,2) * choose(4,4)
≡ choose(19,1) * choose(3,1) * choose(3,1) * choose(4,2) * choose(4,4)
≡ 4 * 3 * 3 * 1 * 1 = 36 ≡ 1 (mod 5)
Şimdi uygulaması:
// Preconditions: 0 <= k <= n; p > 1 prime
long long choose_mod_one(long long n, long long k, long long p)
{
// For small k, no recursion is necessary
if (k < p) return choose_mod_two(n,k,p);
long long q_n, r_n, q_k, r_k, choose;
q_n = n/p;
r_n = n % p;
q_k = k/p;
r_k = k % p;
choose = choose_mod_two(r_n, r_k, p);
// If the exponent of p in choose(n,k) isn't determined to be 0
// before the calculation gets serious, short-cut here:
/* if (choose == 0) return 0; */
choose *= choose_mod_one(q_n, q_k, p);
return choose % p;
}
// Preconditions: 0 <= k <= min(n,p-1); p > 1 prime
long long choose_mod_two(long long n, long long k, long long p)
{
// reduce n modulo p
n %= p;
// Trivial checks
if (n < k) return 0;
if (k == 0 || k == n) return 1;
// Now 0 < k < n, save a bit of work if k > n/2
if (k > n/2) k = n-k;
// calculate numerator and denominator modulo p
long long num = n, den = 1;
for(n = n-1; k > 1; --n, --k)
{
num = (num * n) % p;
den = (den * k) % p;
}
// Invert denominator modulo p
den = invert_mod(den,p);
return (num * den) % p;
}
modüler tersini hesaplamak için kullanabileceğiniz Fermat (sözde küçük) teoremi
ise p
asal ve a
bölünemez değil b y p
, daha sonra a^(p-1) ≡ 1 (mod p)
.
ve a^(p-2) (mod p)
olarak tersini hesaplamak ya da sen göreceli asal (pozitif) herhangi bir tamsayı çifti için modüler ters elde bağımsız değişkenler daha geniş bir aralığı genişletilmiş Öklid algoritması ya devam fraksiyonu genişleme için geçerli bir yöntemi kullanmak: a^(p-2) (mod p)
hesaplama gibi
long long invert_mod(long long k, long long m)
{
if (m == 0) return (k == 1 || k == -1) ? k : 0;
if (m < 0) m = -m;
k %= m;
if (k < 0) k += m;
int neg = 1;
long long p1 = 1, p2 = 0, k1 = k, m1 = m, q, r, temp;
while(k1 > 0) {
q = m1/k1;
r = m1 % k1;
temp = q*p1 + p2;
p2 = p1;
p1 = temp;
m1 = k1;
k1 = r;
neg = !neg;
}
return neg ? m - p2 : p2;
}
, bu bazı girişler için bu diğerleri için daha yavaş olduğunu, (bu oldukça hızlıdır küçük k
ve büyük p
için bu yüzden, aslında O(min(log k, log p))
var) önemli ölçüde daha hızlı olduğunu, bir O(log p)
algoritmadır.
Genel olarak, binom katsayıları her binom katsayısı O (p * log_p k) olmak üzere toplam karmaşıklığını verimli, en O (p) işlemleri anda ihtiyacı p
, modulo en O (log_p k) de hesaplamak gerekir bu şekilde operasyonları . k
, p
'dan önemli ölçüde daha büyük olduğunda, bu O(k)
çözümden çok daha iyidir. k <= p
için, bazı ek yükü olan O(k)
çözümüne indirgenir.
Eğer kesin hatırlatma bilmek gerekir yoksa numara p tarafından * eşit * bölünebilir olup olmadığını yeterince bilmektir musunuz? (n k modunu seç p == 0) – vidstige
Soruyu anladığımdan emin değilim. N k mod p seçiminin cevabı kesin/doğru olmalıdır. –
Kombinasyonlar işlevi ne işe yarar (neden 3 argüman alır) –