# ndpar/algorithms

Next chunk of solutions

1 parent a0f2aeb commit 96bb90f308d857c07a23450a2b09189950c62a14 Andrey Paramonov committed Mar 12, 2010
Showing with 362 additions and 24 deletions.
1. +40 −2 mymath.erl
2. +1 −15 p004.erl
3. +21 −0 p017.erl
4. +11 −0 p019.erl
5. +1 −7 p024.erl
6. +22 −0 p029.erl
7. +31 −0 p030.erl
8. +32 −0 p034.erl
9. +55 −0 p035.erl
10. +16 −0 p036.erl
11. +20 −0 p040.erl
12. +69 −0 p043.erl
13. +43 −0 p044.erl
42 mymath.erl
 @@ -1,5 +1,5 @@ -module(mymath). --export([c/2, ds/1, factorial/1, lcm/2, primes_upto/1, prod/1]). +-export([c/2, ds/1, factorial/1, is_palindrome/1, lcm/2, perms/1, pow/2, primes_upto/1, prod/1]). -include_lib("eunit/include/eunit.hrl"). %% Find all prime numbers upto specified value. @@ -60,8 +60,9 @@ c(N, M) -> prod(lists:seq(M+1, N)) div factorial(N-M). %% Product of numbers in the list %% -prod(List) -> lists:foldl(fun(X,Y) -> X*Y end, 1, List). +prod(List) -> lists:foldl(fun erlang:'*'/2, 1, List). +factorial(0) -> 1; factorial(N) -> prod(lists:seq(1,N)). @@ -70,6 +71,25 @@ factorial(N) -> prod(lists:seq(1,N)). ds(M) -> lists:foldl(fun(N, Sum) -> Sum + N - \$0 end, 0, integer_to_list(M)). +%% Integer power of another integer +%% +pow(N, 1) -> N; +pow(N, E) -> N * pow(N, E-1). + + +%% Calculates all permutations for elements from given list +%% +perms([]) -> [[]]; +perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])]. + + +%% Returns true if given list or integer is palindrom +%% +is_palindrome([]) -> []; +is_palindrome([X|Xs]) -> [X|Xs] =:= lists:reverse([X|Xs]); +is_palindrome(N) -> is_palindrome(integer_to_list(N)). + + %% Tests primes_upto_30_test() -> @@ -101,3 +121,21 @@ factorial_test() -> ds_test() -> ?assertEqual(21, ds(1569)). + +pow_test() -> + ?assertEqual(32, pow(2, 5)). + +perms_test() -> + ?assertEqual(["012", "021", "102", "120", "201", "210"], perms("012")). + +is_palindrome_int_true_test() -> + ?assertEqual(true, is_palindrome(9009)). + +is_palindrome_int_false_test() -> + ?assertEqual(false, is_palindrome(9001)). + +is_palindrome_list_true_test() -> + ?assertEqual(true, is_palindrome("1001001001")). + +is_palindrome_list_false_test() -> + ?assertEqual(false, is_palindrome("1001001011")).
16 p004.erl
 @@ -8,7 +8,6 @@ -module(p004). -export([solve/0]). --include_lib("eunit/include/eunit.hrl"). %% Brute force solution %% --------------------- @@ -17,17 +16,4 @@ solve() -> lists:max(palindromes(lists:seq(100, 999))). palindromes(Factors) -> - [M * N || M <- Factors, N <- Factors, is_palindrome(M * N)]. - -is_palindrome(N) -> - Nl = integer_to_list(N), - Nl =:= lists:reverse(Nl). - - -%% Tests - -is_palindrome_true_test() -> - ?assertEqual(true, is_palindrome(9009)). - -is_palindrome_false_test() -> - ?assertEqual(false, is_palindrome(9001)). + [M * N || M <- Factors, N <- Factors, mymath:is_palindrome(M * N)].
