Can this be done Recursively? - java

So me and my friend tried to code this little game when we were children called LOVERS..
Wherein you write down the name of 2 persons,Whole name without the middle name,and count the number of L's,O's,V's,E's,R's,and S's in the name, add it together and put beside the letters.
Sample:
name 1: Hello
name 2: Care
L: 2
O: 1
V: 0
E: 2
R: 1
S: 0
afterwards you will add them in pairs.
Sample:
L: 2 > 3 > 4 > 7 > 15 > 32
O: 1 > 1 > 3 > 8 > 17
V: 0 > 2 > 5 > 9
E: 2 > 3 > 4
R: 1 > 1
S: 0
here's how it goes...first you add the values of the first 2 letters...LO then OV then VE and so on and so forth. until you get one final answer in this case 32....the 32 signifies the percentage in which the 2 people is Compatible with each other.
i know its quite stupid. haha but we just tried to program it for fun. we are 2nd year IT Students here in the philppines. anyway we were wondering if there's a way to do the calculation RECURSIVELY and if there's a way to reduce the number of Arrays used.
Here's our code:
import java.util.*;
public class LOVERS {
static Scanner console = new Scanner(System.in);
public static void main(String[] args) {
String name1="";
String name2="";
char love[] = {'L','O','V','E','R','S'};
int[] lovers = new int[6];
int[] temp= new int[6];
int[] temp2= new int[6];
boolean done = true;
while(done){
name1 = getName();
name2 = getName();
temp = getLetterCount(name1);
temp2 = getLetterCount(name2);
lovers = sumOfLetters(temp,temp2,love);
System.out.println("");
int[] firstLayer = new int[5];
int[] secondLayer = new int[4];
int[] thirdLayer = new int[3];
int[] fourthLayer = new int[2];
firstLayer = sums(lovers);
secondLayer = sums(firstLayer);
thirdLayer = sums(secondLayer);
fourthLayer = sums(thirdLayer);
int output = fourthLayer[0]+fourthLayer[1];
if(output>100){
output=100;
}
System.out.println("Result is : "+ output +"%");
System.out.println("Do you want to try again? Y/N :");
char again = ' ';
if(again == 'n')
{
done = false;
}
else done = true;
}
}
public static int[] sums (int[] y){
int[] x = new int[y.length-1];
for(int ctr=1;ctr<y.length;ctr++){
x[ctr-1]=y[ctr-1]+y[ctr];
}
return x;
}
public static String getName(){
String n="";
System.out.println("Enter name: ");
n = console.nextLine();
n = n.toUpperCase();
return n;
}
public static int[] sumOfLetters(int[] temp, int[] temp2, char[] love){
int[] lovers = new int[6];
for(int ctr=0;ctr<6;ctr++){
lovers[ctr]=temp[ctr]+temp2[ctr];
System.out.println(love[ctr]+" - "+lovers[ctr]);
}
return lovers;
}
public static int[] getLetterCount(String n){
int[] temp = new int[6];
for(int x=0;x<n.length();x++){
if(n.charAt(x)=='L'){
temp[0]++;
}
else if(n.charAt(x)=='O'){
temp[1]++;
}
else if(n.charAt(x)=='V'){
temp[2]++;
}
else if(n.charAt(x)=='E'){
temp[3]++;
}
else if(n.charAt(x)=='R'){
temp[4]++;
}
else if(n.charAt(x)=='S'){
temp[5]++;
}
}
return temp;
}
}
as you can see we used 4 arrays for the 4 layers of calculation and we used a looping statement for the calculation.
So can this be done RECURSIVELY? and How can we reduce the number of arrays used?
this can help us greatly in learning how to do proper Recursive functions since we are currently learning Data Structures. hope you guys can help me. thanks

Yes, of course you can code it recursively.
First of all, your sum-fn. Instead of going through the string byte for byte, you can pass the string to the same function over and over again, just removing one character each time. That character will be added to your result-number. Your final-check will be that the string is empty, then you return null. Evaluation will go back up the recursion, potentially adding 1 (or 0 otherwise) for each character in the string.
For clearer, more readable code you should use enums instead of byte-arrays for storing your ints.
Also, instead of static functions, make it a class in which you can access the attributes.
For the summation of the 6 chars, each level does the same operation on it. So each function call should do that addition and return the result being called in the function again. Your final-check is that only the first integer value positive. If all the other values are 0 the first one holds your sum.

