LeetCode 264. Ugly Number II - custom Ugly Number finding algorithm [duplicate] - java

This question already has answers here:
nᵗʰ ugly number
(13 answers)
Closed 2 years ago.
I am trying to solve the LeetCode ugly number challenge II. I came up with an algorithm of my own, seems working in theory but does not. I want to know why. I implemented this in Java but I write in Python normally, so if you correct the Java code, would appreciate.
The problem statement is:
"Write a program to find the n-th ugly number.
Ugly numbers are positive numbers whose prime factors only include 2,
3, 5.
Example:
Input: n = 10 Output: 12 Explanation: 1, 2, 3, 4, 5, 6, 8, 9, 10, 12
is the sequence of the first 10 ugly numbers. Note:
1 is typically treated as an ugly number. n does not exceed 1690."
This is my code:
class Solution {
public int nthUglyNumber(int n) {
// Gather all the uglies in one place
int[] uglies = new int[n];
// Set first one as one
uglies[0] = 1;
// Start filling
for (int i = 1; i < uglies.length - 1; i++) {
int num = i;
if (isUgly(num) == true) {
uglies[i] = num;
} else {
while (isUgly(num) == false) {
num++;
}
uglies[i] = num;
System.out.println(uglies[i]);
}
}
return uglies[uglies.length - 1];
}
public boolean isUgly(int m) {
boolean ans = false;
// Check if the number is divisible by an integer other than 2,3 or 5
// Simply iterate through numbers smaller than n to find a smaller divisor
for (int i = 2; i < m; i++) {
// If n is divisable by i and i is not a multiple of 2,3 or 5
boolean other_divisor = (m % i == 0) && (i % 2 != 0 || i % 3 != 0 || i % 5 != 0);
if (other_divisor == true) {
ans = false;
} else {
ans = true;
}
}
return ans;
}
}
So I basically made a function isUgly(n), which takes a number and checks if it is ugly by finding out if it has a divisor other than 2,3,5. If there is, then it should not be an ugly number. Than in the main body I go over all integers and check if they are ugly. If they are, I add them to an array until I fill out the n-th position. Would appreciate if corrected.

