From e8163b9026fa1c66fdf635fd11be90753d7bc4b8 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 9 Jan 2025 10:26:48 -0800 Subject: [PATCH 1/7] moves the comments --- src/1.ts | 4 ++-- src/2.ts | 4 ++-- src/22.ts | 3 +++ src/3.ts | 4 ++-- 4 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 src/22.ts diff --git a/src/1.ts b/src/1.ts index fb48de0..8f53692 100644 --- a/src/1.ts +++ b/src/1.ts @@ -1,8 +1,8 @@ -import _ from 'lodash'; - // Computes the sum of all the multiples of 3 or 5 below the given limit. // // See {@link https://projecteuler.net/problem=1}. +import _ from 'lodash'; + export default function compute(limit: number) { return _.range(limit) .filter(n => n % 3 === 0 || n % 5 === 0) diff --git a/src/2.ts b/src/2.ts index e3592af..b690913 100644 --- a/src/2.ts +++ b/src/2.ts @@ -1,8 +1,8 @@ -import fibs from './core/fibs'; - // Computes the sum of all the even Fibonacci numbers below the given limit. // // See {@link https://projecteuler.net/problem=2}. +import fibs from './core/fibs'; + export default function compute(limit: number) { return fibs(limit) .filter(n => n % 2 === 0) diff --git a/src/22.ts b/src/22.ts new file mode 100644 index 0000000..b99b6f2 --- /dev/null +++ b/src/22.ts @@ -0,0 +1,3 @@ +// Computes name scores. +// +// See {@link https://projecteuler.net/problem=22} \ No newline at end of file diff --git a/src/3.ts b/src/3.ts index ab9dc53..211c703 100644 --- a/src/3.ts +++ b/src/3.ts @@ -1,8 +1,8 @@ -import primeFactors from './core/prime-factors'; - // Computes the largest prime factor of a given number. // // See {@link https://projecteuler.net/problem=3}. +import primeFactors from './core/prime-factors'; + export default function compute(n: number) { return Math.max(...primeFactors(n)); } From 2e8d38924bffa51fa434631663042701b931a864 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 9 Jan 2025 10:27:27 -0800 Subject: [PATCH 2/7] moves the comments --- src/4.ts | 4 ++-- src/5.ts | 6 +++--- src/6.ts | 4 ++-- src/7.ts | 4 ++-- src/8.ts | 6 +++--- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/4.ts b/src/4.ts index f4fd8ac..126cf43 100644 --- a/src/4.ts +++ b/src/4.ts @@ -1,8 +1,8 @@ -import _ from 'lodash'; - // Finds the largest palindrome made from the product of two 3-digit numbers. // // See {@link https://projecteuler.net/problem=4}. +import _ from 'lodash'; + export default function compute() { let max = 0; diff --git a/src/5.ts b/src/5.ts index 6acb219..376d827 100644 --- a/src/5.ts +++ b/src/5.ts @@ -1,9 +1,9 @@ -import _ from 'lodash'; -import lcm from './core/lcm'; - // Computes the smallest number that can be divided by each of the numbers from 1 to n without any remainder. // // See {@link https://projecteuler.net/problem=5}. +import _ from 'lodash'; +import lcm from './core/lcm'; + export default function compute(n: number) { return _.range(1, n + 1).reduce(lcm, 1); } diff --git a/src/6.ts b/src/6.ts index 2ba9a3d..2f7fd49 100644 --- a/src/6.ts +++ b/src/6.ts @@ -1,9 +1,9 @@ -import _ from 'lodash'; - // Computes the difference between the sum of the squares of the first n natural numbers and the square of the sum of // the first n natural numbers. // // See {@link https://projecteuler.net/problem=6}. +import _ from 'lodash'; + export default function compute(limit: number) { const a = _.range(limit + 1) .map(n => n ** 2) diff --git a/src/7.ts b/src/7.ts index fd0f469..5186522 100644 --- a/src/7.ts +++ b/src/7.ts @@ -1,11 +1,11 @@ -import primes from './core/primes'; - // Computes the nth prime number. // // @remarks // Cheats. Just uses a precomputed list of prime numbers. // // See {@link https://projecteuler.net/problem=7}. +import primes from './core/primes'; + export function compute(n: number) { return primes()[n - 1]; } diff --git a/src/8.ts b/src/8.ts index 79df4df..191a7e8 100644 --- a/src/8.ts +++ b/src/8.ts @@ -1,6 +1,3 @@ -import _ from 'lodash'; -import slurp from './core/slurp'; - // Compute the largest product of of `window` consecutive digits in the given data. // // @remarks @@ -9,6 +6,9 @@ import slurp from './core/slurp'; // product if we have no zeroes in the window. // // See {@link https://projecteuler.net/problem=8}. +import _ from 'lodash'; +import slurp from './core/slurp'; + export default function compute(window: number): number { const data = slurp('8.txt').split('\n').join(''); From cae4cbff5f6f90bcf23b613c360843c700479b3e Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 9 Jan 2025 15:37:01 -0800 Subject: [PATCH 3/7] updates problem 22 --- src/10.ts | 4 ++-- src/11.ts | 6 +++--- src/12.ts | 4 ++-- src/13.ts | 6 +++--- src/14.ts | 30 +++++++++++++++--------------- src/15.ts | 4 ++-- src/16.ts | 4 ++-- src/20.ts | 6 +++--- src/21.ts | 6 +++--- src/22.rkt | 30 ------------------------------ src/22.ts | 37 +++++++++++++++++++++++++++++++++++-- src/core/slurp.ts | 3 ++- test/22.test.ts | 7 +++++++ 13 files changed, 79 insertions(+), 68 deletions(-) delete mode 100644 src/22.rkt create mode 100644 test/22.test.ts diff --git a/src/10.ts b/src/10.ts index 311291b..e7e8fd3 100644 --- a/src/10.ts +++ b/src/10.ts @@ -1,8 +1,8 @@ -import primes from './core/primes'; - // Computes the sum of all the prime numbers below the given limit. // // See {@link https://projecteuler.net/problem=10}. +import primes from './core/primes'; + export default function compute(limit: number) { return primes() .filter(p => p < limit) diff --git a/src/11.ts b/src/11.ts index b103479..278dfae 100644 --- a/src/11.ts +++ b/src/11.ts @@ -1,9 +1,9 @@ -import _ from 'lodash'; -import slurp from './core/slurp'; - // Computes the greatest product of 4 adjacent numbers in the same direction in the grid defined by `11.txt`. // // See {@link https://projecteuler.net/problem=11}. +import _ from 'lodash'; +import slurp from './core/slurp'; + export default function compute() { const m = slurp('11.txt') .trim() diff --git a/src/12.ts b/src/12.ts index 4174c2f..bc5f0ec 100644 --- a/src/12.ts +++ b/src/12.ts @@ -1,8 +1,8 @@ -import tau from './core/tau'; - // Computes the value of the first triangle number to have over the given number of divisors. // // See {@link https://projecteuler.net/problem=12}. +import tau from './core/tau'; + export default function compute(limit: number): number { let n = 0; let i = 1; diff --git a/src/13.ts b/src/13.ts index 8bf39c7..9e21f9a 100644 --- a/src/13.ts +++ b/src/13.ts @@ -1,9 +1,9 @@ -import _ from 'lodash'; -import slurp from './core/slurp'; - // Compute the first ten digits of the sum of the one-hundred 50-digit numbers defined by `13.txt`. // // See {@link https://projecteuler.net/problem=13}. +import _ from 'lodash'; +import slurp from './core/slurp'; + export default function compute() { const numbers = slurp('13.txt').split('\n').map(BigInt); const sum = numbers.reduce((a, b) => a + b, 0n); diff --git a/src/14.ts b/src/14.ts index 71ea55a..f8ef086 100644 --- a/src/14.ts +++ b/src/14.ts @@ -1,21 +1,6 @@ // Computes the number that produces the longest Collatz chain under a given limit. // // See {@link https://projecteuler.net/problem=14}. -export default function compute(limit: number) { - let max = 0; - let result = 0; - - for (let i = 1; i < limit; i++) { - const current = collatzLength(i); - if (current > max) { - max = current; - result = i; - } - } - - return result; -} - const memo = new Map(); function collatzLength(n: number): number { @@ -33,3 +18,18 @@ function collatzLength(n: number): number { memo.set(n, result); return result; } + +export default function compute(limit: number) { + let max = 0; + let result = 0; + + for (let i = 1; i < limit; i++) { + const current = collatzLength(i); + if (current > max) { + max = current; + result = i; + } + } + + return result; +} diff --git a/src/15.ts b/src/15.ts index 89b6cbb..2049673 100644 --- a/src/15.ts +++ b/src/15.ts @@ -1,5 +1,3 @@ -import factorial from './core/factorial'; - // Computes the number of routes from the upper left to the bottom right of a lattice grid. // // @remarks @@ -13,6 +11,8 @@ import factorial from './core/factorial'; // // See {@link https://en.wikipedia.org/wiki/Permutation#Permutations_of_multisets}. // See {@link https://projecteuler.net/problem=15}. +import factorial from './core/factorial'; + export default function compute() { const k = factorial(20); const n = factorial(20 + 20); diff --git a/src/16.ts b/src/16.ts index 05f2ac6..da6ddde 100644 --- a/src/16.ts +++ b/src/16.ts @@ -1,8 +1,8 @@ -import _ from 'lodash'; - // Computes the sum of the digits of the given number raised to the given power. // // See {@link https://projecteuler.net/problem=16}. +import _ from 'lodash'; + export default function compute(k: number, e: number) { const s = BigInt(Math.pow(k, e)).toString(); return s.split('').reduce((a, b) => a + _.toNumber(b), 0); diff --git a/src/20.ts b/src/20.ts index f6af528..5c1fc16 100644 --- a/src/20.ts +++ b/src/20.ts @@ -1,9 +1,9 @@ -import _ from 'lodash'; -import factorial from './core/factorial'; - // Computes the sum of the digits of the factorial of a given number. // // See {@link https://projecteuler.net/problem=20}. +import _ from 'lodash'; +import factorial from './core/factorial'; + export default function compute(limit: number) { const text = _.toString(factorial(limit)); diff --git a/src/21.ts b/src/21.ts index 6b49661..1692e9e 100644 --- a/src/21.ts +++ b/src/21.ts @@ -1,9 +1,9 @@ -import _ from 'lodash'; -import sigma from './core/sigma'; - // Computes the sum of all amicable numbers under `limit`. // // See {@link https://projecteuler.net/problem=21} +import _ from 'lodash'; +import sigma from './core/sigma'; + export default function compute(limit: number) { let sum = 0; diff --git a/src/22.rkt b/src/22.rkt deleted file mode 100644 index b224e8c..0000000 --- a/src/22.rkt +++ /dev/null @@ -1,30 +0,0 @@ -#lang racket -(require rackunit) -(require srfi/1) -(require "lib/core.rkt") - - -(define names - ((compose (curry regexp-split ",") - (curryr string-replace "\"" "")) - (file->string "/data/22.txt"))) - -(define scores - (zipmap (map integer->char (range (char->integer #\A) - (add1 (char->integer #\Z)))) - (range 1 (add1 26)))) - -(define (score s) - (for/sum ((c (string->list s))) - (hash-ref scores c))) - -(define m - (zipmap (range 1 (add1 (length names))) - (map (curry score) (sort names string line.slice(1, -1)) + .sort((a, b) => a.localeCompare(b)); +} + +function getScores() { + const codes = _.range('A'.charCodeAt(0), 'Z'.charCodeAt(0) + 1).map(c => String.fromCharCode(c)) + const values = _.range(1, 27); + return new Map(_.zip(codes, values) as [[string, number]]); +} + +function getScore(name: string, scores: Map) { + return name.split('').reduce((a, b) => a + scores.get(b)!, 0); +} + +export default function compute() { + const scores = getScores(); + const names = getNames(); + + let result = 0; + for (let i = 0; i < names.length; i++) { + const name = names[i]; + const score = getScore(name, scores); + result += (i + 1) * score; + } + + return result; +} \ No newline at end of file diff --git a/src/core/slurp.ts b/src/core/slurp.ts index 5101acd..fd9bd62 100644 --- a/src/core/slurp.ts +++ b/src/core/slurp.ts @@ -2,5 +2,6 @@ import fs from 'fs'; import path from 'path'; export default function slurp(name: string): string { - return fs.readFileSync(path.join('data', name), 'utf8').toString(); + const file = path.join('data', name); + return fs.readFileSync(file, 'utf8').toString(); } diff --git a/test/22.test.ts b/test/22.test.ts new file mode 100644 index 0000000..9df60ad --- /dev/null +++ b/test/22.test.ts @@ -0,0 +1,7 @@ +import compute from '../src/22'; + +describe('name scores', () => { + test('problem 22', async () => { + expect(compute()).toBe(871198282); + }); +}); From 2b1fa9e5dbe7d8d212f8ba8cba82d008e74d1e08 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 9 Jan 2025 15:49:21 -0800 Subject: [PATCH 4/7] abunduns --- src/1.ts | 4 ++-- src/10.ts | 4 ++-- src/11.ts | 6 +++--- src/12.ts | 4 ++-- src/13.ts | 6 +++--- src/14.ts | 30 +++++++++++++++--------------- src/15.ts | 4 ++-- src/16.ts | 4 ++-- src/2.ts | 4 ++-- src/20.ts | 6 +++--- src/21.ts | 6 +++--- src/22.ts | 4 ++-- src/23.rkt | 24 ------------------------ src/23.ts | 23 +++++++++++++++++++++++ src/3.ts | 4 ++-- src/4.ts | 4 ++-- src/5.ts | 6 +++--- src/6.ts | 4 ++-- src/7.ts | 7 ++++--- src/8.ts | 7 ++++--- src/core/slurp.ts | 2 +- test/23.test.ts | 7 +++++++ 22 files changed, 89 insertions(+), 81 deletions(-) delete mode 100644 src/23.rkt create mode 100644 src/23.ts create mode 100644 test/23.test.ts diff --git a/src/1.ts b/src/1.ts index 8f53692..fb48de0 100644 --- a/src/1.ts +++ b/src/1.ts @@ -1,8 +1,8 @@ +import _ from 'lodash'; + // Computes the sum of all the multiples of 3 or 5 below the given limit. // // See {@link https://projecteuler.net/problem=1}. -import _ from 'lodash'; - export default function compute(limit: number) { return _.range(limit) .filter(n => n % 3 === 0 || n % 5 === 0) diff --git a/src/10.ts b/src/10.ts index e7e8fd3..311291b 100644 --- a/src/10.ts +++ b/src/10.ts @@ -1,8 +1,8 @@ +import primes from './core/primes'; + // Computes the sum of all the prime numbers below the given limit. // // See {@link https://projecteuler.net/problem=10}. -import primes from './core/primes'; - export default function compute(limit: number) { return primes() .filter(p => p < limit) diff --git a/src/11.ts b/src/11.ts index 278dfae..b103479 100644 --- a/src/11.ts +++ b/src/11.ts @@ -1,9 +1,9 @@ -// Computes the greatest product of 4 adjacent numbers in the same direction in the grid defined by `11.txt`. -// -// See {@link https://projecteuler.net/problem=11}. import _ from 'lodash'; import slurp from './core/slurp'; +// Computes the greatest product of 4 adjacent numbers in the same direction in the grid defined by `11.txt`. +// +// See {@link https://projecteuler.net/problem=11}. export default function compute() { const m = slurp('11.txt') .trim() diff --git a/src/12.ts b/src/12.ts index bc5f0ec..4174c2f 100644 --- a/src/12.ts +++ b/src/12.ts @@ -1,8 +1,8 @@ +import tau from './core/tau'; + // Computes the value of the first triangle number to have over the given number of divisors. // // See {@link https://projecteuler.net/problem=12}. -import tau from './core/tau'; - export default function compute(limit: number): number { let n = 0; let i = 1; diff --git a/src/13.ts b/src/13.ts index 9e21f9a..8bf39c7 100644 --- a/src/13.ts +++ b/src/13.ts @@ -1,9 +1,9 @@ -// Compute the first ten digits of the sum of the one-hundred 50-digit numbers defined by `13.txt`. -// -// See {@link https://projecteuler.net/problem=13}. import _ from 'lodash'; import slurp from './core/slurp'; +// Compute the first ten digits of the sum of the one-hundred 50-digit numbers defined by `13.txt`. +// +// See {@link https://projecteuler.net/problem=13}. export default function compute() { const numbers = slurp('13.txt').split('\n').map(BigInt); const sum = numbers.reduce((a, b) => a + b, 0n); diff --git a/src/14.ts b/src/14.ts index f8ef086..71ea55a 100644 --- a/src/14.ts +++ b/src/14.ts @@ -1,6 +1,21 @@ // Computes the number that produces the longest Collatz chain under a given limit. // // See {@link https://projecteuler.net/problem=14}. +export default function compute(limit: number) { + let max = 0; + let result = 0; + + for (let i = 1; i < limit; i++) { + const current = collatzLength(i); + if (current > max) { + max = current; + result = i; + } + } + + return result; +} + const memo = new Map(); function collatzLength(n: number): number { @@ -18,18 +33,3 @@ function collatzLength(n: number): number { memo.set(n, result); return result; } - -export default function compute(limit: number) { - let max = 0; - let result = 0; - - for (let i = 1; i < limit; i++) { - const current = collatzLength(i); - if (current > max) { - max = current; - result = i; - } - } - - return result; -} diff --git a/src/15.ts b/src/15.ts index 2049673..89b6cbb 100644 --- a/src/15.ts +++ b/src/15.ts @@ -1,3 +1,5 @@ +import factorial from './core/factorial'; + // Computes the number of routes from the upper left to the bottom right of a lattice grid. // // @remarks @@ -11,8 +13,6 @@ // // See {@link https://en.wikipedia.org/wiki/Permutation#Permutations_of_multisets}. // See {@link https://projecteuler.net/problem=15}. -import factorial from './core/factorial'; - export default function compute() { const k = factorial(20); const n = factorial(20 + 20); diff --git a/src/16.ts b/src/16.ts index da6ddde..05f2ac6 100644 --- a/src/16.ts +++ b/src/16.ts @@ -1,8 +1,8 @@ +import _ from 'lodash'; + // Computes the sum of the digits of the given number raised to the given power. // // See {@link https://projecteuler.net/problem=16}. -import _ from 'lodash'; - export default function compute(k: number, e: number) { const s = BigInt(Math.pow(k, e)).toString(); return s.split('').reduce((a, b) => a + _.toNumber(b), 0); diff --git a/src/2.ts b/src/2.ts index b690913..e3592af 100644 --- a/src/2.ts +++ b/src/2.ts @@ -1,8 +1,8 @@ +import fibs from './core/fibs'; + // Computes the sum of all the even Fibonacci numbers below the given limit. // // See {@link https://projecteuler.net/problem=2}. -import fibs from './core/fibs'; - export default function compute(limit: number) { return fibs(limit) .filter(n => n % 2 === 0) diff --git a/src/20.ts b/src/20.ts index 5c1fc16..f6af528 100644 --- a/src/20.ts +++ b/src/20.ts @@ -1,9 +1,9 @@ -// Computes the sum of the digits of the factorial of a given number. -// -// See {@link https://projecteuler.net/problem=20}. import _ from 'lodash'; import factorial from './core/factorial'; +// Computes the sum of the digits of the factorial of a given number. +// +// See {@link https://projecteuler.net/problem=20}. export default function compute(limit: number) { const text = _.toString(factorial(limit)); diff --git a/src/21.ts b/src/21.ts index 1692e9e..6b49661 100644 --- a/src/21.ts +++ b/src/21.ts @@ -1,9 +1,9 @@ -// Computes the sum of all amicable numbers under `limit`. -// -// See {@link https://projecteuler.net/problem=21} import _ from 'lodash'; import sigma from './core/sigma'; +// Computes the sum of all amicable numbers under `limit`. +// +// See {@link https://projecteuler.net/problem=21} export default function compute(limit: number) { let sum = 0; diff --git a/src/22.ts b/src/22.ts index 1900013..5c6aea1 100644 --- a/src/22.ts +++ b/src/22.ts @@ -12,7 +12,7 @@ function getNames() { } function getScores() { - const codes = _.range('A'.charCodeAt(0), 'Z'.charCodeAt(0) + 1).map(c => String.fromCharCode(c)) + const codes = _.range('A'.charCodeAt(0), 'Z'.charCodeAt(0) + 1).map(c => String.fromCharCode(c)); const values = _.range(1, 27); return new Map(_.zip(codes, values) as [[string, number]]); } @@ -33,4 +33,4 @@ export default function compute() { } return result; -} \ No newline at end of file +} diff --git a/src/23.rkt b/src/23.rkt deleted file mode 100644 index fffb9cd..0000000 --- a/src/23.rkt +++ /dev/null @@ -1,24 +0,0 @@ -#lang racket -(require rackunit) -(require "lib/number-theory.rkt") - - -(define limit 28123) - -(define numbers - (list->vector (range (add1 limit)))) - -(define abundants - (list->vector (filter (lambda (n) (> (sigma* n) n)) (range 1 (add1 limit))))) - -(for* ((i (range (vector-length abundants))) - (j (range i (vector-length abundants)))) - (let ((n (+ (vector-ref abundants i) (vector-ref abundants j)))) - (when (<= n limit) - (vector-set! numbers n 0)))) - -(define result - (for/sum ((n numbers) #:when (not (zero? n))) n)) - -(displayln result) -(check-equal? result 4179871) diff --git a/src/23.ts b/src/23.ts new file mode 100644 index 0000000..7db3f51 --- /dev/null +++ b/src/23.ts @@ -0,0 +1,23 @@ +import _ from 'lodash'; +import sigma from './core/sigma'; + +// Computes the sum of all the positive integers which cannot be written as the sum of two abundant numbers. +// +// See {@link https://projecteuler.net/problem=23} +export default function compute(limit: number) { + const abundants = _.range(1, limit + 1).filter(n => sigma(n) - n > n); + const numbers = _.range(0, limit + 1); + + for (let i = 0; i < abundants.length; i++) { + for (let j = i; j < abundants.length; j++) { + const sum = abundants[i] + abundants[j]; + if (sum <= limit) { + numbers[sum] = 0; + } else { + break; + } + } + } + + return numbers.reduce((a, b) => a + b, 0); +} diff --git a/src/3.ts b/src/3.ts index 211c703..ab9dc53 100644 --- a/src/3.ts +++ b/src/3.ts @@ -1,8 +1,8 @@ +import primeFactors from './core/prime-factors'; + // Computes the largest prime factor of a given number. // // See {@link https://projecteuler.net/problem=3}. -import primeFactors from './core/prime-factors'; - export default function compute(n: number) { return Math.max(...primeFactors(n)); } diff --git a/src/4.ts b/src/4.ts index 126cf43..f4fd8ac 100644 --- a/src/4.ts +++ b/src/4.ts @@ -1,8 +1,8 @@ +import _ from 'lodash'; + // Finds the largest palindrome made from the product of two 3-digit numbers. // // See {@link https://projecteuler.net/problem=4}. -import _ from 'lodash'; - export default function compute() { let max = 0; diff --git a/src/5.ts b/src/5.ts index 376d827..6acb219 100644 --- a/src/5.ts +++ b/src/5.ts @@ -1,9 +1,9 @@ -// Computes the smallest number that can be divided by each of the numbers from 1 to n without any remainder. -// -// See {@link https://projecteuler.net/problem=5}. import _ from 'lodash'; import lcm from './core/lcm'; +// Computes the smallest number that can be divided by each of the numbers from 1 to n without any remainder. +// +// See {@link https://projecteuler.net/problem=5}. export default function compute(n: number) { return _.range(1, n + 1).reduce(lcm, 1); } diff --git a/src/6.ts b/src/6.ts index 2f7fd49..2ba9a3d 100644 --- a/src/6.ts +++ b/src/6.ts @@ -1,9 +1,9 @@ +import _ from 'lodash'; + // Computes the difference between the sum of the squares of the first n natural numbers and the square of the sum of // the first n natural numbers. // // See {@link https://projecteuler.net/problem=6}. -import _ from 'lodash'; - export default function compute(limit: number) { const a = _.range(limit + 1) .map(n => n ** 2) diff --git a/src/7.ts b/src/7.ts index 5186522..9c13001 100644 --- a/src/7.ts +++ b/src/7.ts @@ -1,11 +1,12 @@ +import primes from './core/primes'; + // Computes the nth prime number. // // @remarks -// Cheats. Just uses a precomputed list of prime numbers. +// +// Problem cheats. Just uses a precomputed list of prime numbers. // // See {@link https://projecteuler.net/problem=7}. -import primes from './core/primes'; - export function compute(n: number) { return primes()[n - 1]; } diff --git a/src/8.ts b/src/8.ts index 191a7e8..d452ccc 100644 --- a/src/8.ts +++ b/src/8.ts @@ -1,14 +1,15 @@ +import _ from 'lodash'; +import slurp from './core/slurp'; + // Compute the largest product of of `window` consecutive digits in the given data. // // @remarks +// // Can be solved with a sliding window algorithm. We just have to take care to note the number of zeroes in the window; // if a window has any zeroes in it, thet product of that window will be zero. At each step, we only compute the // product if we have no zeroes in the window. // // See {@link https://projecteuler.net/problem=8}. -import _ from 'lodash'; -import slurp from './core/slurp'; - export default function compute(window: number): number { const data = slurp('8.txt').split('\n').join(''); diff --git a/src/core/slurp.ts b/src/core/slurp.ts index fd9bd62..2d22f3a 100644 --- a/src/core/slurp.ts +++ b/src/core/slurp.ts @@ -2,6 +2,6 @@ import fs from 'fs'; import path from 'path'; export default function slurp(name: string): string { - const file = path.join('data', name); + const file = path.join('data', name); return fs.readFileSync(file, 'utf8').toString(); } diff --git a/test/23.test.ts b/test/23.test.ts new file mode 100644 index 0000000..cce81cd --- /dev/null +++ b/test/23.test.ts @@ -0,0 +1,7 @@ +import compute from '../src/23'; + +describe('non-abundant sums', () => { + test('problem 23', async () => { + expect(compute(28123)).toBe(4179871); + }); +}); From 6ede22b2019645b8e792097f799b421d9a6c7e62 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 9 Jan 2025 16:32:27 -0800 Subject: [PATCH 5/7] totient perm --- src/70.rkt | 69 ------------------------------------------------- src/70.ts | 69 +++++++++++++++++++++++++++++++++++++++++++++++++ test/70.test.ts | 7 +++++ 3 files changed, 76 insertions(+), 69 deletions(-) delete mode 100644 src/70.rkt create mode 100644 src/70.ts create mode 100644 test/70.test.ts diff --git a/src/70.rkt b/src/70.rkt deleted file mode 100644 index 54b656a..0000000 --- a/src/70.rkt +++ /dev/null @@ -1,69 +0,0 @@ -#lang racket -(require rackunit) -(require "lib/number-theory.rkt") - - -; Search around the radius of (sqrt 1e7), and generate all possible semi-prime -; pairs "around" that vicinity. -(define limit (inexact->exact 1e7)) - -(define limit* (* 2 (add1 (integer-sqrt limit)))) - -(define ps - (filter (curryr < limit*) (file->list "/data/primes.txt"))) - -(define bs - (combinations ps 2)) - -; This is a special totient function that optimizes the computation of totient -; for a semi-prime whose factors are p and q. -; -; For a prime number, the totient is simply (sub1 p). Because the totient is -; a multiplicative function, (phi (* p q)) is the same as (* (phi p) (phi q)). -(define (totient* p q) - (* (sub1 p) (sub1 q))) - -; Checks if two positive integers' digits are permutations of each other. -(define (digits-permutation? m n) - (let ((m* (sort (number->list m) <)) - (n* (sort (number->list n) <))) - (equal? m* n*))) - -(define (digits-permutation*? pair) - (let* ((m (car pair)) - (n (cdr pair))) - (digits-permutation? m n))) - -; Defines pair of totient(n) and the prime factors of n (p and q). -(define (make-totient-to-semi-prime-pair factors) - (let* ((p (car factors)) - (q (cadr factors)) - (n (* p q))) - (cons (totient* p q) n))) - -; Filter out semiprimes > limit that aren't digit permutations with the totient. -(define (within-limit? pair) - (let ((n (cdr pair))) - (<= n limit))) - -(define totient-to-semi-prime-pairs - ((compose (curry filter digits-permutation*?) - (curry filter within-limit?) - (curry map make-totient-to-semi-prime-pair)) - bs)) - -; Of the candidates that are digit permutations and where the ratio is at the -; max, find the largest value of phi(n) * n. -(define min-ratio limit) -(define result 0) - -(for ((pair totient-to-semi-prime-pairs)) - (let* ((phi (car pair)) - (n (cdr pair)) - (ratio (/ n phi))) - (when (< ratio min-ratio) - (set! min-ratio ratio) - (set! result n)))) - -(displayln result) -(check-equal? result 8319823) diff --git a/src/70.ts b/src/70.ts new file mode 100644 index 0000000..8e41410 --- /dev/null +++ b/src/70.ts @@ -0,0 +1,69 @@ +import primes from './core/primes'; + +// Computes the value of n for which φ((n) is a permutation of n and the ratio n/φ(n) produces a minimum. +// +// See {@link https://projecteuler.net/problem=70} +export default function compute(limit: number) { + const pairs = getSemiPrimePairs(getLimit(limit)) + .map(pair => transformPair(pair)) + .filter(pair => isWithinLimit(pair, limit)) + .filter(pair => isPermutation(pair)); + + let minRatio = limit; + let result = 0; + + for (const [phi, n] of pairs) { + const ratio = n / phi; + if (ratio < minRatio) { + minRatio = ratio; + result = n; + } + } + + return result; +} + +// This is a special totient function that optimizes the computation of totient for a semi-prime whose factors are p +// and q. +// +// For a prime number, the totient is simply (sub1 p). Because the totient is a multiplicative function, +// totient(p * q) = totient(p) * totient(q). Therefore, the totient of a semi-prime is simply (p - 1) * (q - 1). +function totient(p: number, q: number): number { + return (p - 1) * (q - 1); +} + +// Checks if two positive integers' digits are permutations of each other. +function isPermutation(pair: [number, number]): boolean { + const [m, n] = pair; + return m.toString().split('').sort().join('') === n.toString().split('').sort().join(''); +} + +function getLimit(limit: number): number { + return 2 * (Math.floor(Math.sqrt(limit)) + 1); +} + +function isWithinLimit(pair: [number, number], limit: number): boolean { + return pair[1] < limit; +} + +function getSemiPrimePairs(limit: number): [number, number][] { + const ps = primes().filter(p => p < limit); + const pairs: [number, number][] = []; + + for (let i = 0; i < ps.length; i++) { + for (let j = i; j < ps.length; j++) { + const p = ps[i]; + const q = ps[j]; + pairs.push([p, q]); + } + } + + return pairs; +} + +// Creates totient to semi-prime pairs. +function transformPair(factors: [number, number]): [number, number] { + const [p, q] = factors; + const n = p * q; + return [totient(p, q), n]; +} diff --git a/test/70.test.ts b/test/70.test.ts new file mode 100644 index 0000000..3f1febc --- /dev/null +++ b/test/70.test.ts @@ -0,0 +1,7 @@ +import compute from '../src/70'; + +describe('totient permutation', () => { + test('problem 70', async () => { + expect(compute(1e7)).toBe(8319823); + }); +}); From ed513c71763c67122c7a700d8fc19970effca767 Mon Sep 17 00:00:00 2001 From: Min Huang Date: Thu, 9 Jan 2025 16:36:51 -0800 Subject: [PATCH 6/7] converts 67 --- src/18.ts | 2 ++ src/67.rkt | 42 ------------------------------------------ src/67.ts | 19 +++++++++++++++++++ test/67.test.ts | 7 +++++++ 4 files changed, 28 insertions(+), 42 deletions(-) delete mode 100644 src/67.rkt create mode 100644 src/67.ts create mode 100644 test/67.test.ts diff --git a/src/18.ts b/src/18.ts index ffe84e5..315b388 100644 --- a/src/18.ts +++ b/src/18.ts @@ -1,6 +1,7 @@ // Compute the maximum total from top to bottom of the triangle below. // // @remarks +// // Rather than calculating the path sums going down, look at the bottom row and // the row above it. // @@ -51,5 +52,6 @@ export default function compute() { triangle[i][j] += Math.max(triangle[i + 1][j], triangle[i + 1][j + 1]); } } + return triangle[0][0]; } diff --git a/src/67.rkt b/src/67.rkt deleted file mode 100644 index 08b9d70..0000000 --- a/src/67.rkt +++ /dev/null @@ -1,42 +0,0 @@ -#lang racket -(require rackunit) - - -(define (2dvector-num-rows a) (vector-length a)) - -(define (2dvector-num-cols a i) - (let ((v (vector-ref a i))) - (length (takef (vector->list v) (lambda (e) (not (= e 0))))))) - -(define (2dvector-ref a i j) - (vector-ref (vector-ref a i) j)) - -(define (2dvector-set! a i j x) - (vector-set! (vector-ref a i) j x)) - -(define input (string-trim (file->string "/data/67.txt"))) - -(define (string->row s) - (map string->number (regexp-split #px" " s))) - -(define data - ((compose - list->vector - (curry map list->vector) - (curry map string->row) - (curry map string-trim) - (curry regexp-split #px"\n")) - input)) - -(for* ((i (range (- (2dvector-num-rows data) 2) (sub1 0) -1)) - (j (range 0 (2dvector-num-cols data i)))) - (let* ((current (2dvector-ref data i j)) - (left (2dvector-ref data (add1 i) j)) - (right (2dvector-ref data (add1 i) (add1 j))) - (bigger (max left right))) - (2dvector-set! data i j (+ current bigger)))) - -(define result (2dvector-ref data 0 0)) - -(displayln result) -(check-equal? result 7273) diff --git a/src/67.ts b/src/67.ts new file mode 100644 index 0000000..5d87e84 --- /dev/null +++ b/src/67.ts @@ -0,0 +1,19 @@ +import slurp from "./core/slurp" + +// Computes the maximum path sum from the top to the bottom of a triangle. +// +// See {@link https://projecteuler.net/problem=67} +export default function compute() { + const triangle = slurp('67.txt') + .trim() + .split('\n') + .map(row => row.trim().split(' ').map(Number)); + + for (let i = triangle.length - 2; i >= 0; i--) { + for (let j = 0; j < triangle[i].length; j++) { + triangle[i][j] += Math.max(triangle[i + 1][j], triangle[i + 1][j + 1]); + } + } + + return triangle[0][0]; +} diff --git a/test/67.test.ts b/test/67.test.ts new file mode 100644 index 0000000..f91e4ec --- /dev/null +++ b/test/67.test.ts @@ -0,0 +1,7 @@ +import compute from '../src/67'; + +describe('maximum path sum ii', () => { + test('problem 67', async () => { + expect(compute()).toBe(7273); + }); +}); From 8b8a2e2cf988fe605d6a61c391cb6ea01942c747 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Fri, 10 Jan 2025 00:37:21 +0000 Subject: [PATCH 7/7] Automatic commit via GitHub Actions --- src/67.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/67.ts b/src/67.ts index 5d87e84..39de4c0 100644 --- a/src/67.ts +++ b/src/67.ts @@ -1,4 +1,4 @@ -import slurp from "./core/slurp" +import slurp from './core/slurp'; // Computes the maximum path sum from the top to the bottom of a triangle. //