Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/18.ts
Original file line number Diff line number Diff line change
@@ -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.
//
Expand Down Expand Up @@ -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];
}
30 changes: 0 additions & 30 deletions src/22.rkt

This file was deleted.

36 changes: 36 additions & 0 deletions src/22.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// Computes name scores based on the names in the text file.
//
// See {@link https://projecteuler.net/problem=22}
import _ from 'lodash';
import slurp from './core/slurp';

function getNames() {
return slurp('22.txt')
.split(',')
.map(line => 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<string, number>) {
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;
}
24 changes: 0 additions & 24 deletions src/23.rkt

This file was deleted.

23 changes: 23 additions & 0 deletions src/23.ts
Original file line number Diff line number Diff line change
@@ -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);
}
42 changes: 0 additions & 42 deletions src/67.rkt

This file was deleted.

19 changes: 19 additions & 0 deletions src/67.ts
Original file line number Diff line number Diff line change
@@ -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];
}
3 changes: 2 additions & 1 deletion src/7.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ 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}.
export function compute(n: number) {
Expand Down
69 changes: 0 additions & 69 deletions src/70.rkt

This file was deleted.

69 changes: 69 additions & 0 deletions src/70.ts
Original file line number Diff line number Diff line change
@@ -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];
}
1 change: 1 addition & 0 deletions src/8.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ 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.
Expand Down
3 changes: 2 additions & 1 deletion src/core/slurp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
}
7 changes: 7 additions & 0 deletions test/22.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import compute from '../src/22';

describe('name scores', () => {
test('problem 22', async () => {
expect(compute()).toBe(871198282);
});
});
7 changes: 7 additions & 0 deletions test/23.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import compute from '../src/23';

describe('non-abundant sums', () => {
test('problem 23', async () => {
expect(compute(28123)).toBe(4179871);
});
});
7 changes: 7 additions & 0 deletions test/67.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import compute from '../src/67';

describe('maximum path sum ii', () => {
test('problem 67', async () => {
expect(compute()).toBe(7273);
});
});
7 changes: 7 additions & 0 deletions test/70.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import compute from '../src/70';

describe('totient permutation', () => {
test('problem 70', async () => {
expect(compute(1e7)).toBe(8319823);
});
});
Loading