Problem:
Given two numbers represented as strings, return multiplication of the numbers as a string.
Note: The numbers can be arbitrarily large and are non-negative.
Analysis:
The naive solution of this problem is to following the routine of multiplication, whose algorithm we are quite familar with. Solution 1: (Naive solution) 1. Iterate on one string's digit, use the digit multiply with the other number. 2. Add the partial result with total result. (note the trailing zero of each mulitiplication). Although the algorithm is clear, but it involves many mainpulations over string. I have made many mistakes in my first implementation.
Wrong solution:
public class Solution { public String multiply(String num1, String num2) { if (num1 == null || num2 == null) throw new IllegalArgumentException("The passed in arguments is illegal!"); char[] num2_array = num2.toCharArray(); String ret = "0"; for (int i = num2_array.length - 1; i >= 0; i--) { String temp = multiple(num1, num2_array[i]); ret = add(temp + ('0'*num2_array.length-1-i), ret); } return ret; } private String multiple(String num_str, Character n) { int carry = 0; char[] num = num_str.toCharArray(); StringBuffer ret = new StringBuffer(); for (int i = num.length - 1; i >= 0; i--) { int temp = Integer.valueOf(num[i]) * Integer.valueOf(n) + carry; int cur_digit = temp % 10; carry = temp / 10; ret.insert(0, cur_digit); } return ret.toString(); } private String add(String num1_str, String num2_str) { char[] num1 = num1_str.toCharArray(); char[] num2 = num2_str.toCharArray(); int carry = 0; int len = Math.max(num1.length, num2.length); StringBuffer ret = new StringBuffer(); for(int i = 0; i < len; i++) { int temp1 = (num1.length-1 >= i) ? Integer.valueOf(num1[i]) : 0; int temp2 = (num2.length-1 >= i) ? Integer.valueOf(num2[i]) : 0; int temp = temp1 + temp2 + carry; int cur_digit = temp % 10; carry = temp / 10; ret.insert(0, cur_digit); } return ret.toString(); } }
Mistakes analysis:
***************************************************************************** Mistakes mistake 1: forget to add carray number, when loop is over. for (int i = num.length - 1; i >= 0; i--) { ... ret.insert(0, cur_digit); } return ret.toString(); ? What if the carry is not 0. Fix: for (int i = num.length - 1; i >= 0; i--) { ... } if (carry != 0) ret.insert(0, carry); return ret.toString(); mistake 2: use Integer.valueOf() against character rather than String. int temp = Integer.valueOf(num[i]) * Integer.valueOf(n) + carry; Integer.valueOf(character c) would return the Ascii code of character c, rather than the Integer form with the value: c. Fix: covert the character into a string. int temp = Integer.valueOf(num[i]+"") * Integer.valueOf(n+"") + carry; mistake 3: use Ruby code for the implemetation. wish '0' * 3 could return "000" ret = add(temp + ('0'*num2_array.length-1-i), ret); Fix: use a while loop to add trial zero. while (trail_zero < num2_array.length-1-i) { temp += '0'; trail_zero++; } mistake 4: the index of add is wrong, since the two string may be in different length, and we must start from the right digt to left digit. Start from left is absolutely wrong! for(int i = 0; i < len; i++) { int temp1 = (num1.length-1 >= i) ? Integer.valueOf(num1[i]) : 0; int temp2 = (num2.length-1 >= i) ? Integer.valueOf(num2[i]) : 0; ... } Fix: for(int i = 0; i < len; i++) { int temp1 = (len1-1-i >= 0) ? Integer.valueOf(num1[len1-1-i]+"") : 0; int temp2 = (len2-1-i >= 0) ? Integer.valueOf(num2[len2-1-i]+"") : 0; ... }
Solution 1:
public class Solution { static public String multiply(String num1, String num2) { if (num1 == null || num2 == null) throw new IllegalArgumentException("The passed in arguments is illegal!"); char[] num2_array = num2.toCharArray(); String ret = "0"; for (int i = num2_array.length - 1; i >= 0; i--) { String temp = multiple(num1, num2_array[i]); int trail_zero = 0; while (trail_zero < num2_array.length-1-i) { temp += '0'; trail_zero++; } ret = add(temp, ret); } while (ret.length() > 1 && ret.charAt(0) == '0') ret = ret.substring(1, ret.length()); return ret; } static private String multiple(String num_str, Character n) { int carry = 0; char[] num = num_str.toCharArray(); StringBuffer ret = new StringBuffer(); for (int i = num.length - 1; i >= 0; i--) { int temp = Integer.valueOf(num[i]+"") * Integer.valueOf(n+"") + carry; int cur_digit = temp % 10; carry = temp / 10; ret.insert(0, cur_digit); } if (carry != 0) ret.insert(0, carry); return ret.toString(); } static private String add(String num1_str, String num2_str) { char[] num1 = num1_str.toCharArray(); char[] num2 = num2_str.toCharArray(); int len1 = num1.length; int len2 = num2.length; int carry = 0; int len = Math.max(num1.length, num2.length); StringBuffer ret = new StringBuffer(); for(int i = 0; i < len; i++) { int temp1 = (len1-1-i >= 0) ? Integer.valueOf(num1[len1-1-i]+"") : 0; int temp2 = (len2-1-i >= 0) ? Integer.valueOf(num2[len2-1-i]+"") : 0; int temp = temp1 + temp2 + carry; int cur_digit = temp % 10; carry = temp / 10; ret.insert(0, cur_digit); } if (carry != 0) ret.insert(0, carry); return ret.toString(); } }
Improvement analysis:
Apparently, although the above solutio is clear, but it is really hard to implement. We could a use more concise solution for this problem. When we multiply two numbers, we should keep following truth in mind. Suppose: num1, num2, result. result = num1 * num2 ------------------------------------------------------------------ Truth 1: The result's length must no greater than num1.length+num2.length ------------------------------------------------------------------ Truth 2: Each digit's weight were actually decided through digit[i+j] += (num1[i] * num2[j]) + carry; Note the i,j (it free us from the previous solution at give us the underlying principle) num1: 11 num2: 22 11 22 ---------- 22 22 Note the underlying reaon! ------------------------------------------------------------------ Once we have above truth in mind, it is really easy to solve this problem. But You shoul note some implement skils. Skill 1: the high digit is start from low index(0), and we need to start from low digit, how could we do it!!! Reverse the string!!! num1 = new StringBuffer(num1).reverse().toString(); num2 = new StringBuffer(num2).reverse().toString(); Skill 2: how to map digit value in character form into integer. digit[i+j] += (num1.charAt(i) - '0') * (num2.charAt(j) - '0'); Skill 3: add carry for each digit (note the last digit has no carry) for (int i = 0; i < digit.length; i++) { int mod = digit[i] % 10; //cause last digit has no carry. int carry = digit[i] / 10; if (i+1 < digit.length) digit[i+1] += carry; //Truth 1 ret.insert(0, mod); } Skill 4: since we caculate from the last digit, how could we guarantee the right order? ret.insert(0, mod);
Solution 2:
public class Solution { static public String multiply(String num1, String num2) { if (num1 == null || num2 == null) throw new IllegalArgumentException("The passed in arguments is illegal!"); num1 = new StringBuffer(num1).reverse().toString(); num2 = new StringBuffer(num2).reverse().toString(); int[] digit = new int[num1.length() + num2.length()]; StringBuffer ret = new StringBuffer(); for (int i = 0; i < num1.length(); i++) { for (int j = 0; j < num2.length(); j++) { digit[i+j] += (num1.charAt(i) - '0') * (num2.charAt(j) - '0'); } } for (int i = 0; i < digit.length; i++) { int mod = digit[i] % 10; int carry = digit[i] / 10; if (i+1 < digit.length) digit[i+1] += carry; ret.insert(0, mod); } while (ret.length() > 1 && ret.charAt(0) == '0') ret.deleteCharAt(0); return ret.toString(); } }