「素数 (prime number)」は正の約数が 1 と自分自身しかない自然数のことです。1 は素数に含めません。1 でない正整数で、素数ではないものを「合成数 (composite number)」といいます。たとえば、100 以下の素数は 25 個あります。
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
今回は素数にまつわるプログラムを JavaScript で作ってみましょう。詳しい説明は拙作のページをお読みください。
//
// primes.js : 素数
//
// Copyright (c) 2025 Makoto Hiroi
//
// Released under the MIT license
// https://opensource.org/license/mit/
//
// べき剰余
function modpow(x, y, n) {
let r = 1;
while (y > 0) {
if ((y & 1) > 0) r = r * x % n;
x = x * x % n;
y >>= 1;
}
return r;
}
// エラトステネスの篩
function sieve(n) {
let ps = [2];
let x = 3;
let primes = new Uint8Array(Math.floor(n / 2));
while (x * x <= n) {
let y = Math.floor((x - 3) / 2);
if (!primes[y]) {
ps.push(x);
y += x;
while (y < primes.length) {
primes[y] = 1;
y += x;
}
}
x += 2;
}
while (x <= n) {
let y = Math.floor((x - 3) / 2);
if (!primes[y]) ps.push(x);
x += 2;
}
return ps;
}
// 区間篩
function segmented_sieve(low, high) {
if (low <= 2) return sieve(high)
let xs = new Uint8Array(high - low + 1);
for (let p of sieve(Math.floor(Math.sqrt(high)))) {
let s = Math.ceil(low / p) * p - low;
if (low <= p) s += p;
for (let i = s; i < xs.length; i += p) xs[i] = 1;
}
let ps = [];
for (let i = 0; i < xs.length; i++) {
if (!xs[i]) ps.push(i + low);
}
return ps;
}
// 素数の判定 (試し割り)
function is_prime(n) {
let wheel = [1, 2, 2, 4, 2, 4, 2, 4, 6, 2, 6];
let [i, s] = [0, 3];
let p = 2;
while (p * p <= n) {
if (n % p == 0) return false;
p += wheel[i];
if (++i >= wheel.length) i = s;
}
return true;
}
function big_random_sub(n, d) {
let a = 0n;
while (d-- > 0) {
a = (a << 32n) + BigInt(Math.floor(0x1_0000_0000 * Math.random()));
}
return a % n;
}
// s 以上 e 未満の乱数を BigInt で生成する
function big_random(s, e) {
let n = e - s;
let d = 0;
for (let m = n; m > 0n; m >>= 32n) d++;
return s + big_random_sub(n, d);
}
// k 個の乱数を生成
function big_randoms(s, e, k) {
let n = e - s;
let xs = [];
let d = 0;
for (let m = n; m > 0n; m >>= 32n) d++;
while (k-- > 0) {
xs.push(s + big_random_sub(n, d));
}
return xs;
}
// ミラーラビン素数判定法
// n を (2 ** c) * d に分解する
function factor2(n) {
let c = 0n;
while (n % 2n == 0n) {
n /= 2n;
c++;
}
return [c, n];
}
// べき剰余 (BigInt 版)
function modpow_bigint(x, y, n) {
let r = 1n;
while (y > 0n) {
if ((y & 1n) > 0n) r = r * x % n;
x = x * x % n;
y >>= 1n;
}
return r;
}
function miller_rabin_sub(n, s, d, ks) {
for (let a of ks) {
if (a >= n) break;
if (modpow_bigint(a, d, n) != 1n) {
let r = 0n;
while (r < s) {
if (modpow_bigint(a, (2n ** r) * d, n) == n - 1n) break;
r++;
}
if (r >= s) return false;
}
}
return true;
}
function miller_rabin_test(n, k = 20) {
if (n < 2n) return false;
if (n == 2n) return true;
if (n % 2n == 0) return false;
let [s, d] = factor2(n - 1n);
let ks;
if (n < 4759123141n) {
ks = [2n, 7n, 61n];
} else if (n <= 0x1_0000_0000_0000_0000n) {
ks = [2n, 325n, 9375n, 28178n, 450775n, 9780504n, 1795265022n];
} else {
ks = big_randoms(1n, n, k);
}
return miller_rabin_sub(n, s, d, ks);
}
// リュカ-レーマー・テスト
function lucas_lehmer_test(p) {
if (p % 2 == 0) return false;
let m = 2n ** BigInt(p) - 1n;
let x = 4n;
for (let i = 0; i < p - 2; i++) {
x = (x * x - 2n) % m
}
return x % m == 0;
}
// 双子素数
function twin_primes(n) {
let xs = sieve(n + 2);
let ys = [];
for (let i = 0; i < xs.length - 1; i++) {
if (xs[i] + 2 == xs[i + 1]) ys.push([xs[i], xs[i+1]]);
}
return ys;
}
export {
modpow, sieve, segmented_sieve, is_prime, big_random, big_randoms,
miller_rabin_test, lucas_lehmer_test, twin_primes
}
//
// test_primes.js : 素数のテスト
//
// Copyright (c) 2025 Makoto Hiroi
//
// Released under the MIT license
// https://opensource.org/license/mit/
//
import * as P from './primes.js';
console.log('---- modepow -----');
console.log(P.modpow(7, 654321, 10)); // 7
console.log(P.modpow(7, 6543210, 10)); // 9
console.log('---- sieve, segmented_sieve -----');
console.log(P.sieve(100)); // 2,3,5,7,...
console.log(P.sieve(100).length); // 25
console.log(P.sieve(100000).length); // 9592
console.log(P.sieve(1000000).length); // 78498
console.log(P.sieve(10000000).length); // 664579
console.log(P.segmented_sieve(100, 200)); // 101 ... 199
console.log(P.segmented_sieve(100, 200).length); // 21
console.log(P.segmented_sieve(1000000, 1000100)); // [1000003, 1000033, 1000037, 1000039, 1000081, 1000099]
console.log('---- is_prime -----');
console.log(P.is_prime(2)); // true
console.log(P.is_prime(97)); // true
console.log(P.is_prime(111)); // false
console.log(P.is_prime(2 * 31 - 1)); // true
console.log(P.is_prime(2 * 32 - 1)); // false
console.log('---- miller_rabin_test -----');
console.log(P.miller_rabin_test(2n)); // true
console.log(P.miller_rabin_test(97n)); // true
console.log(P.miller_rabin_test(111n)); // false
console.log(P.miller_rabin_test(2n ** (2n ** 0n) + 1n)); // true
console.log(P.miller_rabin_test(2n ** (2n ** 1n) + 1n)); // true
console.log(P.miller_rabin_test(2n ** (2n ** 2n) + 1n)); // true
console.log(P.miller_rabin_test(2n ** (2n ** 3n) + 1n)); // true
console.log(P.miller_rabin_test(2n ** (2n ** 4n) + 1n)); // true
console.log(P.miller_rabin_test(2n ** (2n ** 5n) + 1n)); // false
console.log(P.miller_rabin_test(2n ** 31n - 1n)); // true
console.log(P.miller_rabin_test(2n ** 32n - 1n)); // false
console.log(P.miller_rabin_test(2n ** 89n - 1n)); // true
console.log(P.miller_rabin_test(2n ** 90n - 1n)); // false
console.log(P.miller_rabin_test(2n ** 607n - 1n)); // true
console.log(P.miller_rabin_test(2n ** 608n - 1n)); // false
console.log('---- lucas_lehmer_test -----');
for (let i = 3; i < 1000; i += 2) {
if (P.lucas_lehmer_test(i)) console.log(i);
}
console.log('---- twin_primes -----');
console.log(P.twin_primes(100));
console.log(P.twin_primes(3000000).length); // 20932
console.log('---- big_random -----');
console.log(2n**32n);
for (let i = 0; i < 4; i++) console.log(P.big_random(0n, 2n ** 32n));
console.log(P.big_randoms(2n**16n, 2n ** 32n, 4));
console.log(2n**64n);
for (let i = 0; i < 4; i++) console.log(P.big_random(0n, 2n ** 64n));
console.log(P.big_randoms(2n**48n, 2n ** 64n, 4));
console.log(2n**96n);
for (let i = 0; i < 4; i++) console.log(P.big_random(0n, 2n ** 96n));
console.log(P.big_randoms(2n**80n, 2n ** 96n, 4));
$ node test_primes.js ---- modepow ----- 7 9 ---- sieve, segmented_sieve ----- [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97 ] 25 9592 78498 664579 [ 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199 ] 21 [ 1000003, 1000033, 1000037, 1000039, 1000081, 1000099 ] ---- is_prime ----- true true false true false ---- miller_rabin_test ----- true true false true true true true true false true false true false true false ---- lucas_lehmer_test ----- 3 5 7 13 17 19 31 61 89 107 127 521 607 ---- twin_primes ----- [ [ 3, 5 ], [ 5, 7 ], [ 11, 13 ], [ 17, 19 ], [ 29, 31 ], [ 41, 43 ], [ 59, 61 ], [ 71, 73 ] ] 20932 ---- big_random ----- 4294967296n 445165036n 67746803n 3111766799n 317258130n [ 1243407988n, 368579738n, 2794824690n, 489971548n ] 18446744073709551616n 14754816802008137100n 9315702261375939981n 12849699641133499619n 6410765238664596166n [ 7159733700449148631n, 14753923102134523062n, 9021503864372089985n, 495178360824506896n ] 79228162514264337593543950336n 45897100895909178701239571339n 30936128865905323611882010292n 26503787494083486129373503926n 15771361232900844706876833616n [ 52136448438308779376695304250n, 60460452991954675642761860585n, 48552450636253818932392439895n, 35381167006374857514585795697n ]