|
1 |
| -// Ugly numbers are numbers whose only prime factors are 2, 3 or 5. The sequence 1, 2, 3, 4, 5, 6, 8, 9, 10, 12, 15, … shows the first 11 ugly numbers. |
2 |
| -// By convention, 1 is included. |
3 |
| -// A program to find the nth Ugly number |
4 |
| -// Algorithm : |
5 |
| -// Initialize three-pointers two, three, and five pointing to zero. |
6 |
| -// Take 3 variables nm2, nm3, and nm5 to keep track of next multiple of 2,3 and 5. |
7 |
| -// Make an array of size n to store the ugly numbers with 1 at 0th index. |
8 |
| -// Initialize a variable next which stores the value of the last element in the array. |
9 |
| -// Run a loop n-1 times and perform steps 6,7 and 8. |
10 |
| -// Update the values of nm2, nm3, nm5 as ugly[two]*2, ugly[three]*3, ugly[5]*5 respectively. |
11 |
| -// Select the minimum value from nm2, nm3, and nm5 and increment the pointer related to it. |
12 |
| -// Store the minimum value in variable next and array. |
13 |
| -// Return next. |
14 | 1 | package com.thealgorithms.maths;
|
15 | 2 |
|
16 |
| -import java.util.*; |
| 3 | +import java.util.HashMap; |
| 4 | +import java.util.ArrayList; |
| 5 | +import java.util.Arrays; |
| 6 | +import java.lang.IllegalArgumentException; |
17 | 7 |
|
18 |
| -class NthUglyNumber { |
19 | 8 |
|
20 |
| - /* Function to get the nth ugly number*/ |
21 |
| - public long getNthUglyNo(int n) { |
22 |
| - long[] ugly = new long[n]; |
23 |
| - int two = 0, three = 0, five = 0; |
24 |
| - long nm2 = 2, nm3 = 3, nm5 = 5; |
25 |
| - long next = 1; |
| 9 | +/** |
| 10 | + * @brief class computing the n-th ugly number (when they are sorted) |
| 11 | + * @details the ugly numbers with base [2, 3, 5] are all numbers of the form 2^a*3^b^5^c, |
| 12 | + * where the exponents a, b, c are non-negative integers. |
| 13 | + * Some properties of ugly numbers: |
| 14 | + * - base [2, 3, 5] ugly numbers are the 5-smooth numbers, cf. https://oeis.org/A051037 |
| 15 | + * - base [2, 3, 5, 7] ugly numbers are 7-smooth numbers, cf. https://oeis.org/A002473 |
| 16 | + * - base [2] ugly numbers are the non-negative powers of 2, |
| 17 | + * - the base [2, 3, 5] ugly numbers are the same as base [5, 6, 2, 3, 5] ugly numbers |
| 18 | + */ |
| 19 | +public class NthUglyNumber { |
| 20 | + ArrayList<Long> uglyNumbers = new ArrayList<>(Arrays.asList(1L)); |
| 21 | + final int[] baseNumbers; |
| 22 | + HashMap<Integer, Integer> positions = new HashMap<>(); |
26 | 23 |
|
27 |
| - ugly[0] = 1; |
| 24 | + /** |
| 25 | + * @brief initialized the object allowing to compute ugly numbers with given base |
| 26 | + * @param baseNumbers the given base of ugly numbers |
| 27 | + * @exception IllegalArgumentException baseNumber is empty |
| 28 | + */ |
| 29 | + NthUglyNumber(int[] baseNumbers) { |
| 30 | + if (baseNumbers.length == 0) { |
| 31 | + throw new IllegalArgumentException("baseNumbers must be non-empty."); |
| 32 | + } |
28 | 33 |
|
29 |
| - for (int i = 1; i < n; i++) { |
30 |
| - next = Math.min(nm2, Math.min(nm3, nm5)); |
| 34 | + this.baseNumbers = baseNumbers; |
| 35 | + for (final var baseNumber : baseNumbers) { |
| 36 | + this.positions.put(baseNumber, 0); |
| 37 | + } |
| 38 | + } |
31 | 39 |
|
32 |
| - ugly[i] = next; |
33 |
| - if (next == nm2) { |
34 |
| - two = two + 1; |
35 |
| - nm2 = ugly[two] * 2; |
36 |
| - } |
37 |
| - if (next == nm3) { |
38 |
| - three = three + 1; |
39 |
| - nm3 = ugly[three] * 3; |
40 |
| - } |
41 |
| - if (next == nm5) { |
42 |
| - five = five + 1; |
43 |
| - nm5 = ugly[five] * 5; |
| 40 | + /** |
| 41 | + * @param n the zero-based-index of the queried ugly number |
| 42 | + * @exception IllegalArgumentException n is negative |
| 43 | + * @return the n-th ugly number (starting from index 0) |
| 44 | + */ |
| 45 | + public Long get(int n) { |
| 46 | + if (n < 0) { |
| 47 | + throw new IllegalArgumentException("n must be non-negative."); |
| 48 | + } |
| 49 | + |
| 50 | + while (uglyNumbers.size() <= n) { |
| 51 | + addUglyNumber(); |
| 52 | + } |
| 53 | + |
| 54 | + return uglyNumbers.get(n); |
| 55 | + } |
| 56 | + |
| 57 | + private void addUglyNumber() { |
| 58 | + uglyNumbers.add(computeMinimalCandidate()); |
| 59 | + updatePositions(); |
| 60 | + } |
| 61 | + |
| 62 | + private void updatePositions() { |
| 63 | + final var lastUglyNumber = uglyNumbers.get(uglyNumbers.size() - 1); |
| 64 | + for (final var baseNumber : baseNumbers) { |
| 65 | + if (computeCandidate(baseNumber) == lastUglyNumber) { |
| 66 | + positions.put(baseNumber, positions.get(baseNumber) + 1); |
44 | 67 | }
|
45 | 68 | }
|
46 |
| - return next; |
47 | 69 | }
|
48 | 70 |
|
49 |
| - public static void main(String[] args) { |
50 |
| - Scanner sc = new Scanner(System.in); |
51 |
| - System.out.println("Enter the value of n : "); |
52 |
| - int n = sc.nextInt(); |
53 |
| - NthUglyNumber ob = new NthUglyNumber(); |
54 |
| - long ugly = ob.getNthUglyNo(n); |
55 |
| - System.out.println("nth Ugly number is : " + ugly); |
| 71 | + private long computeCandidate(int candidateBase) { |
| 72 | + return candidateBase * uglyNumbers.get(positions.get(candidateBase)); |
| 73 | + } |
| 74 | + |
| 75 | + private long computeMinimalCandidate() { |
| 76 | + long res = Long.MAX_VALUE; |
| 77 | + for (final var baseNumber : baseNumbers) { |
| 78 | + res = Math.min(res, computeCandidate(baseNumber)); |
| 79 | + } |
| 80 | + return res; |
56 | 81 | }
|
57 | 82 | }
|
0 commit comments