Ninety-Nine Problems in Elixir

This list of problems is inspired by the original Ninety-Nine Prolog Problems post. Each entry is given a brief descriptor and, where applicable, a type spec you can use as a starting point. Solutions are left as an exercise, but you can follow along with mine here!

Working with lists

  1. Find the last element of a list.

    @spec last(list :: list()) :: any()
    
  2. Find the last but one element of a list.

    @spec penultimate(list :: list()) :: any()
    
  3. Find the K’th element of a list (first element is number 1).

    @spec element_at(list :: list(), k :: pos_integer()) :: any()
    
  4. Find the number of elements of a list.

    @spec length(list :: list()) :: non_neg_integer()
    
  5. Reverse a list.

    @spec reverse(list :: list()) :: list()
    
  6. Find out whether a list is a palindrome.

    @spec palindrome?(list :: list()) :: boolean()
    
  7. Flatten a nested list structure.

    @spec flatten(nested :: list()) :: list()
    
  8. Eliminate consecutive duplicates of list elements.

    @spec compress(list :: list()) :: list()
    
  9. Pack consecutive duplicates of list elements into sublists.

    @spec pack(list :: list()) :: list(list())
    
  10. Run-length encoding of a list.

    @spec encode(list :: list()) :: list({pos_integer(), any()})
    
  11. Modified run-length encoding (singleton elements copied as-is).

    @spec encode_modified(list :: list()) :: list()
    
  12. Decode a run-length encoded list.

    @spec decode(encoded :: list()) :: list()
    
  13. Run-length encoding of a list (direct solution).

    @spec encode_direct(list :: list()) :: list()
    
  14. Duplicate the elements of a list.

    @spec dupli(list :: list()) :: list()
    
  15. Duplicate the elements of a list a given number of times.

    @spec dupli(list :: list(), n :: pos_integer()) :: list()
    
  16. Drop every N’th element from a list.

    @spec drop(list :: list(), n :: pos_integer()) :: list()
    
  17. Split a list into two parts; the length of the first part is given.

    @spec split(list :: list(), n :: non_neg_integer()) :: {list(), list()}
    
  18. Extract a slice from a list (both indices inclusive, starting at 1).

    @spec slice(list :: list(), i :: pos_integer(), k :: pos_integer()) :: list()
    
  19. Rotate a list N places to the left (negative N rotates right).

    @spec rotate(list :: list(), n :: integer()) :: list()
    
  20. Remove the K’th element from a list.

    @spec remove_at(list :: list(), k :: pos_integer()) :: {any(), list()}
    
  21. Insert an element at a given position into a list.

    @spec insert_at(list :: list(), x :: any(), k :: pos_integer()) :: list()
    
  22. Create a list containing all integers within a given range.

    @spec range(from :: integer(), to :: integer()) :: list(integer())
    
  23. Extract a given number of randomly selected elements from a list.

    @spec rnd_select(list :: list(), n :: non_neg_integer()) :: list()
    
  24. Lotto: draw N different random numbers from the set 1..M.

    @spec lotto(n :: pos_integer(), m :: pos_integer()) :: list(pos_integer())
    
  25. Generate a random permutation of the elements of a list.

    @spec rnd_permu(list :: list()) :: list()
    
  26. Generate the combinations of K distinct objects chosen from the N elements of a list.

    @spec combination(list :: list(), k :: non_neg_integer()) :: list(list())
    
  27. Group the elements of a set into disjoint subsets.

    @spec group(list :: list(), sizes :: list(pos_integer())) :: list(list())
    
  28. Sort a list of lists according to length of sublists.

    # a) by sublist length
    @spec lsort(list_of_lists :: list(list())) :: list(list())
    
    # b) by length frequency
    @spec lfsort(list_of_lists :: list(list())) :: list(list())
    

