Overview
(3 Jan 2025) Algorithms S and P are special to me, from when I was in MATH-470.
Algorithm subtraction and division algorithms for bigints, i.e. larger than what the processor can handle.
Algorithm P
Commonly known as the Fisher-Yates shuffle.
for (auto i = vec.size(); i-- > 0;){
auto j = random_integer(i); // Random integer from 0 <= j <= i
std::swap(vec[i], vec[j]);
}
Algorithm S
From Knuth's The Art of Computer Programming, Volume 2: Seminumerical Algorithms 3rd edition, pg. 267.
Given nonnegative integers n place integers (un−1…u1u0)b≥(vn−1…v1v0)b, this algorithm gives their difference (wn−1…w1w0)b.
- (Initialize) Set j=0,k=0.
- (Subtract Digits) Let wj=(uj−vj+k)modb, and k=⌊(uj−vj+k)/b⌋.
- In other words, k∈{−1,0}, depending on whether a borrow occurs, i.e. uj−vj+k<0.
- (Loop on j)
- Increment j.
- If j<n, goto step 2.
- Otherwise, terminate. Ensure that k=0, if k=−1 then the starting conditions for the algorithm were not met, and the results could be wrong.
Algorithm D
From pg. 272.
Given nonnegative integers u=(um+n−1…u1u0)b and v=(vn−1…v1v0)b, where un−1=0 and n>1, we can form the radix-b quotient ⌊u/v⌋=(qmqm−1…q0)b and the remainder umodv=(rn−1…r1r0)b.
- (Normalization)
- Let d=⌊(b−1)/bn−1⌋.
- Depending on the representation/system, it may be preferable to choose d to be a power of 2.
- In any case, an acceptable d would satisfy vn−1≥⌊b/2⌋.
- Then, set (um+num+n−1…u1u0)b equal to d times (um+n−1…u1u0)b.
- If d=1, set um+n=0.
- Similiarly, set (vn−1…u1u0)b equal to d times (vn−1…v1v0)b.
- (Initialize j)
- Let j=m.
- The loop over j, i.e. steps 2 to 7, will essentially do a division of (uj+n…uj+1uj)b by (vn−1…v1v0)b to get a single digit of the quotient.
- (Calculate q^)
- Set q^=⌊(uj+nb+uj+n−1)/vn−1⌋, and let r^=(uj+nb+uj+n−1modvn−1 be the remainder.
- Test if q^=b or q^vn−2>br^+uj+n−2.
- If yes, then decrement q^ and add vn−1 to r^. If r^<b, repeat.
- Otherwise, continue.
- (Mulsub)
- Replace (uj+nuj+n−1…uj)b with (uj+nuj+n−1…uj)b−q^(vn−1…v1v2)b.
- The digits (uj+n,uj+n−1,…,uj) should be kept positive.
- If the result is actually negative, then (uj+nuj+n−1…uj)b should be left as the true value plus bn+1, namely the b's complement of the true value, and the borrow to the left should be remembered.
- (Test Remainder)
- Set qj=q^.
- If the result of step 4 was negative, goto 6, otherwise goto 7.
- (Add Back)
- Decrement qj and add (0vn−1…v1v0)b to (uj+nuj+n−1…uj+1uj)b.
- Note that a carry will occur to the left of uj+n, but it should be ignored because it cancels with the borrow in step 4.
- (Loop on j)
- Decrement j. Goto 3 if j≥0.
- (Unnormalize)
- Now (qm…q1q0)b is the quotient.
- The remainder can be obtained by dividing (un−1…u1u0)b by d.