题目:
一个巨大无比的数字(现有的内存无法容纳,只能存储在磁盘文件中),求该数对7的模!半夜看到一个提问这个的帖子,心血来潮捣鼓了下,花了我3小时,觉得蛮有意思就变成自己开帖子了,欢迎讨论!首先,如果这个数能够被BigInteger装得下,那么就直接取模就OK了
如果这个数实在是大到没有办法用内存来装它的话,那么对于7的模,我还有办法:首先是理论依据:
1、取模的性质:(a+b)%c=(a%c+b%c)%c;
这个性质是我的解法的基础,对于任意一个数,比如31246而言:
31246%7=(30000%7+1000%7+200%7+40%7+6%7)%7
因此,只要能够将该数按位(十进制)分拆开来计算7的模,并汇总之后模7即可!
2、我的解法关键就是如何计算每一个位对7的模,以下是我写的一个测试程序: BigInteger mod=new BigInteger("7");
for(int i=0;i<100;i++){
for(int j=0;j<10;j++){
BigInteger bi=new BigInteger("10");
BigInteger b=new BigInteger(String.valueOf(j));
bi=bi.pow(i).multiply(b).mod(mod);
System.out.print(" "+bi);
}
System.out.println();
}其输出结果如下:
0 1 2 3 4 5 6 0 1 2
0 3 6 2 5 1 4 0 3 6
0 2 4 6 1 3 5 0 2 4
0 6 5 4 3 2 1 0 6 5
0 4 1 5 2 6 3 0 4 1
0 5 3 1 6 4 2 0 5 3
0 1 2 3 4 5 6 0 1 2
0 3 6 2 5 1 4 0 3 6
0 2 4 6 1 3 5 0 2 4
0 6 5 4 3 2 1 0 6 5
0 4 1 5 2 6 3 0 4 1
0 5 3 1 6 4 2 0 5 3
0 1 2 3 4 5 6 0 1 2
0 3 6 2 5 1 4 0 3 6
0 2 4 6 1 3 5 0 2 4
0 6 5 4 3 2 1 0 6 5
.................
可以发现,0-9的十个数字中除了0和7之外,它们从个十百千万不断提升位数时,对7的模总是按照一定的规律:132645132645...不断地循环,只是个位数上的起始循环位置的不同,那么这就为计算nEx对7的模提供了一定的可能
(虽然我不能证明当x非常大的时候这个规律仍然有效,但是这一发现已经让我兴奋得无法承认它不可靠,就算错也让它一错到底吧!)
下面是一个演示程序,如果真要让它处理巨大的数字,肯定会需要用文件流来操作,我只想演示这个过程可行,就不费那神了: int[][] base=new int[][]{{1,3,2,6,4,5},{2,6,4,5,1,3},{3,2,6,4,5,1},{4,5,1,3,2,6},{5,1,3,2,6,4},{6,4,5,1,3,2}};
byte[] buf="17777777777777777777715".getBytes();
int mod=0,ccnt=0;
for(int i=buf.length-1;i>=0;i--,ccnt++){// 从个位数往高位依次处理
int d=buf[i]&0x0F;// ASCII to number
d%=7;// 8,9的情况与1,2相同,so...
if(d==0) continue;
int index=ccnt%6;
mod+=base[d-1][index];
mod%=7;
}
System.out.println(mod);我没过多地测试这个程序,可能还有BUG也说不定,至少这个程序应该能够证明,能够有一种手段对可以使用“流”的方式来计算7的模,我不想继续花时间在这个小程序上,因为我现在更想知道和思考如果不是7的话,是不是也有类似的解决方案,有没有通用的解决方案呢?
一个巨大无比的数字(现有的内存无法容纳,只能存储在磁盘文件中),求该数对7的模!半夜看到一个提问这个的帖子,心血来潮捣鼓了下,花了我3小时,觉得蛮有意思就变成自己开帖子了,欢迎讨论!首先,如果这个数能够被BigInteger装得下,那么就直接取模就OK了
如果这个数实在是大到没有办法用内存来装它的话,那么对于7的模,我还有办法:首先是理论依据:
1、取模的性质:(a+b)%c=(a%c+b%c)%c;
这个性质是我的解法的基础,对于任意一个数,比如31246而言:
31246%7=(30000%7+1000%7+200%7+40%7+6%7)%7
因此,只要能够将该数按位(十进制)分拆开来计算7的模,并汇总之后模7即可!
2、我的解法关键就是如何计算每一个位对7的模,以下是我写的一个测试程序: BigInteger mod=new BigInteger("7");
for(int i=0;i<100;i++){
for(int j=0;j<10;j++){
BigInteger bi=new BigInteger("10");
BigInteger b=new BigInteger(String.valueOf(j));
bi=bi.pow(i).multiply(b).mod(mod);
System.out.print(" "+bi);
}
System.out.println();
}其输出结果如下:
0 1 2 3 4 5 6 0 1 2
0 3 6 2 5 1 4 0 3 6
0 2 4 6 1 3 5 0 2 4
0 6 5 4 3 2 1 0 6 5
0 4 1 5 2 6 3 0 4 1
0 5 3 1 6 4 2 0 5 3
0 1 2 3 4 5 6 0 1 2
0 3 6 2 5 1 4 0 3 6
0 2 4 6 1 3 5 0 2 4
0 6 5 4 3 2 1 0 6 5
0 4 1 5 2 6 3 0 4 1
0 5 3 1 6 4 2 0 5 3
0 1 2 3 4 5 6 0 1 2
0 3 6 2 5 1 4 0 3 6
0 2 4 6 1 3 5 0 2 4
0 6 5 4 3 2 1 0 6 5
.................
可以发现,0-9的十个数字中除了0和7之外,它们从个十百千万不断提升位数时,对7的模总是按照一定的规律:132645132645...不断地循环,只是个位数上的起始循环位置的不同,那么这就为计算nEx对7的模提供了一定的可能
(虽然我不能证明当x非常大的时候这个规律仍然有效,但是这一发现已经让我兴奋得无法承认它不可靠,就算错也让它一错到底吧!)
下面是一个演示程序,如果真要让它处理巨大的数字,肯定会需要用文件流来操作,我只想演示这个过程可行,就不费那神了: int[][] base=new int[][]{{1,3,2,6,4,5},{2,6,4,5,1,3},{3,2,6,4,5,1},{4,5,1,3,2,6},{5,1,3,2,6,4},{6,4,5,1,3,2}};
byte[] buf="17777777777777777777715".getBytes();
int mod=0,ccnt=0;
for(int i=buf.length-1;i>=0;i--,ccnt++){// 从个位数往高位依次处理
int d=buf[i]&0x0F;// ASCII to number
d%=7;// 8,9的情况与1,2相同,so...
if(d==0) continue;
int index=ccnt%6;
mod+=base[d-1][index];
mod%=7;
}
System.out.println(mod);我没过多地测试这个程序,可能还有BUG也说不定,至少这个程序应该能够证明,能够有一种手段对可以使用“流”的方式来计算7的模,我不想继续花时间在这个小程序上,因为我现在更想知道和思考如果不是7的话,是不是也有类似的解决方案,有没有通用的解决方案呢?
解决方案 »
- 请高手看一下发送邮件的这个程序代码,问题在哪儿
- 在JTable中添加/删除一行,如何使得与之连接的数据库也相应改动保持一致
- 比较有意思的题-线程问题
- [向高手求助]java如何把网页上的图片下载下来,保存在本地某个文件中
- 我要比较 object[1][0] 和 10 的大小,怎么做呢?
- 关于if语句
- java中如何实现取得机器的网卡物理地址
- 游泳的猫(现代程序员)
- 用jdk1.2执行java程序,javac通过了,但java总是执行不了,是因为什么?(错误提示为——Exception in thread "main" java.lang.NoClassDefFoundError:Helloworld/class)
- 怎么从键盘连续输入?
- 无法正常添加菜单项
- 谁可推荐一下数值算法参考书
昨天我也看到那个帖子
当然也想到用BigInteger来对7取模
但感觉 要是直接转成BigInteger来进行运算的话
这似乎不是面试官的意图
这样的话 就跟普通的运算没什么两样
应该是有什么比较好的算法
继续关注 高手们的解答
顶!!!!
向你说的。。我先用流读进来一部分来计算
既然是7的模,你可以一部分一部分的读,有一个道理就是,7可以被7×10的整数倍除,对于1 2 3 4 5 ....
这种数字,你可以先计算1 2 3 % 7的值,结果为6,那么再将6和后面的一部分继续处理,就成了6 4 5 ...这样的了。
这种处理一种继续,最后就成了一个int以内的计算了。
实际上一个数以写成 X(模) + 7 * 10 + 7 * 100 + 7 *1000,这种的写法
那么,我们就可以倒算,先减到7*10X 这种,最后就自然得到最后的模了。还有一个简单的算法,对于大数,不管是什么,先对每个数-7,比如97,可以当成(9-7)(7-0)这样,即20来算,结果算出来的模和 97算出来的模是一样的。这两种算法,相互结合,结果一定很快,可以达到最快速的计算。
import java.math.BigInteger;
import java.util.Random;public class Test {
public static void main(String[] args) {
final int number = 7;
Random rand = new Random(System.nanoTime());
StringBuilder sb = new StringBuilder();
// 测试
System.out.println("字符串一次性求模");
for (int i = 0; i < 50; ++i) {
sb.append(rand.nextInt(10));
String str = sb.toString();
System.out.println(str);
System.out.println("My: " + calculateMod(str, number, 5));
System.out.println("Bi: " + new BigInteger(str).mod(new BigInteger(number + "")));
System.out.println();
}
System.out.println("\n字符串分解成多个子串求模");
sb.delete(0, sb.length());
int mod = 0;
for (int i = 0; i < 50; ++i) {
int value = rand.nextInt(10);
sb.append(value);
String str = sb.toString();
System.out.println(str);
// 把一个大串分成多个部分来进行求模
mod = calculateMod(mod + "" + value, number, 5);
System.out.println("My: " + mod);
System.out.println("Bi: " + new BigInteger(str).mod(new BigInteger(number + "")));
System.out.println();
}
} // 求表示整数的字符串str对整数number的模,dx表示读取子串的长度
public static int calculateMod(String str, final int number, final int dx) {
int mod = 0;
int end = 0;
for (int i = 0; i < str.length(); i += dx) {
end = i + dx;
end = end > str.length() ? str.length() : end;
String temp = str.substring(i, end);
mod = mod * (int)(Math.pow(10, temp.length())) + Integer.parseInt(temp);
mod %= number;
} return mod;
}
}
BigInteger bi=new BigInteger("10");
bi=bi.pow(100000);
System.out.println(bi.toString().length());
System.out.println(new Date().getTime()-start.getTime());差不多要3秒,所以你的思路在输入数据规模很大的时候时间开销与输入长度不成正比!
并且
1对7的余数为1
10对7的余数为3
100对7的余数为2
1000对7的余数为6
10000对7的余数为4
100000对7的余数为5
1000000对7的余数为1
接下来为132645的循环。
继续求232应该为;
((2 * 100%7)%7 + (3 * 10%7)%7 + (2 * 1%7)%7 ) %7 =( (2*2)%7 + (3*3)%7 + (2*1)%7 )%7 = 8%7=1
对大数可以依次类推
而且楼主说得很清楚,这个数可以大到内存装不下,所以BigInteger不能解决这个问题
如 1%8=1 10%8=2 100%8=4 1000%8=0,之后就是全0了而楼主判断大数余数的算法的不足在与是从低位到高位计算的,所以需要全部读入内存,然后处理。其实完全可以从高位往低位逐位计算。
只要一个循环,逐字符读入文件,就可以完成计算了。
原理;
若数字A%x=a
则进位后10A % x = (10 * (A % x)) % x
程序稍后再贴
System.out.println(mod7("17777777777777777777715"));
} public static int mod7(String num) {
int sign = 1;
char[] digits = null;
if(num.charAt(0) == '-') {
sign = -1;
digits = num.substring(1).toCharArray();
} else {
digits = num.toCharArray();
}
int mod = 0;
int k = digits.length;
for(int i = k - 1; i > -1; i--) {
mod += MODS[(k - i - 1) % MODS.length] * (digits[i] - '0');
mod %= 7;
}
return mod * sign;
}
}
真的很简单哦public class ModOfNumberInFile {
public static void main(String[] args) throws IOException {
Reader f = new FileReader("number.txt"); //大数在此文件中,字符形式记录
int d;
int result = 0;
while((d=f.read())!=-1){
result = (result*10+((char)d-'0'))%7;
}
System.out.println("文件中数Mod7的余数是:" + result);
}
}
楼上火龙果的代码也不长,不过还没看明白。再看看。
static String num = "14141412345787709787897419874985791709878925723758923752897592837279701751751881758573153"+
"3091888883098127489175891275871892759175091729571927509170917";
static int[] mods = new int[]{1,3,2,6,4,5};
public static void main(String[] args) {
long time1 = System.nanoTime();
int result = 0;
for(int i = 0;i < num.length();i++){
result += Integer.parseInt(num.charAt(num.length() - i - 1)+"") * mods[i % mods.length];
}
result %= 7;
long time2 = System.nanoTime();
System.out.println("数字长度:" + num.length());
System.out.println("结果:" + result);
System.out.println("时间:" + (time2-time1)* 1.0/Math.pow(10, 9)+"秒");
}}省略了读文件的过程。
结果:
数字长度:150
结果:6
时间:6.27917E-4秒
基于一般规律 (a+b)%c=(a%c+b%c)%c; ①考虑的,19L,26L给出了程序,不过26L的有点考虑不足,数据过大,result在for里就溢出了。
16L给出的规律挺好的,(10*A) % x = (10 * (A % x)) % x ② ,20L给出了代码
这里就替20L解释一下20L的代码吧
假设A%x=result,那么根据一般规律①展开,(10*A+B)%x = ((10*A)%x + B%x)%x
根据②规律,(10*A) % x = (10 * (A % x)) % x代入右边
(10*A+B)%x = (10*(A%x) + B%x)%x = (10*result + B%x)%x
根据一般规律①展开,(10*result + B%x)%x = ((10*result)%x + (B%x)%x)%x
而我们知道,一个数对某个数取余后在对该某个数取余还是等于头一次取余的结果,
即A%x == (A%x)%x == ((A%x)%x)%x ... ,不管后面多少次重复取余,结果都是头一次
所以((10*result)%x + (B%x)%x)%x = ((10*result)%x + B%x)%x
根据一般规律①等号两边交换位置,((10*result)%x + B%x)%x = (10*result + B)%x
所以(10*A+B)%x = (10*result + B)%x,把(10*A+B)%x看成下一个result,于是可得20L的代码
result = (result*10+((char)d-'0'))%7;
假设A%x=result,那么根据一般规律①展开,(10*A+B)%x = ((10*A)%x + B%x)%x
根据②规律,(10*A) % x = (10 * (A % x)) % x代入右边
(10*A+B)%x = ((10*(A%x))%x + B%x)%x = ((10*result)%x + B%x)%x根据一般规律①等号两边交换位置,((10*result)%x + B%x)%x = (10*result + B)%x
所以(10*A+B)%x = (10*result + B)%x,把(10*A+B)%x看成下一个result,于是可得20L的代码
result = (result*10+((char)d-'0'))%7;
10L和16L(20L)的规律是一样的
即 (A*B)%x = (A*(B%x))%x = ((A%x)*(B%x))%x
所以20L的 result = (result*10+((char)d-'0'))%7; 改为
result = (result*(10%7)+((char)d-'0'))%7; 即result = (result*3+((char)d-'0'))%7;结果是一样的
result = (result*10+((char)d-'0'))%7; 用到了(A*B)%x = (A*(B%x))%x
result = (result*3+((char)d-'0'))%7;用到了(A*B)%x = ((A%x)*(B%x))%x而10L的 mod相当于20L的result,(int)(Math.pow(10, temp.length())) 相当于10,Integer.parseInt(temp)相当于(char)d-'0'10L代码测试代码部分过多,核心部分没突出,所以一开始没看,忽略了,现在细看才发现10L和20L的是在同一规律的基础上实现的,只是20L是一位一位算,10L是多位多位算的,只要保证多位的时候不超出int范围就可以。