Not sure where your bug is. But, it has to be solved efficiently (time and space).
These solutions would pass through LeetCode, yet are not the most efficient algorithms for the problem. Since the question is math related, pretty sure there are many ways to make it much efficient.
C++
#include <vector>
class Solution {
public:
int nthUglyNumber(int n) {
int factor_two = 0;
int factor_three = 0;
int factor_five = 0;
std::vector<int> uglies(n);
uglies[0] = 1;
for (int index = 1; index < n; index++) {
uglies[index] = std::min(uglies[factor_five] * 5, std::min(uglies[factor_two] * 2, uglies[factor_three] * 3));
if (uglies[index] == uglies[factor_two] * 2) {
factor_two++;
}
if (uglies[index] == uglies[factor_three] * 3) {
factor_three++;
}
if (uglies[index] == uglies[factor_five] * 5) {
factor_five++;
}
}
return uglies[n - 1];
}
};
Java
public class Solution {
public int nthUglyNumber(int n) {
int[] uglies = new int[n];
uglies[0] = 1;
int indexTwo = 0;
int indexThree = 0;
int indexFive = 0;
int two = 2;
int three = 3;
int five = 5;
for (int index = 1; index < n; index++) {
int minFactor = Math.min(five, Math.min(two, three));
uglies[index] = minFactor;
if (minFactor == two) {
two = 2 * uglies[++indexTwo];
}
if (minFactor == three) {
three = 3 * uglies[++indexThree];
}
if (minFactor == five) {
five = 5 * uglies[++indexFive];
}
}
return uglies[n - 1];
}
}
Python
class Solution:
def nthUglyNumber(self, n: int) -> int:
uglies = (1,)
factor_two = factor_three = factor_five = 0
while len(uglies) < n:
while uglies[factor_two] * 2 <= uglies[-1]:
factor_two += 1
while uglies[factor_three] * 3 <= uglies[-1]:
factor_three += 1
while uglies[factor_five] * 5 <= uglies[-1]:
factor_five += 1
curr_ugly = min(uglies[factor_two] * 2, uglies[factor_three] * 3, uglies[factor_five] * 5)
uglies += (curr_ugly,)
return uglies[-1]
References
For additional details, you can see the Discussion Board. There are plenty of accepted solutions with a variety of languages and explanations, efficient algorithms, as well as asymptotic time/space complexity analysis1, 2 in there.
If you are preparing for interviews:
We would want to write bug-free and clean codes based on standards and conventions (e.g., c1, 2, c++1, 2, java1, 2, c#1, 2, python1, javascript1, go1, rust1). Overall, we would like to avoid anything that might become controversial for interviews.
There are also other similar platforms, which you might have to become familiar with, in case you'd be interviewing with specific companies that would use those platforms.
If you are practicing for contests1:
Just code as fast as you can, almost everything else is very trivial.
For easy questions, brute force algorithms usually get accepted. For interviews, brute force is less desired, especially if the question would be an easy level.
For medium and hard questions, about 90% of the time, brute force algorithms fail mostly with Time Limit Exceeded (TLE) and less with Memory Limit Exceeded (MLE) errors.
Contestants are ranked based on an algorithm explained here.

Related

How to optimize an algorithm with multiple while loops?

I am trying to solve a problem on leetcode.com Ugly Number II.
problem: An ugly number is a positive integer whose prime factors are limited to 2, 3, and 5.
Given an integer n, return the nth ugly number.
example:
Input: n = 10
Output: 12
Explanation: [1, 2, 3, 4, 5, 6, 8, 9, 10, 12] is the sequence of the first 10 ugly numbers.
This is my solution
class Solution {
public int nthUglyNumber(int n) {
int outputNumber = 6;
int temp = 1;
if (n < 7) {
return n;
}
int i = 7;
while (i != (n + 1)) {
outputNumber = outputNumber + 1;
temp = outputNumber;
while (temp % 5 == 0) {
temp = temp / 5;
}
while (temp % 2 == 0) {
temp = temp / 2;
}
while (temp % 3 == 0) {
temp = temp / 3;
}
if (temp == 1) {
i = i + 1;
}
}
return outputNumber;
}
}
this works for small numbers, but when the input is a big number, then I have Time Limit Exceeded
The question is how to optimize this code?
Thank you!
Hint: You're looking for numbers of the form 2a×3b×5c for non-negative integers a, b, c. Instead of looking for ugly numbers, wouldn't it be easier to just generate them?
I used two tricks to make it about twice as fast, but it's still far too slow. I suspect the check-all-integers-for-ugliness approach is hopeless, and you'll find faster approaches in the discussions on LeetCode.
class Solution {
public int nthUglyNumber(int n) {
for (int i = 1; true; i++)
if (1418776204833984375L % (i / (i & -i)) == 0)
if (--n == 0)
return i;
}
}
The two tricks:
i & -i extracts the lowest 1-bit, so dividing by that takes out every factor 2.
1418776204833984375 is 319×513. Every positive int with only factors 3 and 5 divides that, and every other positive int doesn't.
I think the easiest way is to just maintain a collection of ugly numbers that we will need to visit. We start with a collection containing just 1, and then at each step, we remove the lowest value, and add the values found by multiplying our lowest value by 2, by 3, and by 5. Since these may be duplicates (24 = 8 * 3 and 24 = 12 * 2) but we only want them once apiece, our collection should be a Set.
My Java is far too rusty, but here's a JavaScript implementation that you could use as pseudocode:
const ugly = (count) => {
const upcoming = new Set ([1])
const found = []
while (found .length < count) {
const next = Math .min (...upcoming .values ())
found .push (next)
upcoming .delete (next)
upcoming .add (2 * next)
upcoming .add (3 * next)
upcoming .add (5 * next)
}
return found
}
const uglies = ugly (1690) // This was the upper bound specified in the problem
const nthUglyNumber = (n) => uglies [n - 1]
console .log (nthUglyNumber (10))
console .log (nthUglyNumber (1690))
ugly finds the first count ugly numbers, returning them as an Array. Internally, it keeps two collections. upcoming is the ugly numbers we know we would eventually hit. found is an array of numbers we have actually reached, always taking the smallest of the upcoming values. When we select the next one, we remove it from upcoming and add to upcoming its 2-, 3-, and 5-multiples.
This could be modified to be our only function. But it's nice to call it once for our top value, and store the resulting array, as uglies. Then nthUglyNumber is a simple function that extracts the value at the correct index.

How to calculate the probability of getting the sum X using N six-sided dice

The Challenge:
For example, what is the probability of getting the sum of 15 when using 3 six-sided dice. This can be for example by getting 5-5-5 or 6-6-3 or 3-6-6 or many more options.
A brute force solution for 2 dice - with complexity of 6^2:
Assuming we had only 2 six-sided dice, we can write a very basic code like that:
public static void main(String[] args) {
System.out.println(whatAreTheOdds(7));
}
public static double whatAreTheOdds(int wantedSum){
if (wantedSum < 2 || wantedSum > 12){
return 0;
}
int wantedFound = 0;
int totalOptions = 36;
for (int i = 1; i <= 6; i++) {
for (int j = 1; j <= 6; j++) {
int sum = i+j;
if (sum == wantedSum){
System.out.println("match: " + i + " " + j );
wantedFound +=1;
}
}
}
System.out.println("combinations count:" + wantedFound);
return (double)wantedFound / totalOptions;
}
And the output for 7 will be:
match: 1 6
match: 2 5
match: 3 4
match: 4 3
match: 5 2
match: 6 1
combination count:6
0.16666666666666666
The question is how to generalize the algorithm to support N dice:
public static double whatAreTheOdds(int wantedSum, int numberOfDices)
Because we can't dynamically create nested for loops, we must come with a different approach.
I thought of something like that:
public static double whatAreTheOdds(int sum, int numberOfDices){
int sum;
for (int i = 0; i < numberOfDices; i++) {
for (int j = 1; j <= 6; j++) {
}
}
}
but failed to come up with the right algorithm.
Another challenge here is - is there a way to do it efficiently, and not in a complexity of 6^N?
Here is a recursive solution with memoization to count the combinations.
import java.util.Arrays;
import java.lang.Math;
class Dices {
public static final int DICE_FACES = 6;
public static void main(String[] args) {
System.out.println(whatAreTheOdds(40, 10));
}
public static double whatAreTheOdds(int sum, int dices) {
if (dices < 1 || sum < dices || sum > DICE_FACES * dices) return 0;
long[][] mem = new long[dices][sum];
for (long[] mi : mem) {
Arrays.fill(mi, 0L);
}
long n = whatAreTheOddsRec(sum, dices, mem);
return n / Math.pow(DICE_FACES, dices);
}
private static long whatAreTheOddsRec(int sum, int dices, long[][] mem) {
if (dices <= 1) {
return 1;
}
long n = 0;
int dicesRem = dices - 1;
int minFace = Math.max(sum - DICE_FACES * dicesRem, 1);
int maxFace = Math.min(sum - dicesRem, DICE_FACES);
for (int i = minFace; i <= maxFace; i++) {
int sumRem = sum - i;
long ni = mem[dicesRem][sumRem];
if (ni <= 0) {
ni = whatAreTheOddsRec(sumRem, dicesRem, mem);
mem[dicesRem][sumRem] = ni;
}
n += ni;
}
return n;
}
}
Output:
0.048464367913724195
EDIT: For the record, the complexity of this algorithm is still O(6^n), this answer just aims to give a possible implementation for the general case that is better than the simplest implementation, using memoization and search space prunning (exploring only feasible solutions).
As Alex's answer notes, there is a combinatorial formula for this:
In this formula, p is the sum of the numbers rolled (X in your question), n is the number of dice, and s is the number of sides each dice has (6 in your question). Whether the binomial coefficients are evaluated using loops, or precomputed using Pascal's triangle, either way the time complexity is O(n2) if we take s = 6 to be a constant and X - n to be O(n).
Here is an alternative algorithm, which computes all of the probabilities at once. The idea is to use discrete convolution to compute the distribution of the sum of two random variables given their distributions. By using a divide and conquer approach as in the exponentiation by squaring algorithm, we only have to do O(log n) convolutions.
The pseudocode is below; sum_distribution(v, n) returns an array where the value at index X - n is the number of combinations where the sum of n dice rolls is X.
// for exact results using integers, let v = [1, 1, 1, 1, 1, 1]
// and divide the result through by 6^n afterwards
let v = [1/6.0, 1/6.0, 1/6.0, 1/6.0, 1/6.0, 1/6.0]
sum_distribution(distribution, n)
if n == 0
return [1]
else if n == 1
return v
else
let r = convolve(distribution, distribution)
// the division here rounds down
let d = sum_distribution(r, n / 2)
if n is even
return d
else
return convolve(d, v)
Convolution cannot be done in linear time, so the running time is dominated by the last convolution on two arrays of length 3n, since the other convolutions are on sufficiently shorter arrays.
This means if you use a simple convolution algorithm, it should take O(n2) time to compute all of the probabilities, and if you use a fast Fourier transform then it should take O(n log n) time.
You might want to take a look at Wolfram article for a completely different approach, which calculates the desired probability with a single loop.
The idea is to have an array storing the current "state" of each dice, starting will every dice at one, and count upwards. For example, with three dice you would generate the combinations:
111
112
...
116
121
122
...
126
...
665
666
Once you have a state, you can easily find if the sum is the one you are looking for.
I leave the details to you, as it seems a useful learning exercise :)