Yes, you can do it recursively:
public static int L(int i) {
return (i == 0) ? LVAL : L(i - 1) + O(i - 1);
}
public static int O(int i) {
return (i == 0) ? OVAL : O(i - 1) + V(i - 1);
}
public static int V(int i) {
return (i == 0) ? VVAL : V(i - 1) + E(i - 1);
}
public static int E(int i) {
return (i == 0) ? EVAL : E(i - 1) + R(i - 1);
}
public static int R(int i) {
return (i == 0) ? RVAL : R(i - 1) + SVAL;
}
Calling L(5) gives you the answer.

Actually the problem here is one instance of a much more general class of problems/algorithms which are incidentally quite important in some fields nobody would believe from this example ;)
Basically you can regard your triangle above as a matrix. Ie the second number in the L row (the 3) would be (0,1), 3rd value in the E row would be (3,2). If you look at it you see that every value except the start values depend on exactly two other nodes, which makes this a 2-point stencil. There are some extremely intriguing algorithms out there for this kind of problem - eg a cache oblivious, parallel algorithm for higher-order stencils (LBMHD uses 13-points or something).
Anyways that stuff is completely out of your league I fear (don't ask me about details either~) - there are even more or less recent papers about this ;)
PS: And my personal small implementation. Can't get much simpler and has the typical structure
of a simple recursive program. You call it with getVal(0, 5); or more generally getVal(0, startVals.length - 1). Just think about it as working backwards from the solution to the start. We want to know what the right field in the first row has. To get this we need to know two the values of two other fields which we have to compute first in the same manner. This is done until we get to a field where we already know the result - ie our start values.
private int[] startVals; // contains start values for all 6 letters.
// for your example startVals[0] == 2; startVals[5] == 0
public int getVal(int row, int col) {
if (col == 0) return startVals[row];
return getVal(row, col-1) + getVal(row + 1, col - 1);
}

Related

ProjectEuler solution returns incorrect answer

The problem I'm trying to solve comes from ProjectEuler.
Some integers have following property:
n + reverse(n) = a number consisting entirely of odd digits.
For example:
14: 14 + 41 = 55
Numbers starting or ending with 0 aren't allowed.
How many of these "reversible" numbers are there below 10^9?
The problem also gives a hint:
there are 120 such numbers below 1000.
I'm quite new to Java, and I tried to solve this problem by writing a program that checks all the numbers up to a billion, which is not the best way, I know, but I'm ok with that.
The problem is that my program gives out a wrong amount of numbers and I couldn't figure out why! (The code will most likely contain some ugly things, feel free to improve it in any way)
int result = 0;
boolean isOdd = true;
boolean hasNo0 = true;
public int reverseNumber(int r) //this method should be working
{ //guess the main problem is in the second method
int n = 0;
String m = "";
if (r % 10 == 0) { hasNo0 = false; }
while (r > 0){
n = r % 10;
m = String.valueOf(m+n);
r /= 10;
}
result = Integer.parseInt(m);
return result;
}
public void isSumOdd(int max)
{
int number = 1;
int sum = 0;
Sums reverseIt = new Sums();
int amount = 0;
while (number <= max)
{
sum = reverseIt.reverseNumber(number) + number;
while (sum > 0)
{
int x = sum % 10;
if (x % 2 == 0) { isOdd = false; }
sum /= 10;
}
if (isOdd && hasNo0) { amount++; }
number++;
isOdd = true;
hasNo0 = true;
}
System.out.println(amount);
}
Called by
Sums first = new Sums();
first.reversibleNumbers(1000000000);
The most important problem in your code is the following line:
sum = reverseIt.reverseNumber(number) + number;
in isSumOdd(int max) function. Here the reverseIt object is a new instance of Sums class. Since you are using Sums member data (the boolean variables) to signal some conditions when you use the new instance the value of these member variables is not copied to the current caller object. You have to change the line to:
sum = this.reverseNumber(number) + number;
and remove the Sums reverseIt = new Sums(); declaration and initialization.
Edit: Attempt to explain why there is no need to instantiate new object instance to call a method - I've found the following answer which explains the difference between a function and a (object)method: https://stackoverflow.com/a/155655/25429. IMO the explanation should be enough (you don't need a new object because the member method already has access to the member data in the object).
You overwrite odd check for given digit when checking the next one with this code: isOdd = false;. So in the outcome you check only whether the first digit is odd.
You should replace this line with
idOdd = idOdd && (x % 2 == 0);
BTW. You should be able to track down an error like this easily with simple unit tests, the practice I would recommend.
One of the key problems here is that your reverseNumber method does two things: check if the number has a zero and reverses the number. I understand that you want to ignore the result (or really, you have no result) if the number is a multiple of 10. Therefore, you have two approaches:
Only send numbers into reverseNumber if they are not a multiple of 10. This is called a precondition of the method, and is probably the easiest solution.
Have a way for your method to give back no result. This is a popular technique in an area of programming called "Functional Programming", and is usually implemented with a tool called a Monad. In Java, these are implemented with the Optional<> class. These allow your method (which always has to return something) to return an object that means "nothing at all". These will allow you to know if your method was unable or unwilling to give you a result for some reason (in this case, the number had a zero in it).
I think that separating functionnalities will transform the problem to be easier. Here is a solution for your problem. Perhaps it isn't the best but that gives a good result:
public static void main(final String [] args) {
int counter = 0;
for (int i = 0; i < 20; i++) {
final int reversNumber = reverseNumber(i);
final int sum = i + reversNumber;
if (hasNoZeros(i) && isOdd(sum)) {
counter++;
System.out.println("i: " + i);
System.out.println("r: " + reversNumber);
System.out.println("s: " + sum);
}
}
System.out.println(counter);
}
public static boolean hasNoZeros(final int i){
final String s = String.valueOf(i);
if (s.startsWith("0") || s.endsWith("0")) {
return false;
}
return true;
}
public static int reverseNumber(final int i){
final StringBuilder sb = new StringBuilder(String.valueOf(i));
return Integer.parseInt(sb.reverse().toString());
}
public static boolean isOdd(final int i){
for (final char s : String.valueOf(i).toCharArray()) {
if (Integer.parseInt(String.valueOf(s))%2 == 0) {
return false;
}
}
return true;
}
the output is:
i: 12
r: 21
s: 33
i: 14
r: 41
s: 55
i: 16
r: 61
s: 77
i: 18
r: 81
s: 99
4
Here is a quick working snippet:
class Prgm
{
public static void main(String args[])
{
int max=(int)Math.pow(10, 3); //change it to (10, 9) for 10^9
for(int i=1;i<=max;i++)
{
if(i%10==0)
continue;
String num=Integer.toString(i);
String reverseNum=new StringBuffer(num).reverse().toString();
String sum=(new Long(i+Long.parseLong(reverseNum))).toString();
if(sum.matches("^[13579]+$"))
System.out.println(i);
}
}
}
It prints 1 number(satisfying the condition) per line, wc is word count linux program used here to count number of lines
$javac Prgm.java
$java Prgm
...//Prgm outputs numbers 1 per line
$java Prgm | wc --lines
120

