0
\$\begingroup\$

I recently had a job interview and messed up on writing an algorithm which, realistically I wouldn't have any trouble with if I wasn't under pressure. I was annoyed and wrote the solution up when I got home in like 5 minutes. My conclusion as to why this happened: I am a bit rusty at writing algorithms so drawing a blank when pressure is applied, therefore I am practicing several questions to get my mind ticking again.

I ran into this question;

"Write a program that reads a number with commas and decimal dots (such as “23,419.34”) and then prints a number that is half of it. Do not use Double.parseDouble()."

Constraints:

  1. Integer.parseInt only (Assumed)
  2. No use of Arrays/Lists etc.

Here is my solution;

private static void textToNum() {
    System.out.println("Enter Number nnn,xxx,yyy.zz");
    String text = sc.nextLine();

    double res = 0.0;
    StringBuilder bd = new StringBuilder();

    for (int i = 0; i < text.length(); i++) {
        if (Character.isDigit(text.charAt(i))) {
            System.out.println("CA: " + text.charAt(i));
            bd.append(text.substring(i, i+1));
            if (i == text.length() - 1) {
                double d = Math.pow(10, bd.length());
                double temp = Integer.parseInt(bd.toString()) / d;
                res += (temp / 2);
            }
        }
        else if (text.charAt(i) == '.') {
            res = Integer.parseInt(bd.toString()) / 2.0;
            System.out.println("1Res: " + res);
            bd = new StringBuilder();
        }
    }
    System.out.println("Res: " + res);
}

While this solves the problem, I couldn't help thinking there must be a cleaner solution that I was missing.

Any suggestions?

\$\endgroup\$
5
  • \$\begingroup\$ @FinalFind nothing bad but which company asked such a question during your interview? \$\endgroup\$
    – Luai Ghunim
    Nov 12, 2017 at 22:15
  • \$\begingroup\$ Sorry if it was misleading, but this wasnt the question lol (it was simpler than this). This question in an interview for a junior java developer I feel would have been harsh. Considering I only got 5 mins or so \$\endgroup\$
    – FinalFind
    Nov 12, 2017 at 22:17
  • \$\begingroup\$ You can just split the String using a comma separator, get the string array, join it and then divide the number by two. \$\endgroup\$ Nov 12, 2017 at 22:19
  • \$\begingroup\$ Was it allowed to use NumberFormat.parse(String)? \$\endgroup\$
    – Benjamin Caure
    Nov 12, 2017 at 22:19
  • \$\begingroup\$ Nah, I was also trying to avoid using arrays, as I am using uni notes, and this question appeared before any array operations. Sorry another big point that should be there. \$\endgroup\$
    – FinalFind
    Nov 12, 2017 at 22:20

3 Answers 3

2
\$\begingroup\$

Was intended as a joke, but should actually work for very large numbers because it only looks at the digits one by one. It uses some knowledge about digit character code points (48..58) O:)

int carry = 0;
boolean dot = false;
for (int i = 0; i < text.length(); i++) {
  char c = text.charAt(i);
  if (c >= '0' && c <= '9') {
    System.out.print((char) (c / 2 + 24 + carry))
    carry = (c & 1) * 5;
  } else {
    System.out.print(c);
    dot |= c == '.';
  }
}
if (carry != 0) {
  System.out.print(dot ? "5" : ".5");
}

More reasonable (one could do the division inline here, too) -- and no library calls at all (except for printing the result).

double result = 0;
double decimal = 0;
for (int i = 0; i < text.length(); i++) {
  char c = text.charAt(i);
  if (c >= '0' && c <= '9') {
    if (decimal != 0) {
      result += (c - '0') * decimal;
      decimal /= 10;
    } else {
      result = result * 10 + (c - '0');
    }
  } else if (c == '.') {
    decimal = 0.1;
  }
}
System.out.println(result / 2);
\$\endgroup\$
2
  • 1
    \$\begingroup\$ Good thing this didn't start out as a code review. For brevity, I'd probably use dot |= c == '.' (and decimal /= 10). \$\endgroup\$
    – greybeard
    Dec 20, 2017 at 0:24
  • \$\begingroup\$ Changed as suggested O:) \$\endgroup\$ Dec 20, 2017 at 9:35
1
\$\begingroup\$

First, I suggest you to use a for each loop with iterators. It's cleaner and lighten the code.

Second, I wouldn't use this way of reading. I'd use something to split what's before and after the ".", then you can read what's before the "." from right to left and multiply by the good power of 10. What's after the "." you read from left to right and divide by the right power of 10. You add all of this and you get the solution. Going this way avoid you using complicated things like StringBuilder and get two loops very easy to read.

Once you get the number you can divide it by 2. I believe it's easier to read that you first build number then divide it by 2.

\$\endgroup\$
3
  • \$\begingroup\$ Not sure how one would use an iterator here. String split and power functions are quite expensive, so it would work, but certainly wouldn't be how the system libraries would implement something like this. \$\endgroup\$
    – Stefan Haustein
    Nov 12, 2017 at 22:56
  • \$\begingroup\$ I'd use iterator in the for each loop. And by using power function i don't mean actually using it. If you have a variable that you multiply by 10 at each loop iteration you increase the power of 10, if you divide, you decrease it. String split is only done once and is O(n) as the entire algorithm so it's not subefficient \$\endgroup\$
    – Ricocotam
    Nov 12, 2017 at 23:10
  • \$\begingroup\$ If I'd ask such a question in an interview (which I wouldn't), it would be about object allocation and copy overhead, as there is no algorithmic challenge at all. \$\endgroup\$
    – Stefan Haustein
    Nov 13, 2017 at 16:45
0
\$\begingroup\$

You could try directly dividing without ever leaving the string-format:

String div2(String val){
     StringBuilder b = new StringBuilder();
     boolean rem = false;
     boolean fractional = false;

     for(char c : val){
         if(Character.isDigit(c))
         {
             int d = c - '0';

             b.append(d / 2 + (rem ? 5 : 0));
             rem = (d % 2) == 1;
         }
         else if(c == '.' || c == ',')
         {
             b.append(c);
             fractional = (c == '.');
         }
         else
         {
             throw new IllegalArgumentException("Invalid char: " + c);
         } 
     }

     if(rem)
     {
         b.append(fractional ? '5' : ".5");
     }

     return rem.toString();
}

This method works without explicitly parsing the number and doesn't rely on the syntax-rules imposed on notation of decimals. In fact this could be fed a simple digit-stream and would still work.

This basically divides the number in simple paper-and-pencil mathematics. If an even number is divided by 2, there is no remainder. I.e. 4 / 2 = 2. On the other hand for odd numbers, an additional 5 must be added to the next digit. 30 / 2 = 15.

\$\endgroup\$
4
  • \$\begingroup\$ How is this different from what I have posted a quarter hour ago? :) \$\endgroup\$
    – Stefan Haustein
    Nov 12, 2017 at 22:48
  • \$\begingroup\$ @StefanHaustein it's in java and with a bit of an added explanation. \$\endgroup\$
    – user82687
    Nov 12, 2017 at 22:49
  • \$\begingroup\$ Are you suggesting my answer is not Java? \$\endgroup\$
    – Stefan Haustein
    Nov 12, 2017 at 22:53
  • \$\begingroup\$ @StefanHaustein my bad. I should probably get some sleep. Sry. \$\endgroup\$
    – user82687
    Nov 12, 2017 at 23:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.