21 p017.erl
 @@ -0,0 +1,21 @@ +%% Problem +%% --------------------- +%% If the numbers 1 to 5 are written out in words: one, two, three, four, +%% five, then there are 3 + 3 + 5 + 4 + 4 = 19 letters used in total. +%% +%% If all the numbers from 1 to 1000 (one thousand) inclusive were written +%% out in words, how many letters would be used? +%% --------------------- + +-module(p017). +-export([solve/0]). + + +solve() -> + Units = length("onetwothreefourfivesixseveneightnine"), + Teens = length("teneleventwelvethirteenfourteenfifteensixteenseventeeneighteennineteen"), + Ties = length("twentythirtyfortyfiftysixtyseventyeightyninety"), + Hundred = length("hundred"), + And = length("and"), + Thousand = length("onethousand"), + Thousand + 9*100*Hundred + 9*99*And + 10*10*Ties + 10*Teens + (9*10+100)*Units.
11 p019.erl
 @@ -0,0 +1,11 @@ +%% Problem +%% --------------------- +%% How many Sundays fell on the first of the month during +%% the twentieth century (1 Jan 1901 to 31 Dec 2000)? +%% --------------------- + +-module(p019). +-export([solve/0]). + + +solve() -> length([ {M, Y} || Y <- lists:seq(1901, 2000), M <- lists:seq(1, 12), calendar:day_of_the_week(Y, M, 1) =:= 7 ]).
8 p024.erl
 @@ -19,14 +19,8 @@ solve() -> find(1000000, "0123456789"). %% Brute force works relatively fast. %% -find(Nth, List) -> lists:nth(Nth, perms(List)). +find(Nth, List) -> lists:nth(Nth, mymath:perms(List)). -perms([]) -> [[]]; -perms(L) -> [[H|T] || H <- L, T <- perms(L--[H])]. - - -perms_test() -> - ?assertEqual(["012", "021", "102", "120", "201", "210"], perms("012")). find_test() -> ?assertEqual("120", find(4, "012")).
22 p029.erl
 @@ -0,0 +1,22 @@ +%% Problem +%% --------------------- +%% Consider all integer combinations of ab for 2 =< a =< 5 and 2 =< b =< 5: +%% +%% 2^2=4, 2^3=8, 2^4=16, 2^5=32 +%% 3^2=9, 3^3=27, 3^4=81, 3^5=243 +%% 4^2=16, 4^3=64, 4^4=256, 4^5=1024 +%% 5^2=25, 5^3=125, 5^4=625, 5^5=3125 +%% +%% If they are then placed in numerical order, with any repeats removed, +%% we get the following sequence of 15 distinct terms: +%% +%% 4, 8, 9, 16, 25, 27, 32, 64, 81, 125, 243, 256, 625, 1024, 3125 +%% +%% How many distinct terms are in the sequence generated by ab for 2 a 100 and 2 b 100? +%% --------------------- + +-module(p029). +-export([solve/0]). + + +solve() -> length(lists:usort([ mymath:pow(N, M) || N <- lists:seq(2, 100), M <- lists:seq(2, 100) ])).
31 p030.erl
 @@ -0,0 +1,31 @@ +%% Problem +%% --------------------- +%% Surprisingly there are only three numbers that can be written as +%% the sum of fourth powers of their digits: +%% +%% 1634 = 1^4 + 6^4 + 3^4 + 4^4 +%% 8208 = 8^4 + 2^4 + 0^4 + 8^4 +%% 9474 = 9^4 + 4^4 + 7^4 + 4^4 +%% +%% The sum of these numbers is 1634 + 8208 + 9474 = 19316. +%% +%% Find the sum of all the numbers that can be written as the sum of +%% fifth powers of their digits. +%% --------------------- + +-module(p030). +-export([solve/0]). +-include_lib("eunit/include/eunit.hrl"). + + +%% 6 * 9^5 = 354294 +%% +solve() -> lists:sum([ N || N <- lists:seq(11, 354294), value(N) =:= N ]). + +value(N) -> lists:sum([ mymath:pow(M-\$0, 5) || M <- integer_to_list(N)]). + + +value_test() -> + ?assertEqual(33, value(12)). + +% [4150,4151,54748,92727,93084,194979]