Checksums - ISBN program

This problem has me puzzled. I tried using a loop like this: Basically I tried to get the first digit from the input and do the formula but it doesn't seem to work. It looks so simple but I can't figure it out. Could you help me? Thanks.
public static int ISBN(String ninedigitNum) {
number = 9;
while (number > 0) {
int nextDigit = ninedigitNum.substring(0,1);
...
}
Checksums (Source: Princeton University). The International Standard
Book Number (ISBN) is a 10 digit code that uniquely specifies a book.
The rightmost digit is a checksum digit which can be uniquely
determined from the other 9 digits from the condition that d1 + 2d2 +
3d3 + ... + 10d10 must be a multiple of 11 (here di denotes the ith
digit from the right). The checksum digit d1 can be any value from 0
to 10: the ISBN convention is to use the value X to denote 10.
Example: the checksum digit corresponding to 020131452 is 5 since is
the only value of d1 between 0 and and 10 for which d1 + 2*2 + 3*5 +
4*4 + 5*1 + 6*3 + 7*1 + 8*0 + 9*2 + 10*0 is a multiple of 11. Create a
Java method ISBN() that takes a 9-digit integer as input, computes the
checksum, and returns the 10-digit ISBN number. Create 3 JUnit test
cases to test your method.
I got it, thanks a lot everyone!
What about it isn't working? Either way, I believe what you're missing is that you're continually getting the same substring, which will be the first number of the string: int nextDigit = ninedigitNum.substring(0,1);. In addition, you're going to want to use an int, not a String; you can technically convert from String to int if desired, but the problem itself calls for an int.
There are two ways to do this that jump to mind. I would do this by realizing that mod in powers of 10 will give you the respective digit of an integer, but the easier way is to convert to a char array and then access directly. Note that there's no error checking here; you'll have to add that yourself. In addition, there are a LOT of 'magic numbers' here: good code typically has very, very few. I would recommend learning more data structures before attempting problems like these; to be honest there's very few things you can do without at least arrays and linked lists.
char[] ISBN = ninedigitNum.toCharArray();
//Process each number
int total = 0;
for(int i=0; i<9; i++){
int current_int = Integer.parseInt(ISBN[i]);
total += current_int * (10 - i)
}
//Find value of d1
for(int i=0; i<9; i++){
if(((total + i) % 11) == 0){
total += i*100000000;
break;
}
}
return total;
In general: Use print outs with System.out.println(x); or use your compiler's debugger to see what's going on during processing.
So,
This is the piece of code that I wrote. I still think it could be made more efficient.
public class Problem3 {
public static String ISBN(String x)
{
char[]temp = x.toCharArray();
int counter = 2;
int sum = 0;
int j=0;
for(int i=8;i>=0;i--)
{
sum+= counter*Integer.parseInt(""+temp[i]);
counter+=1;
}
for(j=0;j<10;j++)
{
if((sum+j)%11==0)
{
break;
}
}
return x+""+j;
}
public static void main(String args[])
{
String a = "020131452";
System.out.println(ISBN(a));
}
}
Hope this helps.
This works:
public static int ISBN(String nineDigitNum){
int sum = 0;
for(int i = 0; i<nineDigitNum.length(); i++){
sum += Integer.parseInt(""+nineDigitNum.charAt(i))*(10-i);
}
return (sum%11);
}
Also I believe if the checksum is == to 10, it should return an X, so you could either change the return type and add an if statement somewhere, or just put the if statement outside wherever you are using this method.
Here is a short one without loops that uses only substring(), charAt() and length():
public static String ISBN(String nineDigits) {
int chkD = 11 - checkDigit(nineDigits, 0);
return nineDigits + ((chkD == 10) ? "X" : chkD);
}
public static int checkDigit(String nDsub, int chkD) {
if (nDsub.length() == 0)
return 0;
chkD = ((nDsub.charAt(0) - '0') * (nDsub.length() + 1));
return (chkD + checkDigit(nDsub.substring(1), chkD)) % 11;
}
Output:
> ISBN("123456789")
"123456789X"
> ISBN("123456780")
"1234567806"

How to solve exception in thread "main", java.lang.ArithmeticException: / by zero? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I keep getting an exception in thread "main":
java.lang.ArithmeticException: / by zero
at PersonalityTest.percentage(PersonalityTest.java:85)
at PersonalityTest.main(PersonalityTest.java:19)
And I want to add the count every time the scanner reads A or B and get the percentage of B.
import java.io.*;
import java.util.*;
public class PersonalityTest {
public static final int dimen = 4;
public static void main(String [] args) throws FileNotFoundException {
Scanner input = new Scanner(new File("personality.txt"));
PrintStream out = new PrintStream(new File("output.txt"));
int[] a = new int[4];
int[] b = new int[4];
welcome();
while (input.hasNextLine()) {
String letter = letter(input, out);
countNum(a, b, out, letter);
int[] percentage = percentage(a, b, out);
type(out, percentage);
out.println("");
}
}
public static void welcome () {
System.out.println("The Keirsey Temperament Sorter is a test that measures four independent dimensions of your personality: ");
System.out.println("1. Extrovert versus Introvert (E vs. I): what energizes you");
System.out.println("2. Sensation versus iNtuition (S vs. N): what you focus on");
System.out.println("3. Thinking versus Feeling (T vs. F): how you interpret what you focus on");
System.out.println("4. Judging versus Perceiving (J vs. P): how you approach life");
System.out.println("");
}
public static String letter (Scanner input, PrintStream out) {
String name = input.nextLine();
out.println(name + ":");
String letter = input.nextLine();
return letter;
}
public static void countNum(int[] a, int[] b, PrintStream out, String letter) {
int[] countA = new int[7];
int[] countB = new int[7];
int n = 0;
out.print("answers: [");
for (int i = 0; i < letter.length(); i++) {
int type = i % 7;
if (letter.charAt(i) == 'A' || letter.charAt(i) == 'a') {
n = 1;
}
else if (letter.charAt(i) == 'B' || letter.charAt(i) == 'b') {
n = 1;
}
countA[type] += n;
countB[type] += n;
if (type == 2 || type == 4 || type == 6) {
a[type / 2] = countA[type - 1]+ countA[type];
b[type / 2] = countB[type - 1]+ countA[type];
} else if (type == 0) {
a[0] = countA[0];
b[0] = countB[0];
}
for (int j = 0; j < dimen; j++) {
out.print(a[j] + "A-" + b[j] + "B," + " ");
}
}
out.print("]");
}
public static int[] percentage (int[] a, int[] b, PrintStream out) {
int[] percentage = new int [4];
out.print("percent B: [");
double n = 0.0;
for (int i = 0; i < dimen; i++) {
n = b[i] * 100 / (a[i] + b[i]); // <--- This is where I get error
percentage [i] = (int) Math.round(n);
out.print(percentage[i]);
}
out.print("]");
return percentage;
}
public static void type (PrintStream out, int[] percentage) {
String[] type = new String [4];
out.print("type: ");
for (int i = 0; i <= dimen; i++) {
while (percentage[i] > 50) {
if (i == 0) {
type[1] = "I";
}
else if (i == 1) {
type[2] = "N";
}
else if (i == 2) {
type[3] = "F";
}
else {
type[4] = "P";
}
}
while (percentage[i] < 50) {
if (i == 0) {
type[1] = "E";
}
else if (i == 1) {
type[2] = "S";
}
else if (i == 2) {
type[3] = "T";
}
else {
type[4] = "J";
}
}
out.print(Arrays.toString(type));
}
}
}
Your logic is very difficult to follow with all the a, b, +1, -1, /2, etc. You should try to reduce it to the smallest amount of code that demonstrates your problem. Odds are that you'll find the problem while you're doing that. You might also provide some sample input. Without one or both of these, it's very difficult to help with your problem.
Update: I'm seeing a number of things that look like problems now that I see what you're trying to do. Unless I'm mistaken, your input file has a name on the first line followed by 70 lines, each with a single letter on them?
For one thing, when read a letter and send it into countNum(), you only have one variable called "n" that you increment whether you see an A or a B, and then you add "n" to both A and B. That's not adding one to either the number of A's or the number of B's. It will always add one to both of them.
Next, since you only send a single letter into countNum(), the outer for loop will only execute one time. That means you'll only ever put a value into a[0] and b[0]. Further, because of the "n" problem, both values will always be 1. Thus the one time you get to the inner for loop, it will always print "1A-1B, 0A-0B, 0A-0B, 0A-0B" for the first part of the answer.
After that, it should be obvious why your percentage() method doesn't work. All but the first position of the array have zeroes in them.
Additional stuff: You define a constant "dimen" equal to 4 but you sometimes use the constant and sometimes use a literal "4". Pick one and stick with it. I'd recommend the constant, and I'd recommend naming it something meaningful, like "NUMBER_OF_PERSONALITY_DIMENSIONS", if that's what it is. For that matter, give all of your variables better names, and it will make things easier for you and me both. Also, in your type() method, you say for ( int i = 0; i <= dimen; i++ ) { to iterate over an array which I think only has 4 elements. That's going to break. Finally, as you kind of mentioned elsewhere, don't pass arrays around, mutating them in multiple different methods. That's a good way to get lost. In general, make methods non-side-effecty. Instead of modifying the things you pass to them, return the important values from the method.
In general, I think you need to take a break and straighten out in your head what you're trying to do. The logic doesn't seem to make any sense.
I don't know if you've learned about this kind of thing yet, but you might consider splitting this into two classes: one to read in the data and one to tally it up. The tallying one might look something like:
class Tallier {
private int numberOfAs;
private int numberOfBs;
private int totalEntriesSoFar;
private int[] typeATotals;
private int[] typeBTotals;
public void gotNewA() {...}
public void gotNewB() {...}
}
You are dividing by zero, the problem is that. On the mentioned line, you have:
n = b[ i ] * 100 / ( a[ i ] + b[ i ] );
Sometimes, a[i]+b[i] is zero. Maybe the problem will be solved by a check like this:
for( int i = 0; i < dimen; i++ ) {
if (a[ i ] + b[ i ]!= 0)
n = b[ i ] * 100 / ( a[ i ] + b[ i ] );
else
//assign a number to n for this situation
percentage [ i ] = ( int ) Math.round( n );
out.print( percentage[ i ] );
}
But logically, you should not divide a number by zero. Then maybe you have to correct your algorithm.

Algorithm that takes an integer and returns all possible format of addition

I need to write an algorithm that takes an integer and returns all possible format of addition
e.g.
If I eneter: 6
it would return the following String:
0+6=6
1+1+1+1+1+1=6
1+1+1+1+2=6
1+1+1+3=6
1+1+4=6
1+5=6
2+1+1+1+1=6
2+1+1+2=6
2+1+3=6
2+4=6
3+1+1+1=6
3+1+2=6
3+3=6
4+1+1=6
4+2=6
5+1=6
6+0=6
Here is my try:
import java.util.*;
public class Test
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Enter an integer? ");
int num = in.nextInt();
System.out.println();
calculate(num);
}
private static void calculate(int n)
{
int[] arInt = new int[n];
for(int i = 0; i <= n; i++)
{
for(int j = 0; j <= n; j++)
{
arInt[j] = i;
}
// ...
}
}
}
I agree with Brad. The best way to complete this would probably be through recursion. In fact, I was working on something related to this last night. I solved my problem using a recursive backtracking algorithm. Check out the Wikipedia page: Backtracking
Now, I make no guarantees that there aren't better, less complex ways to solve this. However, with recursive backtracking you will find all the solutions.
One thing to watch out for though, that 0. You can throw any amount of zeros into an addition/subtraction and it will come out the same.
If you asked the question, you are probably stuck... so i give you a hint :
Usually, in this kind of problem, you do not consider the same combinations with different permutations as different counts, and you do not consider addtion by 0: see Partition.
However, in your example, you seem to be distinguishing different permutations and counting 0. I am pertty much sure that you are not supposed to include 0 because that will give you infinitely many examples to any n. (By the way the answer you gave does not include all counts.) So I assume that you distinguish different permutations but not allow segment into 0. That actually makes the problem much easier.
Suppose you have n = 6.
O O O O O O
^ ^ ^ ^ ^
Think about the n - 1 = 5 positions between the six objects above. For each position, you can decide to either segment at the point or not. For example,
O|O O O|O O
^ ^ ^ ^ ^
is one possible segmentation. Interpret this as: 1+3+2, taking the consecutive objects not segmented by '|'. You should be able to get all possible ways in this way. Namely, for n-1 positions, either segment it or not. For any n, your list should be of 2^(n-1) examples.
E.g. for n = 3:
1+1+1, 2+1, 1+2, 3 => 4 different ways = 2^(3-1)
for n = 6, you should have 2^(6-1) = 32 examples, but you only have 17, which immediately tells that your list is not complete.
Finally note that, as I wrote at the beginning, your question is different from the partion question which is much more standard.
It looks like a homework, so I won't try to write it for you. But I will give you a hint about the solution. You have fixed quantity, imagine e.g. marbles. You are trying to find all possible numbers that add up to that quantity. This means you have to divide the marbles into groups somehow. If you know basic combinatorics, you can easily count the possibilities and enumerate them using an algorithm. Good luck!
Possible solution in Java using recursion:
public void run(int n)
{
List<StringBuilder> combos = showAdditionsFor(n);
for (StringBuilder s : combos)
{
if (s.indexOf("+") < 0)
{
System.out.println(s + " + 0 = " + n);
System.out.println("0 + " + s + " = " + n);
}
else
{
System.out.println(s + " = " + n);
}
}
}
List<StringBuilder> showAdditionsFor(int n)
{
List<StringBuilder> list = new ArrayList<StringBuilder>();
if (n == 0)
list.add(new StringBuilder(""));
else if (n == 1)
list.add(new StringBuilder(String.valueOf(1)));
else
{
for (int i = 1; i <=n; i++)
{
//get n-i list
List<StringBuilder> tempList = showAdditionsFor(n-i);
appendToEachListElement(String.valueOf(i),tempList);
list.addAll(tempList);
}
}
return list;
}
private void appendToEachListElement(String x, List<StringBuilder>l)
{
for (StringBuilder s : l)
{
if (s.length() == 0)
s.append(x);
else
s.append("+" + x);
}
}

