{ }codelper

Learn › JavaScript

Algorithms · 30

GCD, primes and number bases

Find the GCD with remainders, sift out primes with a sieve, and write a number in another base.

These are three tools that keep turning up in number problems.
The example below finds the greatest common divisor of 84 and 36, prints every prime up to 30, and writes 255 in base 16.

const DIGITS = "0123456789abcdefghijklmnopqrstuvwxyz"; function gcd(a, b) {  while (b !== 0) {    [a, b] = [b, a % b]; // (a, b)를 (b, a를 b로 나눈 나머지)로 바꾼다  }  return a;} function toBase(x, base) {  if (x === 0) return "0";  let s = "";  while (x > 0) {    s = DIGITS[x % base] + s; // 나머지는 낮은 자리부터 나오므로 앞에 붙인다    x = Math.floor(x / base);  }  return s;} const [a, b, n, x, base] = require("fs").readFileSync(0, "utf8").trim().split(/\s+/).map(Number); console.log(gcd(a, b)); // 체: 소수를 찾을 때마다 그 배수를 지운다. 끝까지 안 지워진 수가 소수const isPrime = new Array(n + 1).fill(true);isPrime[0] = isPrime[1] = false;for (let i = 2; i * i <= n; i++) {  if (isPrime[i]) {    for (let j = i * i; j <= n; j += i) isPrime[j] = false;  }}const primes = [];for (let i = 2; i <= n; i++) if (isPrime[i]) primes.push(i);console.log(primes.join(" ")); console.log(toBase(x, base));
stdin
84 36
30
255 16
stdout
12
2 3 5 7 11 13 17 19 23 29
ff

Key points

  • The greatest common divisor (GCD) is the largest number that divides both numbers.
    Keep replacing (a, b) with (b, a % b) until b is 0, and the a that is left is the answer.
    (84, 36) → (36, 12) → (12, 0), so it is 12.
  • This is correct because any number that divides both a and b also divides both b and a % b, and the other way round.
    The numbers shrink quickly: even with the slowest kind of input, two numbers close to a trillion finished in 57 laps.
  • A prime is a number greater than 1 that only 1 and itself divide.
    To find every prime up to n, use a sieve.
    Walk up from 2; a number that has not been crossed out yet is prime, and you cross out all of its multiples.
  • Start crossing out at i * i.
    Smaller multiples were already crossed out by a smaller prime.
    For the same reason the outer loop only runs while i * i <= n.
  • To write a number in another base, write down the remainder of dividing by the base, replace the number with the quotient, and repeat until it is 0.
    The remainders come out lowest digit first, so put each one in front.
    255 gives remainder 15 (f) and quotient 15, then remainder 15 (f) and quotient 0, so it is ff.
  • The example does not do it, but to read such a string back as a number, go from the first character and repeat x = x * base + digit.
    ff goes 0 → 15 → 15 × 16 + 15 = 255.
    Digits of 10 and above are written as letters, starting from a (10).

How each language writes it

PythonThere is math.gcd(a, b).
Write in base 2, 8 or 16 with format(255, "b"), "o", "x" (→ ff); read with int("ff", 16).
For other bases, write it yourself as in the example.
The quotient is //
JavaScriptThere is no GCD function, so write your own.
Write with (255).toString(16) (→ ff), read with parseInt("ff", 16).
The quotient is Math.floor(x / base)
TypeScriptSame as JavaScript
JavaThere is no GCD function for int, so write your own (BigInteger does have gcd).
Write with Integer.toString(255, 16), read with Integer.parseInt("ff", 16)
C#There is BigInteger.GreatestCommonDivisor(a, b) (System.Numerics), but for int you can simply write your own.
Convert.ToString(255, 16) and Convert.ToInt32("ff", 16) accept only bases 2, 8, 10 and 16 (3 throws ArgumentException)
C++std::gcd(a, b) in <numeric>.
Read with std::stoi("ff", nullptr, 16); write it yourself as in the example.
The sieve is a std::vector<bool>
CThere is no GCD function, so write your own.
Read with strtol("ff", NULL, 16); write with printf's %x (base 16) and %o (base 8).
For other bases, collect the digits backwards and then reverse them
CraykonThere are no GCD or base functions, so write them all yourself.
The quotient is Mathf.Quot(x, base), a digit character is DIGITS[d], and a character's value is DIGITS.IndexOf(ch)

Common mistakes

Converting 0 gave an empty string

def to_base(x, base):
    s = ""
    while x > 0:
        s = str(x % base) + s
        x //= base
    return s

print("[" + to_base(0, 2) + "]")

→ [] — nothing between the brackets. When x is 0 the loop never runs

Return "0" separately when x is 0.

Taking the quotient with /JavaScript · TypeScript · Craykon

let x = 5, s = "";
while (x > 0) {
  s = (x % 2) + s;
  x = x / 2;
}
console.log(s.length, s.slice(-12));

→ 23085 .6251.250.51 — it should be 101, but the string is over 20,000 characters long. / keeps the fraction (2.5, 1.25, 0.625…), so x takes a very long time to reach 0

Take only the quotient: Math.floor(x / 2) in JavaScript, Mathf.Quot(x, 2) in Craykon.

i * i went past int in the sieveJava · C# · C++ · C

int n = 100000;
boolean[] isPrime = new boolean[n + 1];
Arrays.fill(isPrime, true);
for (int i = 2; i <= n; i++)
    if (isPrime[i])
        for (int j = i * i; j <= n; j += i) isPrime[j] = false;

→ ArrayIndexOutOfBoundsException: Index -2146737495 out of bounds for length 100001 — at i = 46349, i * i passed about 2.1 billion and turned negative. C# throws IndexOutOfRangeException; C and C++ die with SIGSEGV

Run the outer loop only while i * i <= n.
With n at 100,000, i only goes up to 316.

How languages differ
Use a built-in when there is one, but some languages have none and some handle only bases 2, 8 and 16, so know how to write these yourself.
The least common multiple is a / gcd(a, b) * b (252 for 84 and 36).
Divide first and multiply after, so the value in between stays smaller.

Practice