Rilasciato · in miglioramento
Guida a Divide et impera · 3/6
Per ora questo capitolo è disponibile solo in inglese.
This chapter builds two Python routines, counting inversions with merge sort and fast modular exponentiation, then ports fast power to C++, Java and TypeScript, where overflow needs care.
This version sorts a copy in place with one scratch buffer instead of slicing at every level.
def count_inversions(a):
"""Return (number of pairs i < j with a[i] > a[j], sorted copy of a)."""
buf = list(a)
tmp = [0] * len(buf)
def solve(lo, hi): # half-open range [lo, hi)
if hi - lo <= 1:
return 0
mid = (lo + hi) // 2
count = solve(lo, mid) + solve(mid, hi)
i, j, k = lo, mid, lo
while i < mid and j < hi:
if buf[i] <= buf[j]:
tmp[k] = buf[i]
i += 1
else:
tmp[k] = buf[j]
j += 1
count += mid - i # crossing inversions
k += 1
while i < mid:
tmp[k] = buf[i]
i += 1
k += 1
while j < hi:
tmp[k] = buf[j]
j += 1
k += 1
buf[lo:hi] = tmp[lo:hi]
return count
return solve(0, len(buf)), buf
print(count_inversions([5, 2, 4, 7, 1, 3, 2, 6])) # (14, [1, 2, 2, 3, 4, 5, 6, 7])Line by line:
buf is a copy, so the caller's list is left untouched; tmp is the scratch buffer every merge reuses.solve(lo, hi) works on the half-open range [lo, hi). A range of length 0 or 1 has no inversions.mid splits the range. The two recursive calls count the inversions inside each half and leave both halves sorted.buf[i] <= buf[j] takes from the left on ties, so equal values are never counted.mid - i elements still waiting on the left: those are the crossing inversions.buf[lo:hi] = tmp[lo:hi] writes the merged run back.The recursion depth is about log2 n, far below Python's recursion limit.
def power(base, exp, mod):
"""base^exp % mod for exp >= 0, recursive divide and conquer."""
if exp == 0:
return 1 % mod
half = power(base, exp // 2, mod)
result = half * half % mod
if exp % 2 == 1:
result = result * base % mod
return result
def power_iter(base, exp, mod):
"""The same result without recursion: read the bits of exp."""
result = 1 % mod
base %= mod
while exp > 0:
if exp & 1:
result = result * base % mod
base = base * base % mod
exp >>= 1
return result
print(power(3, 200, 1_000_000_007), power_iter(3, 200, 1_000_000_007)) # 136318165 136318165exp == 0 is the base case. 1 % mod gives 0 when mod == 1, which is the correct answer for any power modulo 1.power(base, exp // 2, mod) is the only recursive call: there is exactly one subproblem.half gives base^(2 * (exp // 2)); an odd exponent needs one more factor of base.% mod after every multiplication keeps the numbers small.The iterative version reads the bits of exp from lowest to highest. base takes the values b^1, b^2, b^4, ... and is multiplied into result whenever the current bit is 1. This loop form is the one usually ported to other languages; in everyday Python, call the built-in pow(base, exp, mod).
Test both on random inputs against a brute-force double loop and the built-in pow.
C++ has no built-in big integers, so the product of two residues must fit in 64 bits: keep mod below 2^32.
#include <cstdint>
#include <iostream>
// Assumes 1 <= mod < 2^32 so that every product fits in 64 bits.
std::uint64_t mod_pow(std::uint64_t base, std::uint64_t exp, std::uint64_t mod) {
std::uint64_t result = 1 % mod;
base %= mod;
while (exp > 0) {
if (exp & 1) result = result * base % mod;
base = base * base % mod;
exp >>= 1;
}
return result;
}
int main() {
std::cout << mod_pow(3, 200, 1000000007) << '\n'; // 136318165
}Java's long is a signed 64-bit integer. With mod below 2^31 (for example 1_000_000_007), every product stays below 2^62. Java's % can return a negative value for a negative base, so it is corrected first.
public final class ModPow {
// Assumes 1 <= mod < 2^31 so that every product fits in a long.
static long modPow(long base, long exp, long mod) {
long result = 1 % mod;
base %= mod;
if (base < 0) base += mod;
while (exp > 0) {
if ((exp & 1) == 1) result = result * base % mod;
base = base * base % mod;
exp >>= 1;
}
return result;
}
public static void main(String[] args) {
System.out.println(modPow(3, 200, 1_000_000_007L)); // 136318165
}
}A JavaScript number is a double and loses precision above 2^53, so even 1_000_000_006 * 1_000_000_006 would come out wrong. bigint keeps the arithmetic exact.
export function modPow(base: bigint, exp: bigint, mod: bigint): bigint {
let result = 1n % mod;
base = ((base % mod) + mod) % mod;
while (exp > 0n) {
if (exp & 1n) result = (result * base) % mod;
base = (base * base) % mod;
exp >>= 1n;
}
return result;
}
console.log(modPow(3n, 200n, 1_000_000_007n)); // 136318165nAll three agree with Python's pow(3, 200, 1_000_000_007).
mid - i whenever the right side wins.mod small enough that products fit in 64 bits; in TypeScript, use bigint.
0 commenti
Accedi · Accedi per lasciare un commento.
Scrivi tu il primo commento.