Generating a multiple list of combinations for a number in Java

The following is the problem I'm working on and my snippet of code.
Is there a better way to implement this? I have used basic control structures for this below.
Is it better to store the rows and columns in a map and searching through the map based on the key/value pairs?
There is a security keypad at the entrance of a building. It has 9 numbers 1 - 9 in a 3x3 matrix format.
1 2 3
4 5 6
7 8 9
The security has decided to allow one digit error for a person but that digit should be horizontal or vertical. Example: for 5 the user is allowed to enter 2, 4, 6, 8 or for 4 the user is allowed to enter 1, 5, 7. IF the security code to enter is 1478 and if the user enters 1178 he should be allowed.
The following is a snippet of code i was working on:
ArrayList<Integer> list = new ArrayList<Integer>();
int num = 9;
int[][] arr = {{1,2,3},{4,5,6},{7,8,9}};
for(int i =0;i< arr.length;i++){
for(int j = 0; j <arr.length;j++){
if(num == arr[i][j]){
row = i;
col = j;
break;
}
}
}
for(int j1 = 0; j1< 3 ; j1++){
if(arr[row][j1] != num){
list.add(arr[row][j1]);
}
}
for(int i1 = 0 ; i1 <3;i1++){
if(arr[i1][col] != num){
list.add(arr[i1][col]);
}
}
There are many ways to solve this, but I think it can be solved with HashMaps and HashSets more efficiently than doing several iterations.
If I were you, I would build the data model first using a hash map and a hash set. This is because hash map and hash set have fast lookup, (no iterations)
HashMap<Integer,HashSet<Integer>> values = new HashMap<Integer, HashSet<Integer>>();
//now put in the accepted values for one
HashSet<Integer> oneValues = new HashSet<Integer>();
oneValues.put(1);
oneValues.put(2);
oneValues.put(4);
values.put(1, oneValues);
//put in 2 values
......
Then when you parse your input, if you want to see if an inputed value is accepted for what the code is, just do something like
private boolean isAccepted(int input, int combinationValue)
{
// check to see if the inputed value in the accepted values set
return values.get(combinationValue).contains(input);
}
I would tend to want a function along the lines of isCloseTo(int a, int b) So, say, if I called isCloseTo(5, 5) it would return true. If I called isCloseTo(2, 5) it should return true, too. But if I called isCloseTo(1, 3) it would return false.
So I'd write tests like that:
assertTrue(isCloseTo(5, 5));
OK, that's really easy to get to pass:
public boolean isCloseTo(int a, int b) {return true;}
Then, maybe
assertFalse(isCloseTo(1, 3));
which fails with the above implementation, so I'd need to change it
public boolean isCloseTo(int a, int b) {return a == b;}
That's still an incomplete implementation, so we need another test
assertTrue(isCloseTo(1, 2));
Now we start to need some real substance. And I think I'll leave the rest as an exercise for the reader. Yes, I've left the tricky bits out, but this is a strategy (test-driven design) that leads you more directly to solutions than just trying to write the code. As long as you keep all the test passing, you make steady progress toward a complete solution. Good luck!
There are many different acceptable solutions here. I suppose it's easier to construct 10x10 matrix of integer to check for the errors (for example errorMatrix). First index then will mean original digit, second index - digit typed by user, and value of arr[i][j] is a number of errors for this digit pair. Initialize it that way:
errorMatrix[i][i] = 0 //no error
errorMatrix[i][j] = 1, where i and j are horizontally or vertically neighboring digits
errorMatrix[i][j] = 2, in other cases.
Then for every digit pair you will get number of errors in O(1). You stated that you will accept only one error, so the value of 2 for unmatched pairs will be enough and you can just sum up the error numbers and compare it to one.
So, how to construct this. Iterate through all of the digit pairs and find the value of error. You should better implement function CheckError that will calculate it for digit pair a and b
if a=b, then errorMatrix is 0;
The digits a and b are vertical
neighbors if abs(a-b) = 3. So, is
abs(a-b)==3 set errorMatrix[a][b] =
1;
The digits a and b are horizontal
neighbors if
a. (a-1)/3==(b-1)/3 - here we check that this digits are on the same line.
b. abs(a-b)==1 - here we check that digits are in the neighboring cells.
If (a) and (b) then error value is 1;
In other cases error value is 2.
It seems to me that this spec is right. However, you need to test it before using
So, if you then want to handle the changes of the keypad layout you just have to rewrite CheckError method.
Hope it helps.
Or this...
boolean matchDigit(int p, int d) {
return (p==d)
|| (p==d-3)
|| (p==d+3)
|| (d%3!=1 && p==d-1)
|| (d%3!=0 && p==d+1);
}
this assumes we've already assured that p and d are between 1 and 9.
For the specific keyboard in your question we can use a base 3 to solve this problem and to calculate the distances between digits/keys.
1 { 1 / 3, 1 % 3 } = {0, 1}
2 { 2 / 3, 2 % 3 } = {0, 2}
...
5 { 5 / 3, 5 % 3 } = {1, 2}
...
8 { 8 / 3, 8 % 3 } = {2, 2}
public boolean isValidCode(int code, int expexted) {
while(code > 0)
{
if (!isValidDigit(code % 10, expected % 10))
return false ;
code /= 10 ;
expected /= 10 ;
}
return (code == expected) ;
}
public boolean isValidDigit(int a, int b) {
int dx = (a - b) / 3 ;
int dy = (a - b) % 3 ;
return ((Math.abs(dx) + Math.abs(dy)) == 1)
}
A more generic and robust solution will be to create a Map where you can set what other keys you accept.
Sample: allowing A, Z, P, M, N for A: place a new entry 'A'="AZPMN" in the map, validation checkd if the character is the same or if the type character is in the exceptions string.
private Map acceptedChars = new HashMap() ;
public void loadAcceptedCharacters() {
acceptedChars.put('A', "AZPMN") ;
}
public boolean isValidKeyword(String word, String expected)
{
if (word == null || word.matches("\\s*"))
return false ;
if (word.length() != expected.length())
return false ;
for(int idx = 0; idx < word.length(); idx++)
{
if (!isValidDigit(word.chatAt(idx), expected.charAt(idx)))
return false ;
}
return true ;
}
public boolean isValidDigit(char chr, char expected) {
String accepted ;
if (chr != expected)
{
accepted = acceptedChars.get(chr) ;
if (accepted == null)
return false ;
if (accepted.indexOf(chr) < 0)
return false ;
}
return true ;
}

Categories

Resources