第二章数组,P31。
题目:设计一个可容纳40位数的注n!的程序
我们先来设计一个可用于超长位数加乘法的数据类,有点类似于Integer一样。其测试代码如下:
package cn.com.chengang.algorithms.ch02.array02;
import junit.framework.TestCase;
/**
* 对LongNumber类进行测试
*
* @author 陈刚 2006-9-6 from http://www.ChenGang.com.cn
*/
public class LongNumberTest extends TestCase {
/**
* 测试构造函数
*
*/
public void testNew() {
LongNumber n = new LongNumber();
assertEquals("0", n.value());// 默认值为0
n = new LongNumber("0123456789");
assertEquals("0123456789", n.value());
try {
n = new LongNumber("abc88");
fail("当参数含有非数字字符时,应抛出异常");
} catch (Exception e) {
assertTrue(true);
}
}
/**
* 测试加法
*
*/
public void testAdd() {
// 普通
LongNumber n1 = new LongNumber("13");
LongNumber n2 = new LongNumber("4");
LongNumber n3 = n1.add(n2);
assertEquals("17", n3.value());
// 有进位的
n1 = new LongNumber("9");
n2 = new LongNumber("4");
n3 = n1.add(n2);
assertEquals("13", n3.value());
// 有一个数是零的
n1 = new LongNumber("13");
n2 = new LongNumber("0");
n3 = n1.add(n2);
assertEquals("13", n3.value());
// 位数特长的
n1 = new LongNumber("33333333333333333333333333333333333333333393");
n2 = new LongNumber("298");
n3 = n1.add(n2);
assertEquals("33333333333333333333333333333333333333333691", n3.value());
}
/**
* 测试乘法
*
*/
public void testMul() {
// 普通
LongNumber n1 = new LongNumber("3");
LongNumber n2 = new LongNumber("2");
LongNumber n3 = n1.mul(n2);
assertEquals("6", n3.value());
// 有进位
n1 = new LongNumber("3");
n2 = new LongNumber("4");
n3 = n1.mul(n2);
assertEquals("12", n3.value());
// 零
n1 = new LongNumber("3");
n2 = new LongNumber("0");
n3 = n1.mul(n2);
assertEquals("0", n3.value());
// 多位数
n1 = new LongNumber("98");
n2 = new LongNumber("67");
n3 = n1.mul(n2);
assertEquals("6566", n3.value());
// 位数特长的
n1 = new LongNumber("5897452665");
n2 = new LongNumber("8755445");
n3 = n1.mul(n2);
assertEquals("51634822448510925", n3.value());
}
}
然后,LongNumber的实现代码如下:
package cn.com.chengang.algorithms.ch02.array02;
/**
* 用于超长位数的加乘法,现在暂时不支持负数。<br>
* 同时这和Integer一样是一个不变类
*
* @author 陈刚 2006-9-6 from http://www.ChenGang.com.cn
*/
public class LongNumber {
private int[] data;
private String dataStr;
public LongNumber() {
data = new int[1];
data[0] = 0;
}
public LongNumber(String number) {
byte[] bytes = number.getBytes();
data = new int[bytes.length];
for (int i = 0; i < bytes.length; i++) {
data[i] = bytes[i] - 48;
if (data[i] < 0 || data[i] > 9)
throw new IllegalArgumentException("参数必须是数值");
}
}
/**
* 以字符串的形式返回数值
*
* @return
*/
public String value() {
if (dataStr == null)
dataStr = parseString(data);
return dataStr;
}
/**
* 一个工具方法,把一个int数组转化成字符串<br>
* new int[]{1,4,5}-->145
*
* @param array
* @return
*/
private static String parseString(int[] array) {
StringBuilder buf = new StringBuilder();
for (int c : array) {
buf.append(c);
}
return buf.toString();
}
/**
* 加法。注意:本类为不变类,本类的值不会改变
*
* @param number2 被加数
* @return 加法的结果
*/
public LongNumber add(LongNumber number2) {
int[] result = add(number2.data);
return new LongNumber(parseString(result));
}
/**
* 此方法为了和mul方法共享算法而分离出来的
*
* @param data2
* @return
*/
private int[] add(int[] data2) {
// 创建一个装相加结果的数组data3
int len1 = data.length;
int len2 = data2.length;
int len3 = (len1 > len2 ? len1 : len2) + 1; // 有可能有进位,所以多加一位
int[] data3 = new int[len3];
for (int i = 0; i < len3; i++) {
// 得到index值(从未位开始相加)。
int index1 = len1 - i - 1;
int index2 = len2 - i - 1;
int index3 = len3 - i - 1;
// 相加
if (index1 > -1)
data3[index3] += data[index1];
if (index2 > -1)
data3[index3] += data2[index2];
// 进位判断
if (data3[index3] > 9) {
data3[index3] = data3[index3] - 10;
data3[index3 - 1] = 1;
}
}
// 第一位数是否存在,不存在则剪去,并把剩下的值放到一个新数组中
int[] result = data3;
if (data3[0] == 0) {
result = new int[data3.length - 1];// 新数组位数减1
System.arraycopy(data3, 1, result, 0, result.length);// 数据复制
}
return result;
}
/**
* 乘法
*
* @param number2 被乘数
* @return 相乘的结果
*/
public LongNumber mul(LongNumber number2) {
int len2 = number2.data.length;
LongNumber result = new LongNumber();
for (int i = 0; i < len2; i++) {
int n2 = number2.data[len2 - i - 1];
int[] temp = singleMul(n2);
if (i != 0) {// 向左移位,补零
int[] newTemp = new int[temp.length + i];
System.arraycopy(temp, 0, newTemp, 0, temp.length);
temp = newTemp;
}
String str = parseString(result.add(temp));
result = new LongNumber(str);
}
return result;
}
/**
* number2是个位数:0~9
*/
private int[] singleMul(int number2) {
// 创建一个装相乘结果的数组data3
int len1 = data.length;
int len3 = len1 + 1; // 最大可能位数
int[] data3 = new int[len3];
for (int i = 0; i < len1; i++) {
// 得到index值(从未位开始相加)。
int index1 = len1 - i - 1;
int index3 = len3 - i - 1;
data3[index3] += data[index1] * number2;
// 进位
if (data3[index3] > 9) {
data3[index3 - 1] = data3[index3] / 10;
data3[index3] = data3[index3] % 10;
}
}
// 第一位数是否存在,不存在则剪去,并把剩下的值放到一个新数组中
int[] result = data3;
if (data3[0] == 0) {
result = new int[data3.length - 1];// 新数组位数减1
System.arraycopy(data3, 1, result, 0, result.length);// 数据复制
}
return result;
}
}
说明:由于此类现在是为了求N!,所以暂时不支持负数
接下来我们来实现求N!,先写它的测试代码,如下:
package cn.com.chengang.algorithms.ch02.array02;
import junit.framework.TestCase;
/**
* 对MathUtils类进行测试
*
* @author 陈刚 2006-9-6 from http://www.ChenGang.com.cn
*/
public class MathUtilsTest extends TestCase {
public void testCalculus() {
assertEquals("1", MathUtils.calculus(1));
assertEquals("2", MathUtils.calculus(2));
assertEquals("6", MathUtils.calculus(3));
assertEquals("24", MathUtils.calculus(4));
assertEquals("120", MathUtils.calculus(5));
assertEquals("720", MathUtils.calculus(6));
assertEquals("87178291200", MathUtils.calculus(14));
assertEquals("10888869450418352160768000000", MathUtils.calculus(27));
}
}
然后,再写它的实现代码如下:
package cn.com.chengang.algorithms.ch02.array02;
/**
* 一个数学工具类
*
* @author 陈刚 2006-9-6 from http://www.ChenGang.com.cn
*/
public class MathUtils {
/**
* 求N!
*
* @param n
* @return
*/
public static String calculus(int n) {
LongNumber result = new LongNumber("1");
for (int i = 2; i <= n; i++) {
LongNumber temp = new LongNumber("" + i);
result = result.mul(temp);
}
return result.value();
}
}
从测试代码来看,原书自27!之后全部算错了。仔佃观察原书P35的27!的值第一位少了一个1,也许就是从这里开始出错的吧,具体错误原因我没有去给他找。