32 p034.erl
 @@ -0,0 +1,32 @@ +%% Problem +%% --------------------- +%% 145 is a curious number, as 1! + 4! + 5! = 1 + 24 + 120 = 145. +%% +%% Find the sum of all numbers which are equal to the sum of the factorial of their digits. +%% +%% Note: as 1! = 1 and 2! = 2 are not sums they are not included. +%% --------------------- + +-module(p034). +-export([solve/0]). +-include_lib("eunit/include/eunit.hrl"). + + +%% Some thoughts: +%% 9! = 362880; 7 times 9! is less than 9999999. +%% Brute force with that bound is very slow. +%% To find more accurate upper bound solve the equation: +%% x = 9! * ln(x) => x = 2309171 +%% Even with this bound brute force is still slow. +%% +solve() -> lists:sum([ N || N <- lists:seq(3, 2309171), N =:= sum(N) ]). + +sum(N) -> lists:sum([ mymath:factorial(M-\$0) || M <- integer_to_list(N) ]). + + +sum_test() -> + ?assertEqual(145, sum(145)). + + +% See also: +% http://mathworld.wolfram.com/Factorion.html
55 p035.erl
 @@ -0,0 +1,55 @@ +%% Problem +%% --------------------- +%% The number, 197, is called a circular prime because all rotations +%% of the digits: 197, 971, and 719, are themselves prime. +%% +%% There are thirteen such primes below 100: 2, 3, 5, 7, 11, 13, 17, +%% 31, 37, 71, 73, 79, and 97. +%% +%% How many circular primes are there below one million? +%% --------------------- + +-module(p035). +-export([solve/0]). +-include_lib("eunit/include/eunit.hrl"). + + +solve() -> find(1000000-1). + +find(Max) -> find([ integer_to_list(N) || N <- mymath:primes_upto(Max), no_even(N) ], 0). + +no_even(N) -> length([ M || M <- integer_to_list(N), M rem 2 > 0]) =:= length(integer_to_list(N)). + +find([], Acc) -> Acc; +find([X|Xs], Acc) -> + Circle = circle(X), + Xs1 = Xs -- Circle, + Acc1 = case length(Xs1) == length([X|Xs]) - length(Circle) of + true -> Acc + length(Circle); + false -> Acc + end, + find(Xs1, Acc1). + + +circle(S) -> circle(S, length(S), []). +circle(_, 0, Acc) -> lists:usort(Acc); +circle([H|T], N, Acc) -> circle(T++[H], N-1, [[H|T]|Acc]). + + +no_even_13_test() -> + ?assertEqual(true, no_even(13)). + +no_even_12_test() -> + ?assertEqual(false, no_even(12)). + +circle_1_test() -> + ?assertEqual(["1"], circle("1")). + +circle_11_test() -> + ?assertEqual(["11"], circle("11")). + +circle_123_test() -> + ?assertEqual(["123","231","312"], circle("123")). + +find_test() -> + ?assertEqual(13, find(100)).
16 p036.erl
 @@ -0,0 +1,16 @@ +%% Problem +%% --------------------- +%% The decimal number, 585 = 1001001001 (binary), is palindromic in both bases. +%% +%% Find the sum of all numbers, less than one million, which are palindromic in base 10 and base 2. +%% --------------------- + +-module(p036). +-export([solve/0]). + + +%% Brute force is pretty fast +%% +solve() -> lists:sum([ N || N <- lists:seq(1, 1000000-1), mymath:is_palindrome(N), mymath:is_palindrome(bin(N)) ]). + +bin(N) -> hd(io_lib:format("~.2B", [N])).
20 p040.erl
 @@ -0,0 +1,20 @@ +%% Problem +%% --------------------- +%% An irrational decimal fraction is created by concatenating the positive integers: +%% +%% 0.123456789101112131415161718192021... +%% +%% It can be seen that the 12th digit of the fractional part is 1. +%% +%% If dn represents the nth digit of the fractional part, find the value of the following expression. +%% +%% d1 x d10 x d100 x d1000 x d10000 x d100000 x d1000000 +%% --------------------- + +-module(p040). +-export([solve/0]). + + +solve() -> + Number = lists:flatten([ integer_to_list(X) || X <- lists:seq(1,230000) ]), + mymath:prod([ lists:nth(N,Number) - \$0 || N <- [1,10,100,1000,10000,100000,1000000] ]).