largest product of 30 consecutive digits

I was given a programming question by a friend where I was given a 1000 character string of numbers. The task is to find the largest product of 30 consecutive digits.
Although my code looks right, the answer seems to come out really low, why is this?
The relevant code for this problem has been provided below.
static String s = "2389748943892"; //... This number is actually 1000 characters.
public static void main(String[] args) {
// TODO Auto-generated method stub
int largest = 0;
for(int i = 0; i < 970; i ) {
String cur = s.substring(i, i 30);
int cur_val = 0;
for(int x = 0; x < 30; x ) {
if(x == 0) {
System.out.println(Integer.parseInt(cur.substring(0, 1)));
cur_val = Integer.parseInt(cur.substring(x, 1));
} else {
cur_val = cur_val * Integer.parseInt(cur.substring(x, x 1));
}
}
if(cur_val > largest) {
largest = cur_val;
}
System.out.println("Largest: " largest);
// should be 8876473335152640000 but is 2013265920
}
}
Edit: Arrgh, I read 'slow' instead of 'low'...
OK, forget about the performance issues, I thought you were speaking of.
Howver, long won't help you either: calculate ln(9^30)/ln(2), and you get little more than 95, thus you need 96 digits. Try Math.BigInteger then!
This is due to the excessive usage of substring (resulting in constructing and destroying new String objects all the time). As you are only interested in single characters, better use s.charAt(n). Parsing is then easy, too: Just subtract '0' from the char you got this way. So you get:
for(int i = 0; i < 970; ++i)
{
int cur_val = 0;
for(int x = i; x < i + 30; ++x)
{
cur_val *= s.charAt(x) - '0';
}
}
/* rest as you had already */
(OK, I left out printing the substring).
Additionally, in the code you posted, there are some syntax errors (substring i, i 30, apparently there is missing a '+') and you did not increment the counter variables (if this happened in your code, too, you would have ended in endless loops - but then you would not have gotten a slow result, you would not have gotten any at all).
You might include some short cuts (e. g. if you find a '0' in the outer loop, you know that the result for the next 30 iterations will be 0 and you could skip these), giving you another speedup.

