patternjavaMinor
Vampire number generator
Viewed 0 times
vampirenumbergenerator
Problem
Inspired by Reddit r/dailyprogrammer
A vampire number v is a number v=xy with an even number n of digits formed by multiplying a pair of n/2-digit numbers (where the digits are taken from the original number in any order) x and y together. Pairs of trailing zeros are not allowed. If v is a vampire number, then x and y are called its "fangs."
EDIT FOR CLARITY Vampire numbers were original 2 two-digit number (fangs) that multiplied to form a four digit number. We can extend this concept to an arbitrary number of two digit numbers. For this challenge we'll work with three two-digit numbers (the fangs) to create six digit numbers with the same property - we conserve the digits we have on both sides of the equation.
As always, looking for general feedback over code readability, structure and efficiency. But also I was looking to make the code a bit more flexible.
It works as of now but can only g
A vampire number v is a number v=xy with an even number n of digits formed by multiplying a pair of n/2-digit numbers (where the digits are taken from the original number in any order) x and y together. Pairs of trailing zeros are not allowed. If v is a vampire number, then x and y are called its "fangs."
EDIT FOR CLARITY Vampire numbers were original 2 two-digit number (fangs) that multiplied to form a four digit number. We can extend this concept to an arbitrary number of two digit numbers. For this challenge we'll work with three two-digit numbers (the fangs) to create six digit numbers with the same property - we conserve the digits we have on both sides of the equation.
As always, looking for general feedback over code readability, structure and efficiency. But also I was looking to make the code a bit more flexible.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Created on 6/29/2016.
*
* Inspired by r/dailyprogrammer
* https://www.reddit.com/r/dailyprogrammer/comments/3moxid/20150928_challenge_234_easy_vampire_numbers/
*
*/
public class VampireNumbers {
/**
* Calls generate function then displays return value.
* @param args Not used in this application.
*/
public static void main(String args[]){
generate().forEach(System.out::println); // Generates vampires, displays them on screen.
}
/**
* Generates list of vampire numbers. Currently only supports 3 fangs of length 2.
* @return List of vampires generated along with their fangs.
*/
private static List generate(){
ArrayList vampires = new ArrayList<>();
for (int i = 10; i sortVampire(String vampire){
List list = new ArrayList<>();
for (int i = 0; i < vampire.length(); i++){
list.add(vampire.charAt(i));
}
Collections.sort(list);
return list;
}
}It works as of now but can only g
Solution
Also I had a question over this line specifically:
Instead of concatenating all three fangs, would it be worth it to use
a StringBuilder in this scenario?
Not in this case. The compiler is smart enough to replace the concatenation of the String with the use of a
For 3 fangs of length 2, your code is straight-forward and will be performant. A couple of notes:
To support arbitrary
It takes as input the
A sample code would be:
Some notes in this code:
if (sortVampire(fangOne + fangTwo + fangThree).equals(sortVampire(vampire)))Instead of concatenating all three fangs, would it be worth it to use
a StringBuilder in this scenario?
Not in this case. The compiler is smart enough to replace the concatenation of the String with the use of a
StringBuilder. Using a StringBuilder to create a String from multiple concatenation is typically useful when it is done inside a loop.For 3 fangs of length 2, your code is straight-forward and will be performant. A couple of notes:
- There is a mathematical property that could be used to further improve performance. For bigger lengths, it can improve considerably the performance.
- To determine whether all of the fangs have the same digits as their product, you are currently creating a list of characters and sorting those lists to test for equality. You could do that in a simpler and faster way without sorting anything.
To support arbitrary
vampireLength, i.e. an arbitrary count of numbers to multiply to make up the final vampire number, we can resort to a recursive algorithm. For that, we will define a method generateList generate(int fangLength, int vampireLength, int... previousNumbers)It takes as input the
fangLength (count of digits in each fang), the vampireLength (count of fangs) and the previousVampires (array of all the numbers considered so far). This previousNumbers will be sorted ascendingly by construction. It returns the list of all the solutions.- The base case is when
vampireLengthis 0: it means thatpreviousNumberscontains all the numbers to decide whether their product makes a vampire number. In this case, we perform the multiplication of all those numbers and compare the digits together: if they are the same then we can build the String representing the solution and return it in a singleton list; if they are not, we can return an empty list.
- In the recursive case, we need to loop over all the possible numbers. First, the upper bound to consider: with a count of digits
fangLengthto consider, the upper bound is simply \$10^{\text{fangLength}}\$. Then, the lower bound: if there were no previous numbers then it is 10 (the starting point); else it is the last consideredpreviousNumbers(so the greater). For each of those possible numbersi, we concatenate all the solutions by invoking the generate method again, addingito thepreviousNumbers.
A sample code would be:
private static List generate(int fangLength, int vampireLength, int... previousNumbers) {
if (vampireLength == 0) {
int product = Arrays.stream(previousNumbers).reduce(1, (a, b) -> a * b);
if (sameDigits(product, previousNumbers)) {
String op = Arrays.stream(previousNumbers).mapToObj(String::valueOf).collect(Collectors.joining(" * "));
return Collections.singletonList(op + " = " + product);
}
return Collections.emptyList();
}
List list = new ArrayList<>();
int end = (int) Math.pow(10, fangLength);
int start = previousNumbers.length == 0 ? 10 : previousNumbers[previousNumbers.length - 1];
for (int i = start; i String.valueOf(i).chars()).forEach(c -> array[c - '0']++);
String.valueOf(target).chars().forEach(c -> array[c - '0']--);
return Arrays.stream(array).allMatch(i -> i == 0);
}Some notes in this code:
- It is using the Stream API to make the code more fluent: the product is calculated by reducing over the array of numbers and multiplying each of them together; the
Stringsolution is build by joining all the numbers, converted to aString, separated by" * ".
- The
sameDigitsmethod was rewritten to implement the simpler algorithm of checking whether the digits are the same.
Code Snippets
if (sortVampire(fangOne + fangTwo + fangThree).equals(sortVampire(vampire)))List<String> generate(int fangLength, int vampireLength, int... previousNumbers)private static List<String> generate(int fangLength, int vampireLength, int... previousNumbers) {
if (vampireLength == 0) {
int product = Arrays.stream(previousNumbers).reduce(1, (a, b) -> a * b);
if (sameDigits(product, previousNumbers)) {
String op = Arrays.stream(previousNumbers).mapToObj(String::valueOf).collect(Collectors.joining(" * "));
return Collections.singletonList(op + " = " + product);
}
return Collections.emptyList();
}
List<String> list = new ArrayList<>();
int end = (int) Math.pow(10, fangLength);
int start = previousNumbers.length == 0 ? 10 : previousNumbers[previousNumbers.length - 1];
for (int i = start; i < end; i++) {
int[] currentVampires = Arrays.copyOf(previousNumbers, previousNumbers.length + 1);
currentVampires[currentVampires.length - 1] = i;
list.addAll(generate(fangLength, vampireLength - 1, currentVampires));
}
return list;
}
private static boolean sameDigits(int target, int... numbers) {
int[] array = new int[10];
Arrays.stream(numbers).flatMap(i -> String.valueOf(i).chars()).forEach(c -> array[c - '0']++);
String.valueOf(target).chars().forEach(c -> array[c - '0']--);
return Arrays.stream(array).allMatch(i -> i == 0);
}Context
StackExchange Code Review Q#133726, answer score: 3
Revisions (0)
No revisions yet.