69 p043.erl
 @@ -0,0 +1,69 @@ +%% Problem +%% --------------------- +%% The number, 1406357289, is a 0 to 9 pandigital number because it +%% is made up of each of the digits 0 to 9 in some order, but it also +%% has a rather interesting sub-string divisibility property. +%% +%% Let d1 be the 1st digit, d2 be the 2nd digit, and so on. In this +%% way, we note the following: +%% +%% d2d3d4=406 is divisible by 2 +%% d3d4d5=063 is divisible by 3 +%% d4d5d6=635 is divisible by 5 +%% d5d6d7=357 is divisible by 7 +%% d6d7d8=572 is divisible by 11 +%% d7d8d9=728 is divisible by 13 +%% d8d9d10=289 is divisible by 17 +%% +%% Find the sum of all 0 to 9 pandigital numbers with this property. +%% --------------------- + +-module(p043). +-export([solve/0]). +-include_lib("eunit/include/eunit.hrl"). + + +%% Brute force works for less than a minute. +%% perms/1 function is slow. +%% +solve() -> + lists:sum([ list_to_integer(X) || X <- mymath:perms("0123456789"), condition(X) ]). + +%% Explicit and fast but long +condition(X) -> + condition(X, 2, 2) and + condition(X, 3, 3) and + condition(X, 4, 5) and + condition(X, 5, 7) and + condition(X, 6, 11) and + condition(X, 7, 13) and + condition(X, 8, 17). + +% Functional and short but slow +%condition(X) -> +% lists:foldl(fun([S,P],Acc) -> condition(X,S,P) and Acc end, true, +% [[2,2],[3,3],[4,5],[5,7],[6,11],[7,13],[8,17]]). + +condition(Str, Start, P) -> + list_to_integer(lists:sublist(Str, Start, 3)) rem P == 0. + + +cond_test() -> + ?assertEqual(true, condition("1406357289")). + +cond_3_test() -> + ?assertEqual(true, condition("1406357289", 3, 3)). + +cond_7_test() -> + ?assertEqual(true, condition("1406357289", 5, 7)). + +cond_11_test() -> + ?assertEqual(true, condition("1406357289", 6, 11)). + +cond_13_test() -> + ?assertEqual(true, condition("1406357289", 7, 13)). + +cond_17_test() -> + ?assertEqual(true, condition("1406357289", 8, 17)). + +% 1406357289,1430952867,1460357289,4106357289,4130952867,4160357289
43 p044.erl
 @@ -0,0 +1,43 @@ +%% Problem +%% --------------------- +%% Pentagonal numbers are generated by the formula, Pn=n(3n1)/2. The +%% first ten pentagonal numbers are: +%% +%% 1, 5, 12, 22, 35, 51, 70, 92, 117, 145, ... +%% +%% It can be seen that P4 + P7 = 22 + 70 = 92 = P8. However, their +%% difference, 70 - 22 = 48, is not pentagonal. +%% +%% Find the pair of pentagonal numbers, Pj and Pk, for which their +%% sum and difference is pentagonal and D = |Pk - Pj| is minimised; +%% what is the value of D? +%% --------------------- + +-module(p044). +-export([solve/0]). +-include_lib("eunit/include/eunit.hrl"). + + +solve() -> pent(1, 1, []). + +%% P(n+1) = P(n) + 3n + 1 +%% +pent(N, P, Ps) -> + List = [ P-X || X <- Ps, is_pentagonal(P-X), is_pentagonal(P+X) ], + case List of + [] -> pent(N+1, P+3*N+1, [P|Ps]); + _ -> List + end. + +%% http://en.wikipedia.org/wiki/Pentagonal_number +%% +is_pentagonal(X) -> + N = (math:sqrt(24*X+1) + 1) / 6, + N == trunc(N). + + +is_pentagonal_true_test() -> + ?assertEqual(true, is_pentagonal(92)). + +is_pentagonal_false_test() -> + ?assertEqual(false, is_pentagonal(91)).