How can I shave .1 seconds from the runtime of this program?

I'm working on a practice program at InterviewStreet and I have a solution that runs with a time of 5.15xx seconds, while the maximum time allowed for a java solution is 5 seconds. Is there anything I can do with what I've got here to get it under 5 seconds? There's also a limit of 256 MB so as near as I can tell this is both the most time and memory efficient solution to the problem...
edit:
The possible values for N and K are N <= 10^9 and K <= N, which is why I chose to do everything using BigInteger. The maximum number of trials is 10000. So basically, you input the number of trials, then a pair of integer values for each number of trials, and the program computes the three versions of the binomial coefficient for the equation in the second loop. I figured it would be faster to read everything into the arrays, then process the arrays and put the results into a third array to be processed by the third loop because I figured it might be faster that way. I tried doing everything in the same loop and it ran slower.
I've tried three or four different algorithms for calculating the binomial coefficient (nCr - or n choose r, all are different ways of saying the same thing). Some of the algorithms involve a two dimensional array like c[n][k]. This is the only solution I've submitted that didn't come back with some sort of memory error. The answer needs to be output mod (10 ^ 6) + 3 because the answers of nCr * nCr get pretty huge. A sample run of the program is:
3
4 1
5 2
90 13
2
5
815483
Can't run it on a faster machine because it needs to pass on their machine to count, basically I submit the code and they run it against their test cases, and I have no idea what their test case is, just that the inputs are within the bounds given above.
And the program itself:
import java.math.BigInteger;
import java.util.Scanner;
public class Solution {
public BigInteger nCr(int n, int r) {
if (r > n ) {
return BigInteger.ZERO;
}
if (r > n / 2) {
r = n - r;
}
BigInteger result = BigInteger.ONE;
for (int i = 0; i < r; i++) {
result = result.multiply(BigInteger.valueOf(n - i));
result = result.divide(BigInteger.valueOf(i + 1));
}
return result;
}
public static void main(String[] args) {
Scanner input = new Scanner( System.in );
BigInteger m = BigInteger.valueOf(1000003);
Solution p = new Solution();
short T = input.nextShort(); // Number of trials
BigInteger intermediate = BigInteger.ONE;
int[] r = new int[T];
int[] N = new int[T];
int[] K = new int[T];
short x = 0;
while (x < T) {
N[x] = input.nextInt();
K[x] = input.nextInt();
x++;
}
x = 0;
while (x < T) {
if (N[x] >= 3) {
r[x] = ((p.nCr(N[x] - 3, K[x]).multiply(p.nCr(N[x] + K[x], N[x] - 1))).divide(BigInteger.valueOf((N[x] + K[x]))).mod(m)).intValue();
} else {
r[x] = 0;
}
x++;
}
x = 0;
while (x < T) {
System.out.println(r[x]);
x++;
}
}
}
Not entirely sure what the algorithm is trying to accomplish but I'm guessing something with binomial coefficients based on your tagging of the post.
You'll need to check if my suggestion modifies the result but it looks like you could merge two of your while loops:
Original:
while (x < T) {
N[x] = input.nextInt();
K[x] = input.nextInt();
x++;
}
x = 0;
while (x < T) {
if (N[x] >= 3) {
r[x] = ((p.nCr(N[x] - 3, K[x]).multiply(p.nCr(N[x] + K[x], N[x] - 1))).divide(BigInteger.valueOf((N[x] + K[x]))).mod(m)).intValue();
} else {
r[x] = 0;
}
x++;
}
New:
x = 0;
while (x < T) {
//this has been moved from your first while loop
N[x] = input.nextInt();
K[x] = input.nextInt();
if (N[x] >= 3) {
r[x] = ((p.nCr(N[x] - 3, K[x]).multiply(p.nCr(N[x] + K[x], N[x] - 1))).divide(BigInteger.valueOf((N[x] + K[x]))).mod(m)).intValue();
} else {
r[x] = 0;
}
x++;
}
try to run using a profilerm for example the jvisualvm and run this class with
-Dcom.sun.management.jmxremote
attach to the process and start a profile.

