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\$

migrated from stackoverflow.com Dec 18 '17 at 10:30

This question came from our site for professional and enthusiast programmers.

  • \$\begingroup\$ @FinalFind nothing bad but which company asked such a question during your interview? \$\endgroup\$ – Luai Ghunim Nov 12 '17 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 '17 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\$ – Pritam Banerjee Nov 12 '17 at 22:19
  • \$\begingroup\$ Was it allowed to use NumberFormat.parse(String)? \$\endgroup\$ – Benjamin Caure Nov 12 '17 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 '17 at 22:20
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\$
  • 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 '17 at 0:24
  • \$\begingroup\$ Changed as suggested O:) \$\endgroup\$ – Stefan Haustein Dec 20 '17 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\$
  • \$\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 '17 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 '17 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 '17 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\$
  • \$\begingroup\$ How is this different from what I have posted a quarter hour ago? :) \$\endgroup\$ – Stefan Haustein Nov 12 '17 at 22:48
  • \$\begingroup\$ @StefanHaustein it's in java and with a bit of an added explanation. \$\endgroup\$ – Paul Nov 12 '17 at 22:49
  • \$\begingroup\$ Are you suggesting my answer is not Java? \$\endgroup\$ – Stefan Haustein Nov 12 '17 at 22:53
  • \$\begingroup\$ @StefanHaustein my bad. I should probably get some sleep. Sry. \$\endgroup\$ – Paul Nov 12 '17 at 23:01

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy