/*The magic pairs problem: Let SumFact(n) be the sum of factors of n. Find all n1,n2 in a range such that SumFact(n1)-n1-1 == n2 and SumFact(n2)-n2-1 == n1 ----------------------------------------------------- To find SumFact(k), start with prime factorization: k=(p1^n1)(p2^n2) ... (pN^nN) THEN, SumFact(k)=(1+p1+p1^2...p1^n1)*(1+p2+p2^2...p2^n2)* (1+pN+pN^2...pN^nN) PROOF: Do a couple examples -- it's obvious: 48=2^4*3 SumFact(48)=(1+2+4+8+16)*(1+3)=1+2+4+8+16+3+6+12+24+48 75=3*5^2 SumFact(75)=(1+3)*(1+5+25) =1+5+25+3+15+75 Corollary: SumFact(k)=SumFact(p1^n1)*SumFact(p2^n2)*...*SumFact(pN^nN) */ //Primes are needed to sqrt(N). Therefore, we can use U32. class PowPrime { I64 n; I64 sumfact; //Sumfacts for powers of primes are needed beyond sqrt(N) }; class Prime { U32 prime, pow_count; PowPrime *pp; }; I64 *PrimesNew(I64 N, I64 *_sqrt_primes, I64 *_cbrt_primes) { I64 i, j, sqrt = Ceil(Sqrt(N)), cbrt = Ceil(N ` (1 / 3.0)), sqrt_sqrt = Ceil(Sqrt(sqrt)), sqrt_primes = 0, cbrt_primes = 0; U8 *s = CAlloc((sqrt + 1 + 7) / 8); Prime *primes, *p; for (i = 2; i <= sqrt_sqrt; i++) { if (!Bt(s, i)) { j = i * 2; while (j <= sqrt) { Bts(s, j); j += i; } } } for (i = 2; i <= sqrt; i++) if (!Bt(s, i)) { sqrt_primes++; //Count primes if (i <= cbrt) cbrt_primes++; } p = primes = CAlloc(sqrt_primes * sizeof(Prime)); for (i = 2; i <= sqrt; i++) if (!Bt(s, i)) { p->prime = i; p++; } Free(s); *_sqrt_primes = sqrt_primes; *_cbrt_primes = cbrt_primes; return primes; } PowPrime *PowPrimesNew(I64 N, I64 sqrt_primes, Prime *primes, I64 *_num_powprimes) { I64 i, j, k, sf, num_powprimes = 0; Prime *p; PowPrime *powprimes, *pp; p = primes; for (i = 0; i < sqrt_primes; i++) { num_powprimes += Floor(Ln(N) / Ln(p->prime)); p++; } p = primes; pp = powprimes = MAlloc(num_powprimes * sizeof(PowPrime)); for (i = 0; i < sqrt_primes; i++) { p->pp = pp; j = p->prime; k = 1; sf = 1; while (j < N) { sf += j; pp->n = j; pp->sumfact = sf; j *= p->prime; pp++; p->pow_count++; } p++; } *_num_powprimes = num_powprimes; return powprimes; } I64 SumFact(I64 n, I64 sqrt_primes, Prime *p) { I64 i, k, sf = 1; PowPrime *pp; if (n < 2) return 1; for (i = 0; i < sqrt_primes; i++) { k = 0; while (!(n % p->prime)) { n /= p->prime; k++; } if (k) { pp = p->pp + (k - 1); sf *= pp->sumfact; if (n == 1) return sf; } p++; } return sf * (1 + n); //Prime } Bool TestSumFact(I64 n, I64 target_sf, I64 sqrt_primes, I64 cbrt_primes, Prime *p) { I64 i = 0, k, b, x1, x2; PowPrime *pp; F64 disc; if (n < 2) return FALSE; while (i++ < cbrt_primes) { k = 0; while (!(n % p->prime)) { n /= p->prime; k++; } if (k) { pp = p->pp + (k - 1); if (ModU64(&target_sf, pp->sumfact)) return FALSE; if (n == 1) { if (target_sf == 1) return TRUE; else return FALSE; } } p++; } /* At this point we have three possible cases to test 1)n==p1 ->sf==(1+p1) ? 2)n==p1*p1 ->sf==(1+p1+p1^2) ? 3)n==p1*p2 ->sf==(p1+1)*(p2+1) ? */ if (1 + n == target_sf) { while (i++ < sqrt_primes) { k = 0; while (!(n % p->prime)) { n /= p->prime; k++; } if (k) { pp = p->pp + (k - 1); if (ModU64(&target_sf, pp->sumfact)) return FALSE; if (n == 1) { if (target_sf == 1) return TRUE; else return FALSE; } } p++; } if (1 + n == target_sf) return TRUE; else return FALSE; } k =Sqrt(n); if (k * k == n) { if (1 + k + n == target_sf) return TRUE; else return FALSE; } else { // n==p1*p2 -> sf==(p1+1)*(p2+1) ? where p1 != 1 && p2 != 1 // if p1==1 || p2==1, it is FALSE because we checked a single prime above. // sf==(p1+1)*(n/p1+1) // sf==n+p1+n/p1+1 // sf*p1==n*p1+p1^2+n+p1 // p1^2+(n+1-sf)*p1+n=0 // x=(-b+/-sqrt(b^2-4ac))/2a // a=1 // x=(-b+/-sqrt(b^2-4c))/2 // b=n+1-sf;c=n b = n + 1 - target_sf; // x=(-b+/-sqrt(b^2-4n))/2 disc = b * b - 4 * n; if (disc < 0) return FALSE; x1 = (-b - Sqrt(disc)) / 2; if (x1 <= 1) return FALSE; x2 = n / x1; if (x2 > 1 && x1 * x2 == n) return TRUE; else return FALSE; } } U0 PutFactors(I64 n) //For debugging { I64 i, k, sqrt = Ceil(Sqrt(n)); for (i = 2; i <= sqrt; i++) { k = 0; while (!(n % i)) { k++; n /= i; } if (k) { "%d", i; if (k > 1) "^%d", k; '' CH_SPACE; } } if (n != 1) "%d ", n; } class RangeJob { CDoc *doc; I64 num, lo, hi, N, sqrt_primes, cbrt_primes; Prime *primes; CJob *cmd; } rj[mp_count]; I64 TestCoreSubRange(RangeJob *r) { I64 i, j, m, n, n2, sf, res = 0, range = r->hi - r->lo, *sumfacts = MAlloc(range * sizeof(I64)), *residue = MAlloc(range * sizeof(I64)); U16 *pow_count = MAlloc(range * sizeof(U16)); Prime *p = r->primes; PowPrime *pp; MemSetI64(sumfacts, 1, range); for (n = r->lo; n < r->hi; n++) residue[n - r->lo] = n; for (j = 0; j sqrt_primes; j++) { MemSet(pow_count, 0, range * sizeof(U16)); m = 1; for (i = 0; i < p->pow_count; i++) { m *= p->prime; n = m - r->lo % m; while (n < range) { pow_count[n]++; n += m; } } for (n = 0; n < range; n++) if (i = pow_count[n]) { pp = &p->pp[i - 1]; sumfacts[n] *= pp->sumfact; residue [n] /= pp->n; } p++; } for (n = 0; n < range; n++) if (residue[n] != 1) sumfacts[n] *= 1 + residue[n]; for (n = r->lo; n < r->hi; n++) { sf = sumfacts[n - r->lo]; n2 = sf - n - 1; if (n < n2 < r->N) { if (r->lo <= n2hi && sumfacts[n2 - r->lo] - n2 - 1 == n || TestSumFact(n2, sf, r->sqrt_primes, r->cbrt_primes, r->primes)) { DocPrint(r->doc, "%u:%u\n", n, sf - n - 1); res++; } } } Free(pow_count); Free(residue); Free(sumfacts); return res; } #define CORE_SUB_RANGE 0x1000 I64 TestCoreRange(RangeJob *r) { I64 i, n, res = 0; RangeJob rj; MemCopy(&rj, r, sizeof(RangeJob)); for (i = r->lo; i < r->hi; i += CORE_SUB_RANGE) { rj.lo = i; rj.hi = i + CORE_SUB_RANGE; if (rj.hi > r->hi) rj.hi = r->hi; res += TestCoreSubRange(&rj); n = rj.hi - rj.lo; lock {progress1 += n;} Yield; } return res; } I64 MagicPairs(I64 N) { F64 t0 = tS; I64 res = 0; I64 sqrt_primes, cbrt_primes, num_powprimes, i, k, n = (N - 1) / mp_count + 1; Prime *primes = PrimesNew(N, &sqrt_primes, &cbrt_primes); PowPrime *powprimes = PowPrimesNew(N, sqrt_primes, primes, &num_powprimes); "N:%u SqrtPrimes:%u CbrtPrimes:%u PowersOfPrimes:%u\n", N, sqrt_primes, cbrt_primes, num_powprimes; progress1 = 0; *progress1_desc = 0; progress1_max = N; k = 2; for (i = 0; i < mp_count; i++) { rj[i].doc = DocPut; rj[i].num = i; rj[i].lo = k; k += n; if (k > N) k = N; rj[i].hi = k; rj[i].N = N; rj[i].sqrt_primes = sqrt_primes; rj[i].cbrt_primes = cbrt_primes; rj[i].primes = primes; rj[i].cmd = JobQueue(&TestCoreRange, &rj[i], mp_count - 1 - i, 0); } for (i = 0; i < mp_count; i++) res += JobResGet(rj[i].cmd); Free(powprimes); Free(primes); "Found:%u Time:%9.4f\n", res, tS - t0; progress1 = progress1_max = 0; return res; } MagicPairs(1000000);