Code works extremely slowly and does not print output

I'm working on Project Euler problem #2:
Each new term in the Fibonacci sequence is generated
by adding the previous two terms. By
starting with 1 and 2, the first 10 terms will be:
1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
Find the sum of all the even-valued terms in the sequence which do not exceed four million.
My code:
public class Two {
public static void main(String[] args) {
Two obj = new Two();
int sum = 0, i = 1;
while (obj.fibonacci(i) < 4000001) {
if (obj.fibonacci(i) % 2 == 0) {
sum += obj.fibonacci(i);
i++;
}
}
System.out.println(sum);
}
public int fibonacci(int n) {
if (n == 0) {
return -1;
}
if (n == 1) {
return 1;
}
if (n == 2) {
return 3;
} else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
}
Please help me that what is wrong with this code that when I run it. It doesn't show the output on the console and the total time will be over than 5 minutes
Thanks
You're stuck in an infinite loop there as you're only increasing i when its mod 2 is equal to 0. You need to move your i++ lower.
while (obj.fibonacci(i) <= 4000000) {
if (obj.fibonacci(i) % 2 == 0) {
sum += obj.fibonacci(i);
}
i++;
}
As other comments have metioned, this isn't the best way to solve the fibonacci problem, but it solves your error/problem. You should walk this through a debugger if you don't see why and you'll notice you use a lot of recursive calls which have already been solved. Since you're calling it numerous times in the code, (in the while statement and in the if statement) you've increased your processing time.
Here is a sample of your fibonacci calls, notice how you call the fibonacci method on the same number multiple times:
1
2
3
2
1
4
3
2
1
2
5
As mentioned, the i++ needs to be moved outside the check for eveness or you'll be stuck in a loop.
But you have a slightly bigger problem. The fibonacci sequence starts with
...1, 2, 3, ...
where instead you have ...1, 3, ... which means you get incorrect results. You should have:
// ...
if (n == 2) {
return 2;
// ...
Although you solution might work, it is quite expensive as it recalculates results already obtained.
Using recursion in this case, to have the value of fibonacci(4), you recursively add the values of fibonacci(3) and fibonacci(2), which you already calculated previously.
Try with storing your values in a list instead of recomputing all the time:
List<Long> fibonacci = new ArrayList<Long>();
// First terms
fibonacci.add(-1L); // 0 is dummy, sequence starts at 1
fibonacci.add(1L);
fibonacci.add(2L);
for (int i = 3; fibonacci.get(i - 1) + fibonacci.get(i - 2) < 4000001; i++) {
long u = fibonacci.get(i - 1) + fibonacci.get(i - 2);
fibonacci.add(i, u);
}
Using this technique, you can compute the Fibonacci sequence up to 4000000 in less than 2 seconds (as I tried on my computer).
Then, just add some code to compute the sum inside the loop :-)
One of your problems is that you're excessively using recursion. You should try to store results to avoid to recalculate everything every time.
There's no reason to store the whole sequence of Fibonacci numbers in this case. You can simply "walk" along the sequence with a few local variables, summing as you go.
int fib2 = 0, fib1 = 1, fib0 = fib1 + fib2;
int sum = 0;
while (fib0 <= N)
{
if (fib0 % 2 == 0) sum += fib0;
fib2 = fib1;
fib1 = fib0;
fib0 = fib1 + fib2;
}
An improvement on #Blastfurnace's solution is to note that every third value is even.
public static void main(String[] args) {
long sum = 0;
int runs = 30000;
for (int i=0;i< runs;i++) {
sum = sumEvenFib();
}
long start = System.nanoTime();
for (int i=0;i< runs;i++) {
sum = sumEvenFib();
}
long time = System.nanoTime() - start;
System.out.println(sum+" took "+time/runs+" ns avg");
}
private static long sumEvenFib() {
int sum = 0;
for(int f1 = 1, f2 = 2;f2 < 4000001;) {
sum += f2;
int f3 = f1 + f2;
f1 = f3 + f2;
f2 = f1 + f3;
}
return sum;
}
On my old labtop this takes about 40 ns. or 0.000000040 seconds.
I think you can improve fibonacci next way:
def fib(x)
if(x==0 or x==1) then
return x;
end
a,b = 0,1
(x-1).times{ a,b = b,a+b; }
return b;
end
In other words convert recursion to iteration.
I think the question in already ambiguous.
The sum of all even valued should be below 4 million, or should the biggest even valued number be below 4 million?

Categories

Resources