Arithmetic

  1. Determine whether a given integer number is prime.

    @spec prime?(n :: pos_integer()) :: boolean()
    
  2. Determine the greatest common divisor of two positive integers (Euclid’s algorithm).

    @spec gcd(a :: pos_integer(), b :: pos_integer()) :: pos_integer()
    
  3. Determine whether two positive integer numbers are coprime.

    @spec coprime?(a :: pos_integer(), b :: pos_integer()) :: boolean()
    
  4. Calculate Euler’s totient function phi(m).

    @spec totient_phi(m :: pos_integer()) :: non_neg_integer()
    
  5. Determine the prime factors of a given positive integer.

    @spec prime_factors(n :: pos_integer()) :: list(pos_integer())
    
  6. Determine the prime factors of a given positive integer (with multiplicity).

    @spec prime_factors_mult(n :: pos_integer()) :: list({pos_integer(), pos_integer()})
    
  7. Calculate Euler’s totient function phi(m) (improved, using prime factors).

    @spec phi(n :: pos_integer()) :: pos_integer()
    
  8. Compare the two methods of calculating Euler’s totient function.

    @spec compare_methods(n :: pos_integer()) :: {non_neg_integer(), non_neg_integer()}
    
  9. A list of prime numbers in a given range.

    @spec primes_in_range(from :: pos_integer(), to :: pos_integer()) :: list(pos_integer())
    
  10. Goldbach’s conjecture: find two primes that sum to a given even integer.

    @spec goldbach(n :: even_integer) :: {pos_integer(), pos_integer()}
    
  11. A list of Goldbach compositions in a range of integers.

    @spec goldbach_list(from :: pos_integer(), to :: pos_integer()) :: list({pos_integer(), {pos_integer(), pos_integer()}})
    

Logic and codes

  1. Truth tables for logical expressions (two variables).

    @spec table(a :: boolean(), b :: boolean(), expr :: (boolean(), boolean()) -> boolean()) :: list(map())
    
  2. Truth tables for logical expressions using infix operators.

    @spec table(expr :: any()) :: list(map())
    
  3. Truth tables for logical expressions with any number of variables.

    @spec table(vars :: list(atom()), expr :: any()) :: list(map())
    
  4. Gray code: sequence of N-bit strings.

    @spec gray(n :: pos_integer()) :: list(String.t())
    
  5. Huffman code.

    @spec huffman(frequencies :: list({any(), pos_integer()})) :: list({any(), String.t()})
    

Binary trees

A binary tree is represented as t(x, left, right) with nil for the empty tree.

@type tree() :: nil | {any(), tree(), tree()}
  1. Check whether a term represents a binary tree.

    @spec tree?(any()) :: boolean()
    
  2. Construct completely balanced binary trees for a given number of nodes (generates all solutions).

    @spec cbal_tree(pos_integer()) :: [tree()]
    
  3. Check whether a binary tree is symmetric.

    @spec symmetric?(tree()) :: boolean()
    
  4. Construct a binary search tree from a list of integers.

    @spec construct([integer()]) :: tree()
    
  5. Generate all symmetric, completely balanced binary trees with a given number of nodes.

    @spec sym_cbal_trees(pos_integer()) :: [tree()]
    
  6. Construct height-balanced binary trees for a given height.

    @spec hbal_tree(pos_integer()) :: [tree()]
    
  7. Construct height-balanced binary trees with a given number of nodes.

    @spec hbal_tree_nodes(pos_integer()) :: [tree()]
    
  8. Count the leaves of a binary tree.

    @spec count_leaves(tree()) :: non_neg_integer()
    

61a. Collect the leaves of a binary tree in a list.

```elixir
@spec leaves(tree()) :: [any()]
```
  1. Collect the internal nodes of a binary tree in a list.

    @spec internals(tree()) :: [any()]
    

62b. Collect the nodes at a given level in a list.

