Commit c5eb97d2 authored by Nayuki's avatar Nayuki

P18, P19, P20, P21, P22, P24, P25, P29, P31, P67: Added explanatory comment to...

P18, P19, P20, P21, P22, P24, P25, P29, P31, P67: Added explanatory comment to all language solutions.
parent 65e3cd8b
......@@ -7,6 +7,13 @@
-}
{-
- If we start at a particular cell in the triangle, what is the maximum path total?
- If the cell is in the bottom row, then it is simply the cell's value. Otherwise the answer
- is the cell's value plus either {the maximum path total of the cell down and to the left}
- or {the maximum path total of the cell down and to the right}, whichever is greater.
-}
main = putStrLn (show ans)
ans = maxPath 0 0
......
......@@ -7,6 +7,14 @@
-}
{-
- We use Zeller's congruence to compute the day of week when given the year, month, and day.
- Then we simply check the first day of all the months in the given range by brute force.
-
- Zeller's congruence is well-known and a bit long to explain.
- See: https://en.wikipedia.org/wiki/Zeller%27s_congruence
-}
main = putStrLn (show ans)
ans = sum [1 | y <- [1901..2000], m <- [1..12], dayOfWeek y m 1 == 0]
......
......@@ -7,6 +7,10 @@
-}
{-
- We do a straightforward product thanks to Haskell's built-in arbitrary precision Integer type.
-}
main = putStrLn (show ans)
ans = digitSum (factorial 100 :: Integer)
......
......@@ -7,6 +7,11 @@
-}
{-
- We find the sum of proper divisors of a number by brute force,
- and apply the definition of an amicable number straightforwardly.
-}
main = putStrLn (show ans)
ans = sum [n | n <- [1..10^4], amicable n]
......
......@@ -10,6 +10,10 @@ import Data.List (sort)
import Data.Char (ord)
{-
- We apply straightforward algorithms to sort the names, sum the letter values, and multiply by the position.
-}
main = putStrLn (show ans)
ans = sum (zipWith (*) [1..] (map strSum (sort names)))
......
......@@ -7,6 +7,15 @@
-}
{-
- We initialize an array as the lowest permutation of the given digits, which is the sequence
- (0,1,2,3,4,5,6,7,8,9). Then we call the next permutation algorithm on it 999 999 times
- (because the index in the problem is 1-based), and stringify the resulting sequence.
-
- The next permutation algorithm is well-known and a bit long to explain.
- See: https://www.nayuki.io/page/next-lexicographical-permutation-algorithm
-}
main = putStrLn (show ans)
ans = case (iterate (>>= nextPerm) (Just [0..9])) !! (10^6 - 1)
of Just digits -> digitsToNum 0 digits -- Extract from Just wrapper
......
......@@ -7,6 +7,13 @@
-}
{-
- Because the target number is relatively small, we simply compute each Fibonacci number starting
- from the beginning until we encounter one with exactly 1000 digits. The Fibonacci sequence grows
- exponentially with a base of about 1.618, so the numbers in base 10 will lengthen by one digit
- after every log10(1.618) ~= 4.78 steps on average. This means the answer is at index around 4780.
-}
digits = 1000
main = putStrLn (show ans)
ans = length (takeWhile (< 10 ^ (digits - 1)) fibonacci)
......
......@@ -9,5 +9,9 @@
import Data.List (nub)
{-
- We generate all possible powers in the given range, put the values in a flat list,
- delete the duplicates of any value, and count the length of the remaining list.
-}
main = putStrLn (show ans)
ans = length (nub [a^b | a <- [2..100], b <- [2..100]])
......@@ -7,6 +7,11 @@
-}
{-
- We use the standard recursive algorithm to solve the subset sum problem, with memoization.
- The order of the coin values does not matter, but the values need to be unique.
-}
main = putStrLn (show ans)
ans = ways coins 200
......
......@@ -7,6 +7,15 @@
-}
{-
- If we start at a particular cell in the triangle, what is the maximum path total?
- If the cell is in the bottom row, then it is simply the cell's value. Otherwise the answer
- is the cell's value plus either {the maximum path total of the cell down and to the left}
- or {the maximum path total of the cell down and to the right}, whichever is greater.
- To ensure that the running time is polynomial rather than exponential,
- we memoize the sub-result at each cell to avoid needless recomputation.
-}
main = putStrLn (show ans)
ans = maxPath 0 0
......
......@@ -14,6 +14,20 @@ public final class p018 implements EulerSolution {
}
/*
* We create a new blank triangle with the same dimensions as the original big triangle.
* For each cell of the big triangle, we consider the sub-triangle whose top is at this cell,
* calculate the maximum path sum when starting from this cell, and store the result
* in the corresponding cell of the blank triangle.
*
* If we start at a particular cell, what is the maximum path total? If the cell is at the
* bottom of the big triangle, then it is simply the cell's value. Otherwise the answer is
* the cell's value plus either {the maximum path total of the cell down and to the left}
* or {the maximum path total of the cell down and to the right}, whichever is greater.
* By computing the blank triangle's values from bottom up, the dependent values are always
* computed before they are utilized. This technique is known as dynamic programming.
*/
public String run() {
for (int i = triangle.length - 2; i >= 0; i--) {
for (int j = 0; j < triangle[i].length; j++)
......
......@@ -14,6 +14,14 @@ public final class p019 implements EulerSolution {
}
/*
* We use Zeller's congruence to compute the day of week when given the year, month, and day.
* Then we simply check the first day of all the months in the given range by brute force.
*
* Zeller's congruence is well-known and a bit long to explain.
* See: https://en.wikipedia.org/wiki/Zeller%27s_congruence
*/
public String run() {
int count = 0;
for (int y = 1901; y <= 2000; y++) {
......
......@@ -14,6 +14,9 @@ public final class p020 implements EulerSolution {
}
/*
* We do a straightforward product with help from Java's BigInteger type.
*/
public String run() {
String temp = Library.factorial(100).toString();
int sum = 0;
......
......@@ -14,6 +14,11 @@ public final class p021 implements EulerSolution {
}
/*
* We find the sum of proper divisors of a number by brute force,
* and apply the definition of an amicable number straightforwardly.
*/
public String run() {
int sum = 0;
for (int i = 1; i < 10000; i++) {
......
......@@ -16,6 +16,10 @@ public final class p022 implements EulerSolution {
}
/*
* We apply straightforward algorithms to sort the names, sum the letter values, and multiply by the position.
*/
public String run() {
Arrays.sort(names);
int sum = 0;
......
......@@ -14,6 +14,15 @@ public final class p024 implements EulerSolution {
}
/*
* We initialize an array as the lowest permutation of the given digits, which is the sequence
* (0,1,2,3,4,5,6,7,8,9). Then we call the next permutation algorithm on it 999 999 times
* (because the index in the problem is 1-based), and stringify the resulting sequence.
*
* The next permutation algorithm is well-known and a bit long to explain.
* See: https://www.nayuki.io/page/next-lexicographical-permutation-algorithm
*/
public String run() {
// Initialize
int[] array = new int[10];
......
......@@ -16,6 +16,13 @@ public final class p025 implements EulerSolution {
}
/*
* Because the target number is relatively small, we simply compute each Fibonacci number starting
* from the beginning until we encounter one with exactly 1000 digits. The Fibonacci sequence grows
* exponentially with a base of about 1.618, so the numbers in base 10 will lengthen by one digit
* after every log10(1.618) ~= 4.78 steps on average. This means the answer is at index around 4780.
*/
private static final int DIGITS = 1000;
public String run() {
......
......@@ -18,6 +18,11 @@ public final class p029 implements EulerSolution {
}
/*
* We generate all the possible powers in the given range, put each value
* into a set, and let the set count the number of unique values present.
*/
public String run() {
Set<BigInteger> generated = new HashSet<>();
for (int a = 2; a <= 100; a++) {
......
......@@ -14,6 +14,11 @@ public final class p031 implements EulerSolution {
}
/*
* We use the standard dynamic programming algorithm to solve the subset sum problem over integers.
* The order of the coin values does not matter, but the values need to be unique.
*/
private static final int TOTAL = 200;
private static int[] COINS = {1, 2, 5, 10, 20, 50, 100, 200};
......
......@@ -14,6 +14,20 @@ public final class p067 implements EulerSolution {
}
/*
* We create a new blank triangle with the same dimensions as the original big triangle.
* For each cell of the big triangle, we consider the sub-triangle whose top is at this cell,
* calculate the maximum path sum when starting from this cell, and store the result
* in the corresponding cell of the blank triangle.
*
* If we start at a particular cell, what is the maximum path total? If the cell is at the
* bottom of the big triangle, then it is simply the cell's value. Otherwise the answer is
* the cell's value plus either {the maximum path total of the cell down and to the left}
* or {the maximum path total of the cell down and to the right}, whichever is greater.
* By computing the blank triangle's values from bottom up, the dependent values are always
* computed before they are utilized. This technique is known as dynamic programming.
*/
public String run() {
for (int i = triangle.length - 2; i >= 0; i--) {
for (int j = 0; j < triangle[i].length; j++)
......
......@@ -7,6 +7,13 @@
*)
(*
* If we start at a particular cell in the triangle, what is the maximum path total?
* If the cell is in the bottom row, then it is simply the cell's value. Otherwise the answer
* is the cell's value plus either {the maximum path total of the cell down and to the left}
* or {the maximum path total of the cell down and to the right}, whichever is greater.
*)
triangle = {
{75},
{95,64},
......
......@@ -7,5 +7,8 @@
*)
(*
* We simply use Mathematica's built-in date library to compute the answer by brute force.
*)
<< Miscellaneous`Calendar`
Sum[Boole[DayOfWeek[{y, m, 1}] === Sunday], {y, 1901, 2000}, {m, 1, 12}]
......@@ -7,4 +7,7 @@
*)
(*
* We do a straightforward computation thanks to Mathematica's built-in arbitrary precision integer type.
*)
Total[IntegerDigits[100!]]
......@@ -7,6 +7,10 @@
*)
(*
* We modify a built-in function to find the sum of proper divisors of a number.
* Then we apply the definition of an amicable number straightforwardly.
*)
DivisorSum[n_] := DivisorSigma[1, n] - n
AmicableQ[n_] := DivisorSum[n] != n && DivisorSum[DivisorSum[n]] == n
Total[Select[Range[9999], AmicableQ]]
......@@ -7,6 +7,10 @@
*)
(*
* We apply straightforward algorithms to sort the names, sum the letter values, and multiply by the position.
*)
peoplenames = { (* 10 strings per line, except final line *)
"MARY", "PATRICIA", "LINDA", "BARBARA", "ELIZABETH", "JENNIFER", "MARIA", "SUSAN", "MARGARET", "DOROTHY",
"LISA", "NANCY", "KAREN", "BETTY", "HELEN", "SANDRA", "DONNA", "CAROL", "RUTH", "SHARON",
......
......@@ -7,4 +7,8 @@
*)
(*
* We generate all 10! permutations of the sequence (0,1,2,3,4,5,6,7,8,9)
* into memory, and select the 1 000 000th element (1-based indexing).
*)
FromDigits[Permutations[Range[0, 9]][[1000000]]]
......@@ -7,6 +7,12 @@
*)
(*
* Because the target number is relatively small, we simply compute each Fibonacci number starting
* from the beginning until we encounter one with exactly 1000 digits. The Fibonacci sequence grows
* exponentially with a base of about 1.618, so the numbers in base 10 will lengthen by one digit
* after every log10(1.618) ~= 4.78 steps on average. This means the answer is at index around 4780.
*)
i = 0;
While[Fibonacci[i] < 10^999, i++]
i
......@@ -7,4 +7,8 @@
*)
(*
* We generate all possible powers in the given range, put the values in a flat list,
* delete the duplicates of any value, and count the length of the remaining list.
*)
Length[Union[Flatten[Table[a^b, {a, 2, 100}, {b, 2, 100}]]]]
......@@ -7,6 +7,10 @@
*)
(*
* We use the standard recursive algorithm to solve the subset sum problem, with memoization.
* The order of the coin values does not matter, but the values need to be unique.
*)
Coins = {1, 2, 5, 10, 20, 50, 100, 200};
Ways[coinIndex_, total_] := Ways[coinIndex, total] = (* Memoization *)
If[coinIndex == 0, Boole[total == 0],
......
......@@ -7,6 +7,15 @@
*)
(*
* If we start at a particular cell in the triangle, what is the maximum path total?
* If the cell is in the bottom row, then it is simply the cell's value. Otherwise the answer
* is the cell's value plus either {the maximum path total of the cell down and to the left}
* or {the maximum path total of the cell down and to the right}, whichever is greater.
* To ensure that the running time is polynomial rather than exponential,
* we memoize the sub-result at each cell to avoid needless recomputation.
*)
triangle = {
{59},
{73,41},
......
......@@ -7,6 +7,17 @@
#
# We create a new blank triangle with the same dimensions as the original big triangle.
# For each cell of the big triangle, we consider the sub-triangle whose top is at this cell,
# calculate the maximum path sum when starting from this cell, and store the result
# in the corresponding cell of the blank triangle.
#
# If we start at a particular cell, what is the maximum path total? If the cell is at the
# bottom of the big triangle, then it is simply the cell's value. Otherwise the answer is
# the cell's value plus either {the maximum path total of the cell down and to the left}
# or {the maximum path total of the cell down and to the right}, whichever is greater.
# By computing the blank triangle's values from bottom up, the dependent values are always
# computed before they are utilized. This technique is known as dynamic programming.
def compute():
for i in reversed(range(len(triangle) - 1)):
for j in range(len(triangle[i])):
......
......@@ -9,6 +9,7 @@
import datetime
# We simply use Python's built-in date library to compute the answer by brute force.
def compute():
ans = sum(1
for y in range(1901, 2001)
......
......@@ -9,6 +9,7 @@
import math
# We do a straightforward computation thanks to Python's built-in arbitrary precision integer type.
def compute():
n = math.factorial(100)
ans = sum(int(c) for c in str(n))
......
......@@ -7,6 +7,9 @@
#
# We first compute a table of sum-of-proper-divisors, then we use it to test which numbers are amicable.
# This approach differs from the Java implementation because trying to directly compute
# the proper-divisor-sum of each number by brute force is unacceptably slow in Python.
def compute():
# Compute sum of proper divisors for each number
divisorsum = [0] * 10000
......
......@@ -7,6 +7,7 @@
#
# We apply straightforward algorithms to sort the names, sum the letter values, and multiply by the position.
def compute():
ans = sum((i + 1) * (ord(c) - ord('A') + 1)
for (i, name) in enumerate(sorted(NAMES))
......
......@@ -11,6 +11,9 @@ if sys.version_info.major == 2:
range = xrange
# We initialize a list as the lowest permutation of the given digits, which is the sequence
# (0,1,2,3,4,5,6,7,8,9). Then we call a Python library function that generates a stream of
# all permutations of the values, seek to the 999 999th element (0-based indexing), and stringify it.
def compute():
arr = list(range(10))
temp = itertools.islice(itertools.permutations(arr), 999999, None)
......
......@@ -7,6 +7,10 @@
#
# Because the target number is relatively small, we simply compute each Fibonacci number starting
# from the beginning until we encounter one with exactly 1000 digits. The Fibonacci sequence grows
# exponentially with a base of about 1.618, so the numbers in base 10 will lengthen by one digit
# after every log10(1.618) ~= 4.78 steps on average. This means the answer is at index around 4780.
def compute():
a = 1
b = 1
......
......@@ -7,6 +7,8 @@
#
# We generate all the possible powers in the given range, put each value
# into a set, and let the set count the number of unique values present.
def compute():
seen = set(a**b for a in range(2, 101) for b in range(2, 101))
return str(len(seen))
......
......@@ -7,6 +7,8 @@
#
# We use the standard dynamic programming algorithm to solve the subset sum problem over integers.
# The order of the coin values does not matter, but the values need to be unique.
def compute():
ways = [1] + [0] * 200
for coin in [1, 2, 5, 10, 20, 50, 100, 200]:
......
......@@ -7,6 +7,17 @@
#
# We create a new blank triangle with the same dimensions as the original big triangle.
# For each cell of the big triangle, we consider the sub-triangle whose top is at this cell,
# calculate the maximum path sum when starting from this cell, and store the result
# in the corresponding cell of the blank triangle.
#
# If we start at a particular cell, what is the maximum path total? If the cell is at the
# bottom of the big triangle, then it is simply the cell's value. Otherwise the answer is
# the cell's value plus either {the maximum path total of the cell down and to the left}
# or {the maximum path total of the cell down and to the right}, whichever is greater.
# By computing the blank triangle's values from bottom up, the dependent values are always
# computed before they are utilized. This technique is known as dynamic programming.
def compute():
for i in reversed(range(len(triangle) - 1)):
for j in range(len(triangle[i])):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment