Ninety-Nine Problems in Java 8
This is an adaptation of the Ninety-Nine Prolog Problems written by Werner Hett at the Berne University of Applied Sciences in Berne, Switzerland.
From the original source:
The purpose of this problem collection is to give you the opportunity to practice your skills in logic programming. Your goal should be to find the most elegant solution of the given problems. Efficiency is important, but logical clarity is even more crucial. Some of the (easy) problems can be trivially solved using built-in predicates. However, in these cases, you learn more if you try to find your own solution.
The problems have different levels of difficulty. Those marked with a single asterisk (*) are easy. If you have successfully solved the preceding problems you should be able to solve them within a few (say 15) minutes. Problems marked with two asterisks (**) are of intermediate difficulty. If you are a skilled Java programmer it shouldn't take you more than 30-90 minutes to solve them. Problems marked with three asterisks (***) are more difficult. You may need more time (i.e. a few hours or more) to find a good solution.
I will use Java 8 features wherever it makes sense so that developers can learn how to use new features introduced in Java 8. If you are new to Java 8 then you can learn more about it by following my in-depth Java 8 tutorial.
Table of Contents
Lists
In Java, lists are instances of List<T> sub-types. You could use ArrayList<T> or LinkedList<T>. LinkedList are better suited for writing functional programs because they provide you methods to get the first and last elements of a List. One of the method that you will miss when working with Java LinkedList is tail. tail gives you everything except the first element. You could easily implement tail as shown below.
public static <T> List<T> tail(LinkedList<T> elements) { if (elements == null || elements.isEmpty()) { throw new NoSuchElementException(); } if (elements.size() == 1) { return Collections.emptyList(); } return elements.subList(1, elements.size()); }
Java 8 does not support pattern matching so you have to use if-else in your code.
P01 (*) Find the last element of a list
@Test public void shouldFindLastElementFromAListOfAlphabets() throws Exception { assertThat(P01.last(asList("a", "b", "c", "d")), is(equalTo("d"))); }
P02 (*) Find the last but one element of a list
@Test public void shouldFindSecondLastElementFromAList() throws Exception { List<Integer> numbers = asList(1, 2, 11, 4, 5, 8, 10, 6); assertThat(P02.secondLast(numbers), is(equalTo(10))); }
The method should throw NoSuchElementException when list is either empty or has one element.
@Test(expected = NoSuchElementException.class) public void shouldThrowExceptionWhenListEmpty() throws Exception { P02.secondLast(Collections.emptyList()); } @Test(expected = NoSuchElementException.class) public void shouldThrowExceptionWhenListHasSingleElement() throws Exception { P02.secondLast(Arrays.asList(1)); }
P03 (*) Find the Kth element of a list
The first element of a list has index 0. In the example shown below, last element would be at kth position 4.
@Test public void shouldFindKthElementFromAList() throws Exception { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); assertThat(P03.kth(numbers, 2), is(equalTo(3))); }
P04 (*) Find the number of elements of a list
@Test public void listOfEmptyListShouldBe0() throws Exception { int length = P04.length(Collections.emptyList()); assertThat(length, is(equalTo(0))); } @Test public void shouldFindListOfNonEmptyList() throws Exception { assertThat(P04.length(Arrays.asList(1, 2, 3, 4, 5)), is(equalTo(5))); }
P05 (*) Reverse a list
@Test public void shouldReverseAList() throws Exception { List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); assertThat(P05.reverse(numbers), is(equalTo(Arrays.asList(5, 4, 3, 2, 1)))); }
P06 (*) Find out whether a list is a palindrome
@Test public void shouldReturnTrueWhenListIsPalindrome() throws Exception { assertTrue(isPalindrome(Arrays.asList("x", "a", "m", "a", "x"))); } @Test public void shouldReturnFalseWhenListIsNotPalindrome() throws Exception { assertFalse(isPalindrome(Arrays.asList(1, 2, 3, 4, 5))); }
P07 (**) Flatten a nested list structure
import static java.util.Arrays.asList; @Test public void shouldFlattenAListOfList() throws Exception { List<String> flatten = P07.flatten(asList("a", asList("b", asList("c", "d")), "e"), String.class); assertThat(flatten, hasSize(5)); assertThat(flatten, hasItems("a", "b", "c", "d", "e")); } @Test public void shouldFlattenDeepNestedLists() throws Exception { List<String> flatten = P07.flatten(asList("a", asList("b", asList("c", asList("d", "e", asList("f", "g"))), "h")), String.class); assertThat(flatten, hasSize(8)); assertThat(flatten, hasItems("a", "b", "c", "d", "e", "f", "g", "h")); } @Test public void shouldReturnEmptyListWhenTryingToFlattenAnEmptyList() throws Exception { List<String> flatten = P07.flatten(Collections.emptyList(), String.class); assertTrue(flatten.isEmpty()); }
P08 (**) Eliminate consecutive duplicates of list elements
If a list contains repeated elements they should be replaced with a single copy of the element. The order of the elements should not be changed.
@Test public void shouldRemoveConsecutiveDuplicatesInAList() throws Exception { List<String> compressedList = P08.compress(asList("a", "a", "a", "a", "b", "c", "c", "d", "e", "e", "e", "e")); assertThat(compressedList, hasSize(5)); assertThat(compressedList, contains("a", "b", "c","d", "e")); } @Test public void shouldNotRemoveNonConsecutiveSimilarElementsFromAList() throws Exception { List<String> compressedList = P08.compress(asList("a", "a", "a", "a", "b", "c", "c", "a", "a", "d", "e", "e", "e", "e")); assertThat(compressedList, hasSize(6)); assertThat(compressedList, contains("a", "b", "c","a", "d", "e")); }
P09 (**) Pack consecutive duplicates of list elements into sublists
If a list contains repeated elements they should be placed in separate sublists.
@Test public void shouldReturnAListWithTwoListElementsWhenAListWithTwoUniqueElementsIsPassed() throws Exception { List<List<String>> packedList = P09.pack(Arrays.asList("a", "b")); assertThat(packedList, hasSize(2)); assertThat(packedList.get(0), contains("a")); assertThat(packedList.get(1), contains("b")); } @Test public void shouldPackConsecutiveDuplicatesInTheirOwnLists() throws Exception { List<List<String>> packedList = P09.pack(Arrays.asList("a", "a", "a", "a", "b", "c", "c", "a", "a", "d", "e", "e", "e", "e")); assertThat(packedList, hasSize(6)); assertThat(packedList.get(0), contains("a", "a", "a", "a")); assertThat(packedList.get(1), contains("b")); assertThat(packedList.get(2), contains("c", "c")); assertThat(packedList.get(3), contains("a", "a")); assertThat(packedList.get(4), contains("d")); assertThat(packedList.get(5), contains("e", "e", "e", "e")); }
P10 (*) Run-length encoding of a list
Use the result of problem 1.09 to implement the so-called run-length encoding data compression method. Consecutive duplicates of elements are encoded as terms [N,E] where N is the number of duplicates of the element E.
@Test public void shouldEncodeAList() throws Exception { List<SimpleEntry<Integer, String>> encodedList = P10.encode(Arrays.asList("a", "a", "a", "a", "b", "c", "c", "a", "a", "d", "e", "e", "e", "e")); assertThat(encodedList, hasSize(6)); assertThat(encodedList.get(0), is(equalTo(new SimpleEntry<>(4, "a")))); assertThat(encodedList.get(1), is(equalTo(new SimpleEntry<>(1, "b")))); assertThat(encodedList.get(2), is(equalTo(new SimpleEntry<>(2, "c")))); assertThat(encodedList.get(3), is(equalTo(new SimpleEntry<>(2, "a")))); assertThat(encodedList.get(4), is(equalTo(new SimpleEntry<>(1, "d")))); assertThat(encodedList.get(5), is(equalTo(new SimpleEntry<>(4, "e")))); }
P11 (*) Modified run-length encoding
Modify the result of problem 1.10 in such a way that if an element has no duplicates it is simply copied into the result list. Only elements with duplicates are transferred as [N,E] terms.
@Test public void shouldEncodeAList() throws Exception { List<Object> encodedList = P11.encode_modified(Arrays.asList("a", "a", "a", "a", "b", "c", "c", "a", "a", "d", "e", "e", "e", "e")); assertThat(encodedList, hasSize(6)); assertThat(encodedList.get(0), is(equalTo(new SimpleEntry<>(4, "a")))); assertThat(encodedList.get(1), is(equalTo("b"))); assertThat(encodedList.get(2), is(equalTo(new SimpleEntry<>(2, "c")))); assertThat(encodedList.get(3), is(equalTo(new SimpleEntry<>(2, "a")))); assertThat(encodedList.get(4), is(equalTo("d"))); assertThat(encodedList.get(5), is(equalTo(new SimpleEntry<>(4, "e")))); }
P12 (**) Decode a run-length encoded list
Given a run-length code list generated as specified in problem 1.11. Construct its uncompressed version.
@Test public void shouldDecodeEncodedList() throws Exception { List<String> encoded = P12.decode( Arrays.asList( new SimpleEntry<>(4, "a"), "b", new SimpleEntry<>(2, "c"), new SimpleEntry<>(2, "a"), "d", new SimpleEntry<>(4, "e"))); assertThat(encoded, hasSize(14)); }
P13 (**) Run-length encoding of a list (direct solution)
Implement the so-called run-length encoding data compression method directly. I.e. don't explicitly create the sublists containing the duplicates, as in problem P09, but only count them.
@Test public void shouldEncodeAList() throws Exception { List<SimpleEntry<Integer, String>> encodedList = P13.encode_direct(Arrays.asList("a", "a", "a", "a", "b", "c", "c", "a", "a", "d", "e", "e", "e", "e")); assertThat(encodedList, hasSize(6)); assertThat(encodedList.get(0), is(equalTo(new SimpleEntry<>(4, "a")))); assertThat(encodedList.get(1), is(equalTo(new SimpleEntry<>(1, "b")))); assertThat(encodedList.get(2), is(equalTo(new SimpleEntry<>(2, "c")))); assertThat(encodedList.get(3), is(equalTo(new SimpleEntry<>(2, "a")))); assertThat(encodedList.get(4), is(equalTo(new SimpleEntry<>(1, "d")))); assertThat(encodedList.get(5), is(equalTo(new SimpleEntry<>(4, "e")))); }
P14 (*) Duplicate the elements of a list
@Test public void shouldDuplicateElementsInAList() throws Exception { List<String> duplicates = P14.duplicate(Arrays.asList("a", "b", "c", "d")); assertThat(duplicates, hasSize(8)); assertThat(duplicates, contains("a", "a", "b", "b", "c", "c", "d", "d")); }
P15 ** (**) Duplicate the elements of a list a given number of times**
@Test public void shouldDuplicateElementsInAList() throws Exception { List<String> duplicates = P15.duplicate(Arrays.asList("a", "b", "c"), 3); assertThat(duplicates, hasSize(9)); assertThat(duplicates, contains("a", "a", "a", "b", "b", "b", "c", "c", "c")); }
P16 (**) Drop every N'th element from a list
@Test public void shouldDropEveryNthElement() throws Exception { List<String> result = P16.dropEveryNth(Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"), 3); assertThat(result, hasSize(8)); assertThat(result, contains("a", "b", "d", "e", "g", "h", "j", "k")); } @Test public void shouldReturnSameListWhenNIsLessThanListSize() throws Exception { List<String> result = P16.dropEveryNth(Arrays.asList("a", "b"), 3); assertThat(result, hasSize(2)); assertThat(result, contains("a", "b")); } @Test public void shouldReturnSameListWhenNIsZero() throws Exception { List<String> result = P16.dropEveryNth(Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"), 0); assertThat(result, hasSize(11)); assertThat(result, contains("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k")); }
P17 (*) Split a list into two parts; the length of the first part is given
@Test public void shouldSplitInTwoHalves() throws Exception { Map<Boolean, List<String>> result = P17.split(Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "k"), 3); assertThat(result.get(true), contains("a", "b", "c")); assertThat(result.get(false), contains("d", "e", "f", "g", "h", "i", "k")); }
P18 (**) Extract a slice from a list
@Test public void shouldGiveSliceOfAList() throws Exception { List<String> slice = P18.slice(Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h", "i", "k"), 3, 7); assertThat(slice, hasSize(5)); assertThat(slice, contains("c", "d", "e", "f", "g")); }
P19 (**) Rotate a list N places to the left
@Test public void shouldRotateAListByThreeElementsWhenNIs3() throws Exception { List<String> rotated = P19.rotate(Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h"), 3); assertThat(rotated, equalTo(Arrays.asList("d", "e", "f", "g", "h", "a", "b", "c"))); } @Test public void shouldReturnSameListWhenNIs0() throws Exception { List<String> rotated = P19.rotate(Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h"), 0); assertThat(rotated, equalTo(Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h"))); } @Test public void shouldRotateWhenNIsNegative() throws Exception { List<String> rotated = P19.rotate(Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h"), -2); assertThat(rotated, equalTo(Arrays.asList("g", "h", "a", "b", "c", "d", "e", "f"))); }
P20 (*) Remove the K'th element from a list
@Test public void shouldRemoveKthElementFromList() throws Exception { Object[] result = P20.removeAt(Arrays.asList("a", "b", "c", "d"), 2); assertThat(result[0], equalTo(Arrays.asList("a", "c", "d"))); assertThat(result[1], equalTo("b")); }
P21 (*) Insert an element at a given position into a list
@Test public void shouldInsertElementAtSecondPosition() throws Exception { List<String> input = Stream.of("a", "b", "c", "d").collect(toList()); List<String> result = P21.insertAt(input, 2, "alfa"); assertThat(result, hasSize(5)); assertThat(result, contains("a", "alfa", "b", "c", "d")); } @Test public void shouldInsertElementAtFirstPosition() throws Exception { List<String> input = Stream.of("a", "b", "c", "d").collect(toList()); List<String> result = P21.insertAt(input, 1, "alfa"); assertThat(result, hasSize(5)); assertThat(result, contains("alfa", "a", "b", "c", "d")); } @Test public void shouldInsertElementAtEnd() throws Exception { List<String> input = Stream.of("a", "b", "c", "d").collect(toList()); List<String> result = P21.insertAt(input, 5, "alfa"); assertThat(result, hasSize(5)); assertThat(result, contains("a", "b", "c", "d", "alfa")); }
P22 (*) Create a list containing all integers within a given range
@Test public void shouldCreateARangeBetween4And9() throws Exception { List<Integer> range = P22.range(4, 9); assertThat(range, hasSize(6)); assertThat(range, contains(4, 5, 6, 7, 8, 9)); }
P23 (**) Extract a given number of randomly selected elements from a list
@Test public void shouldReturnAListOfThreeRandomSelectedElements() throws Exception { List<String> result = P23.randomSelect(Arrays.asList("a", "b", "c", "d", "e", "f", "g", "h"), 3); System.out.println(result); assertThat(result, hasSize(3)); }
P24 (*) Lotto: Draw N different random numbers from the set 1..M
Hint: Combine the solutions of problems P22 and P23
@Test public void shouldGive6RandomNumbersFromARangeStartingFrom1To49() throws Exception { List<Integer> randomList = P24.randomSelect(6, 1, 49); assertThat(randomList, hasSize(6)); System.out.println(randomList); // One possible output [47, 30, 36, 38, 11, 1] }
P25 (*) Generate a random permutation of the elements of a list
Hint: Use the solution of problem P23
@Test public void shouldGenerateRandomPermutationOfElementsOfAList() throws Exception { List<String> permutation = P25.randomPermutation(Stream.of("a", "b", "c", "d", "e", "f").collect(toList())); assertThat(permutation, hasSize(6)); assertThat(permutation, containsInAnyOrder("a", "b", "c", "d", "e", "f")); System.out.println(permutation); // one possible output [a, e, f, c, b, d] }
P26 (**) Generate the combinations of K distinct objects chosen from the N elements of a list
@Test public void shouldFindAllCombinationsOfSize3FromAListWithSize6() throws Exception { List<String> input = Stream.of("a", "b", "c", "d", "e", "f").collect(toList()); List<List<String>> combinations = P26.combinations(input, 3); assertThat(combinations, hasSize(20)); }
P27 (**) Group the elements of a set into disjoint subsets
a) In how many ways can a group of 9 people work in 3 disjoint subgroups of 2, 3 and 4 persons? Write a predicate that generates all the possibilities via backtracking
@Test public void shouldGroupIntoThreeGroupsOfSize_2_3_and_4() throws Exception { List<String> input = Stream.of("aldo", "beat", "carla", "david", "evi", "flip", "gary", "hugo", "ida").collect(toList()); List<List<List<String>>> groups = P27.group3(input); assertThat(groups, hasSize(1260)); }
b) Generalize the above predicate in a way that we can specify a list of group sizes and the predicate will return a list of groups.
@Test public void shouldGroupIntoThreeGroupsOfSize_2_2_and_5() throws Exception { List<String> input = Stream.of("aldo", "beat", "carla", "david", "evi", "flip", "gary", "hugo", "ida").collect(toList()); List<List<List<String>>> groups = P27.group(input, Stream.of(2, 2, 5).collect(Collectors.toList())); assertThat(groups, hasSize(756)); }
P28 (**) Sorting a list of lists according to length of sublists
a) We suppose that a list (InList) contains elements that are lists themselves. The objective is to sort the elements of InList according to their length. E.g. short lists first, longer lists later, or vice versa.
@Test public void shouldSortByElementLength() throws Exception { List<List<String>> input = Arrays.asList(Arrays.asList("a", "b", "c"), Arrays.asList("d", "e"), Arrays.asList("f", "g", "h"), Arrays.asList("d", "e"), Arrays.asList("i", "j", "k"), Arrays.asList("m", "n"), Arrays.asList("o")); List<List<String>> result = P28.lsort(input); assertThat(result, is(equalTo(Arrays.asList(Arrays.asList("o"), Arrays.asList("d", "e"), Arrays.asList("d", "e"), Arrays.asList("m", "n"), Arrays.asList("a", "b", "c"), Arrays.asList("f", "g", "h"), Arrays.asList("i", "j", "k"))))); }
b) Again, we suppose that a list (InList) contains elements that are lists themselves. But this time the objective is to sort the elements of InList according to their length frequency; i.e. in the default, where sorting is done in ascending order, lists with rare lengths are placed first, others with a more frequent length come later.
@Test public void shouldSortByLengthFrequency() throws Exception { List<List<String>> input = Arrays.asList(Arrays.asList("a", "b", "c"), Arrays.asList("d", "e"), Arrays.asList("f", "g", "h"), Arrays.asList("d", "e"), Arrays.asList("i", "j", "k", "l"), Arrays.asList("m", "n"), Arrays.asList("o")); List<List<String>> result = P28.lfsort(input); assertThat(result, is(equalTo(Arrays.asList(Arrays.asList("i", "j", "k", "l"), Arrays.asList("o"), Arrays.asList("a", "b", "c"), Arrays.asList("f", "g", "h"), Arrays.asList("d", "e"), Arrays.asList("d", "e"), Arrays.asList("m", "n"))))); }
Arithmetic
P31 (**) Determine whether a given integer number is prime.
@Test public void shouldSay7IsAPrimeNumber() throws Exception { boolean prime = P31.isPrime(7); assertTrue(prime); } @Test public void shouldSay10IsNotAPrimeNumber() throws Exception { boolean prime = P31.isPrime(10); assertFalse(prime); }
P32 (**) Determine the prime factors of a given positive integer.
@Test public void shouldFindPrimeFactorsOf315() throws Exception { List<Integer> primeFactors = P32.primeFactors(315); assertThat(primeFactors, hasItems(3, 3, 5, 7)); } @Test public void shouldFindPrimeFactorsOf33() throws Exception { List<Integer> primeFactors = P32.primeFactors(33); assertThat(primeFactors, hasItems(3, 11)); }
P33 (**) Determine the prime factors of a given positive integer (2).
@Test public void shouldFindPrimeFactorsOf315() throws Exception { List<SimpleEntry<Integer, Integer>> primeFactors = P33.primeFactorsMult(315); assertThat(primeFactors, hasItems(new SimpleEntry<>(3, 2), new SimpleEntry<>(5, 1), new SimpleEntry<>(7, 1))); } @Test public void shouldFindPrimeFactorsOf33() throws Exception { List<SimpleEntry<Integer, Integer>> primeFactors = P33.primeFactorsMult(33); assertThat(primeFactors, hasItems(new SimpleEntry<>(3, 1), new SimpleEntry<>(11, 1))); }
P34 (*) A list of prime numbers
import java.util.stream.IntStream; @Test public void shouldGiveAllPrimeNumbersBetween2And10() throws Exception { List<Integer> primeNumbers = P34.primeNumbers(IntStream.rangeClosed(2, 10)); assertThat(primeNumbers, hasSize(4)); assertThat(primeNumbers, hasItems(2, 3, 5, 7)); } @Test public void shouldGiveAllPrimeNumbersBetween7And31() throws Exception { List<Integer> primeNumbers = P34.primeNumbers(IntStream.rangeClosed(7, 31)); assertThat(primeNumbers, hasSize(8)); assertThat(primeNumbers, hasItems(7, 11, 13, 17, 19, 23, 29, 31)); }
P35 (**) Goldbach's conjecture.
Goldbach's conjecture says that every positive even number greater than 2 is the sum of two prime numbers. Example: 28 = 5 + 23. It is one of the most famous facts in number theory that has not been proved to be correct in the general case. It has been numerically confirmed up to very large numbers. Write a predicate to find the two prime numbers that sum up to a given even integer.
@Test public void _8_isthesumof_3_and_5() throws Exception { List<Integer> numbers = P35.goldbach(8); assertThat(numbers, hasSize(2)); assertThat(numbers, hasItems(3, 5)); } @Test public void _28_isthesumof_5_and_23() throws Exception { List<Integer> numbers = P35.goldbach(28); assertThat(numbers, hasSize(2)); assertThat(numbers, hasItems(5, 23)); }
P36 (**) A list of Goldbach compositions.
Given a range of integers by its lower and upper limit, print a list of all even numbers and their Goldbach composition.
@Test public void shouldProduceAListOfGoldbachCompositions() throws Exception { List<SimpleEntry<Integer, List<Integer>>> compositions = P36.goldbach_list(IntStream.rangeClosed(9, 20)); assertThat(compositions, hasSize(6)); assertThat(compositions, hasItems( new SimpleEntry<>(10, Arrays.asList(3, 7)), new SimpleEntry<>(12, Arrays.asList(5, 7)), new SimpleEntry<>(14, Arrays.asList(3, 11)), new SimpleEntry<>(16, Arrays.asList(3, 13)), new SimpleEntry<>(18, Arrays.asList(5, 13)), new SimpleEntry<>(20, Arrays.asList(3, 17)) )); } @Test public void shouldProduceAListOfGoldbachCompositionsWhereBothPrimeNumbersAreGreaterThan50() throws Exception { List<SimpleEntry<Integer, List<Integer>>> compositions = P36.goldbach_list1(IntStream.rangeClosed(1, 2000), 50); assertThat(compositions, hasSize(4)); assertThat(compositions, hasItems( new SimpleEntry<>(992, Arrays.asList(73, 919)), new SimpleEntry<>(1382, Arrays.asList(61, 1321)), new SimpleEntry<>(1856, Arrays.asList(67, 1789)), new SimpleEntry<>(1928, Arrays.asList(61, 1867)) )); }
P37 (**) Determine the greatest common divisor of two positive integer numbers.
Use Euclid's algorithm.
@Test public void gcdOf36And63Is9() throws Exception { int gcd = P37.gcd(36, 63); assertThat(gcd, equalTo(9)); }
P38 (*) Determine whether two positive integer numbers are coprime.
Two numbers are coprime if their greatest common divisor equals 1.
@Test public void shouldSay35And64IsCoprime() throws Exception { boolean coprime = P38.coprime(35, 64); assertTrue(coprime); }
P39 (**) Calculate Euler's totient function phi(m).
Euler's so-called totient function phi(m) is defined as the number of positive integers r (1 <= r < m) that are coprime to m.
@Test public void shouldSayPhiOf10Is4() throws Exception { long phi = P39.phi(10); assertThat(phi, equalTo(4L)); }
P40 (**) Calculate Euler's totient function phi(m) (2).
See problem P39 for the definition of Euler's totient function. If the list of the prime factors of a number m is known in the form of problem 2.03 then the function phi(m) can be efficiently calculated as follows: Let [[p1,m1],[p2,m2],[p3,m3],...] be the list of prime factors (and their multiplicities) of a given number m. Then phi(m) can be calculated with the following formula:
phi(m) = (p1 - 1) * p1**(m1 - 1) * (p2 - 1) * p2**(m2 - 1) * (p3 - 1) * p3**(m3 - 1) * ...
Note that a**b stands for the b'th power of a.
@Test public void phiOf10Is4() throws Exception { int p = P40.phi(10); assertThat(p, equalTo(4)); } @Test public void phiOf99Is60() throws Exception { int p = P40.phi(99); assertThat(p, equalTo(60)); }
P41 (*) Compare the two methods of calculating Euler's totient function.
Use the solutions of problems P39 and P40 to compare the algorithms. Take the number of logical inferences as a measure for efficiency. Try to calculate phi(10090) as an example.
@Test public void shouldCalculatePhiOf10090UsingP39() throws Exception { long p = P39.phi(10090); assertThat(p, equalTo(4032L)); } @Test public void shouldCalculatePhiOf10090UsingP40() throws Exception { long p = P40.phi(10090); assertThat(p, equalTo(4032L)); }
P46 (**) Truth tables for logical expressions.
Define predicates and/2, or/2, nand/2, nor/2, xor/2, impl/2 and equ/2 (for logical equivalence) which succeed or fail according to the result of their respective operations; e.g. and(A,B) will succeed, if and only if both A and B succeed. Note that A and B can be Prolog goals (not only the constants true and fail).
A logical expression in two variables can then be written in prefix notation, as in the following example: and(or(A,B),nand(A,B)).
Now, write a predicate table/3 which prints the truth table of a given logical expression in two variables.
@Test public void shouldGenerateTruthTable() throws Exception { String table = P46.table((a, b) -> and(a, or(a, b))); String result = "A B result\n" + "true true true\n" + "true false true\n" + "false true false\n" + "false false false"; assertThat(table, is(equalTo(result))); }
P47 (*) Truth tables for logical expressions (2).
Skipping this problem for now.
P48 (**) Truth tables for logical expressions (3).
Skipping this problem for now.
P49 (**) Gray code.
An n-bit Gray code is a sequence of n-bit strings constructed according to certain rules. For example,
n = 1: C(1) = ['0','1'].
n = 2: C(2) = ['00','01','11','10'].
n = 3: C(3) = ['000','001','011','010','110','111','101','100'].
@Test public void shouldFindGrayCodeWhenNIs1() throws Exception { List<String> graySequence = P49.gray(1); assertThat(graySequence, contains("0", "1")); } @Test public void shouldFindGrayCodeWhenNIs2() throws Exception { List<String> graySequence = P49.gray(2); assertThat(graySequence, contains("00", "01", "11", "10")); } @Test public void shouldFindGrayCodeWhenNIs3() throws Exception { List<String> graySequence = P49.gray(3); assertThat(graySequence, contains("000", "001", "011", "010", "110", "111", "101", "100")); }