```elixir
@spec at_level(tree(), pos_integer()) :: [any()]
```
  1. Construct a complete binary tree with N nodes.

    @spec complete_binary_tree(pos_integer()) :: tree()
    
  2. Layout a binary tree (1): positions determined by inorder and depth.

    @spec layout(tree()) :: tree()
    
  3. Layout a binary tree (2): constant horizontal distance between neighbors.

    @spec layout(tree()) :: tree()
    
  4. Layout a binary tree (3): compact layout with symmetry.

    @spec layout(tree()) :: tree()
    
  5. A string representation of binary trees (and back).

    @spec tree_to_string(tree()) :: String.t()
    @spec string_to_tree(String.t()) :: tree()
    
  6. Preorder and inorder sequences of binary trees.

    @spec preorder(tree()) :: [any()]
    @spec inorder(tree()) :: [any()]
    @spec pre_in_tree([any()], [any()]) :: tree()
    
  7. Dotstring representation of binary trees.

    @spec tree_to_dotstring(tree()) :: String.t()
    @spec dotstring_to_tree(String.t()) :: tree()
    

Multiway trees

A multiway tree is represented as t(x, forest) where forest is a list of multiway trees.

@type multiway_tree() :: {any(), [multiway_tree()]}
  1. Tree construction from a node string.

    @spec string_to_mway(String.t()) :: multiway_tree()
    
  2. Determine the internal path length of a multiway tree.

    @spec ipl(multiway_tree()) :: non_neg_integer()
    
  3. Construct the bottom-up order sequence of the tree nodes.

    @spec bottom_up(multiway_tree()) :: [any()]
    
  4. Lisp-like tree representation of a multiway tree.

    @spec multiway_to_lisp(multiway_tree()) :: String.t()
    @spec lisp_to_multiway(String.t()) :: multiway_tree()
    

Graphs

  1. Convert between graph representations (edge-clause, graph-term, adjacency-list, human-friendly forms).

    @spec conversion(graph :: any(), from :: atom(), to :: atom()) :: any()
    
  2. Find an acyclic path from node A to node B in a graph.

    @spec path(graph :: list(), a :: any(), b :: any()) :: list() | nil
    
  3. Find a cycle (closed path) starting at node A.

    @spec cycle(graph :: list(), a :: any()) :: list() | nil
    
  4. Construct all spanning trees of a graph.

    @spec spanning_trees(graph :: list()) :: list(list())
    
  5. Construct the minimal spanning tree (Prim’s algorithm).

    @spec mst(graph :: list()) :: list()
    
  6. Graph isomorphism (bijection between two graphs).

    @spec is_isomorphic?(g1 :: list(), g2 :: list()) :: boolean()
    
  7. Node degree and graph coloration (Welch-Powell algorithm).

    @spec color(graph :: list()) :: [{any(), non_neg_integer()}]
    
  8. Depth-first order graph traversal.

    @spec dfs(graph :: list(), start :: any()) :: list()
    
  9. Split a graph into connected components.

    @spec connected_components(graph :: list()) :: list(list())
    
  10. Determine whether a graph is bipartite.

    @spec bipartite?(graph :: list()) :: boolean()
    

Miscellaneous problems

  1. Eight queens problem.

    @spec queens(n :: pos_integer()) :: list(list())
    
  2. Knight’s tour on an NxN chessboard.

    @spec knights_tour(n :: pos_integer()) :: list({non_neg_integer(), non_neg_integer()}) | nil
    
  3. Von Koch’s conjecture: enumerate nodes and edges 1..N such that each edge K differs by K.

    @spec von_koch(tree :: list()) :: list() | nil
    
  4. Arithmetic puzzle: insert operators into a list of numbers to form a valid equation.

    @spec arithmetic(nums :: list(), target :: number()) :: String.t() | nil
    
  5. Generate K-regular simple graphs with N nodes.

    @spec k_regular(n :: pos_integer(), k :: non_neg_integer()) :: list(list())
    
  6. English number words (e.g. 175 -> one seven five).

    @spec number_words(n :: non_neg_integer()) :: String.t()
    
  7. Syntax checker for Ada-style identifiers.

    @spec valid_identifier?(s :: String.t()) :: boolean()
    
  8. Sudoku solver.

    @spec solve_sudoku(board :: list(list())) :: list(list()) | nil
    
  9. Nonogram solver.

    @spec solve_nonogram(rows :: list(), cols :: list()) :: list(list(0 | 1)) | nil
    
  10. Crossword puzzle solver.

    @spec solve_crossword(words :: list(), grid :: list()) :: list() | nil