* 无限位数相除的设计:
* 规则:
* 1.从被除数的最高位起算,依据除数的位数,选取被除数相同的位数,若该取位数小于除数,则多取一位为取位数;
* 2.初步口算或笔算一个商数,该商数应满足:与除数相乘所得的积等于或小于被除数取位的数,若小于相减后又必须小于除数;
* 3.被除后的余数进行下一位除法计算前,把被除数的下一位放在余数的后面整合为新的取位数;
* 4.若取位数小于除数,则商补一位0,同时把被除数的下一位放在余数的后面整合为新的取位数;
* 5.若被除数小于除数,则加小数点,同时被除数乘10(即在被除数最后面加0),进行小数运算,余数不为0,则进行下一位小数运行,运算前把余数乘10,若乘10后还小于除数则分别把商和余数补一个0后继续运算;
* 除法计算法则歌决 除数是一位, 先看前一位;一位不够,看两位,看到哪位,商哪位;不够商1,0占位;余数要比除数小,然后再除下一位。 除数是两位, 先看前两位;两位不够,看三位,看到哪位,商哪位;不够商1,0占位;余数要比除数小,然后再除下一位。
* 整数的除法法则:1)从被除数的高位起,先看除数有几位,再用除数试除被除数的前几位,如果它比除数小,再试除多一位数;2)除到被除数的哪一位,就在那一位上面写上商;3)每次除后余下的数必须比除数小。
* 除数是整数的小数除法法则:1)按照整数除法的法则去除,商的小数点要和被除数的小数点对齐;2)如果除到被除数的末尾仍有余数,就在余数后面补零,再继续除。
* 除数是小数的小数除法法则:1)先看除数中有几位小数,就把被除数的小数点向右移动几位,数位不够的用零补足;2)然后按照除数是整数的小数除法来除。
* 除法的本质是减法:如9/2,用9不断减2直到余数小于2,所减的次数就是商,即9-2-2-2-2减4次后,余1,余数再乘10后与2作减法,就是小数,减到余数为0止,即10-2-2-2-2-2减5次后余数为0表明被整除,所以,9/2=4.5;
* 这里要设计的整数除法是真正的无限位,和上面的加减乘一样都是真正的无限位运算,如:壹千八百位的被除数和壹千六百位的除数进行除法运算或是两万八百位的被除数和壹千六百位的除数进行除法运算;
* 除法规则多,则逻辑点也多,调试与修改也多,确花费2天左右构思,3天左右调试和验证,不像加减法一气呵成,乘法略花心思,不过再难的逻辑也架不住我一天中或上午改一个逻辑,或下午改一个逻辑,或晚上改一个逻辑,渐渐地就写好了,写程序就是慢工细活,急不得,越急越做不来,如果遇到更难的逻辑设计,通常我的办法就是完全不去理会几天后再去想,也可避开了受智力节律的影响.
static string 无限位数相除(string 被除数, string 除数, string 精度 = "9")
{
if (被除数 == "0") return "非法操作,被除数不得为0";
if (除数 == "1") return 被除数;/*针对乘数“1”的优化,不入运算*/
string 商 = "", 余数 = "", 取位数 = "";
int 位1 = 被除数.Length, 移位 = 0, 位商 = 0, 补位 = 0, 计数 = 0;
取位数 = 被除数.Substring(移位, 除数.Length);/*首次取位*/
Action 求位商 = delegate()
{
do
{
if (取位数 != "0")
余数 = 无限位数相减(取位数, 除数);
if (余数[0] != '被')
{
++位商;
取位数 = 余数;
补位 = 0;
}
else
{
++补位;
break;
}
} while (余数 != "0");
if (移位 < 位1)
{
移位 += 移位 == 0 ? 除数.Length : 1;
if (余数 == "0") ++补位;
}
};
尝试:
求位商();
if (位商 > 0)
{
商 += 位商.ToString();
位商 = 0;
if (商.Contains(".")) ++计数;
}
if (余数[0] == '被')
{
if (移位 > 0) if (补位 > 1)/*商数加0*/
{
商 += "0";
if (商.Contains(".")) ++计数;
}
if (精度 != "0" && 精度 == 计数.ToString() || (精度 == "0" && 移位 == 位1)) return 商;
if (移位 == 位1 && !商.Contains(".")) 商 += ".";
//if (商.Contains(".")) if (精度 == 商.Split('.')[1].Length.ToString()) return 商;
取位数 += (移位 < 位1) ? 被除数.Substring(移位, 1) : "0";/*余数加0*/
//if (商.Contains(".")) Console.WriteLine("运算:{0}", 商);
goto 尝试;
}
else
if (移位 < 位1)
{
取位数 = 被除数.Substring(移位, 1);
goto 尝试;
}
return 商;
}
调用:
Console.WriteLine("正确运算:{0}", 无限位数相除("104104144144", "4"));
Console.WriteLine("祖率:{0}", 无限位数相除("355", "113"));
验证:
Double 被除数 = int.MaxValue, 除数 = 2, 商 = 0, 商1 = 0;
do
{
商 = Double.Parse(无限位数相除(被除数.ToString(), 除数.ToString()));
商1 = 被除数 / 除数;
if (商.ToString("0.00000") == 商1.ToString("0.00000"))
Console.WriteLine("整除:{0} {0}", 商.ToString("0.00000"), 商1.ToString("0.00000"));
else
{
Console.WriteLine("差异:{0} {0}", 商.ToString("0.00000"), 商1.ToString("0.00000"));
//break;
}
} while (++除数 <= 被除数);
断断续续的设计及调试和验证及修改,到2015-10-16完成,代码未进行优化整理,许多逻辑问题都是在验证过程修改到正确的.
求质数:先写一个int范围内用除法运行的例子,再改为无限位运算就容易多了,不过这里也是有限位的,因为字符串寻址范围int最大值限制了,暂时就写这么大范围运算了,要真的无限位,就要扩展位可接续的运算方式,待空闲时再说;
int 被除数 = 2, 除数 = 2;
Action 前进 = delegate()
{
除数 = 2;
++被除数;
};
do
{
string 商 = 无限位数相除(被除数.ToString(), 除数.ToString(), "1");
if (除数 < 被除数)
if (!商.Contains("."))
前进();
else
++除数;
else
{
Console.Write("{0} ", 被除数);
前进();
}
} while (int.MaxValue > 被除数);
改上求素数为无限位,暂时限制int最大值位字符串范围
//最优化字符串求质数算法:
string 被除数 = "2", 除数 = "2", 除数后 = "2", 文件序 = "";//, 最小公倍集 = string.Join("\r\n", 读文本记录(@System.Environment.CurrentDirectory + "\\求质数\\最小公倍集.txt"));
List<string> 读被除数 = 读文本记录(@System.Environment.CurrentDirectory + "\\求质数\\被除数.txt");
List<string> 读质数集 = 读文本记录(@System.Environment.CurrentDirectory + "\\求质数\\质数集.txt");
int 质素序 = 0, 开方序 = 1, 最大开方 = 1;
Func<string, string> 增进 = delegate(string 增数)
{
return 增数 = 无限位数相加(增数, 增数 == "2" ? "1" : "2")[0];
};
Action 前进 = delegate()
{
除数 = "2";
除数后 = "2";
质素序 = 1;
被除数 = 增进(被除数);
};
Action 开方 = delegate()
{
bool 正开方 = 无限位数相乘(除数, 除数) == 被除数, 后开方 = 无限位数相乘(除数后, 除数后) == 被除数;
if (正开方 || 后开方)
{
最大开方 = 读质数集.LastIndexOf((正开方 ? 除数 : 除数后)) + 1;
开方序 = 最大开方;
写改记录((正开方 ? 除数 : 除数后), "求质数", "最大开方", 文件序, false);
}
};
if (读被除数.Count > 0)
被除数 = 增进(读被除数[0]);
if (读质数集.Count > 0)
读质数集 = 读质数集[0].Split(' ').ToList();
else
读质数集.Add("");
开方序 = 最大开方 = 读质数集.LastIndexOf(读文本记录(@System.Environment.CurrentDirectory + "\\求质数\\最大开方.txt")[0]) + 1;
do
{
文件序 = System.DateTime.Now.ToString().Replace("/", "-").Replace(":", ".");
string 商 = 无限位数相除(被除数, 除数, "1");
string 商后 = 无限位数相除(被除数, 除数后, "1");
bool 正商 = 商.Contains("."), 正商后 = 商后.Contains(".");
if (除数 != 被除数 && 除数后 != 被除数 && 开方序 >= 质素序)
if (!正商 || !正商后)
{
//if (!最小公倍集.Contains(" " + (!正商 ? 除数 : 除数后)))
//{
// 最小公倍集 += "\r\n" + 被除数 + " " + (!正商 ? 除数 : 除数后);
// 写改记录(最小公倍集, "求质数", "最小公倍集", 文件序, false);
//}
开方();
开方序 = 最大开方;
前进();
}
else
{
除数 = 开方序 > 4 ? 读质数集[++质素序] : 增进(除数);
除数后 = 开方序 > 4 ? 读质数集[开方序--] : 除数后;
}
else
{
写改记录(" " + 被除数, "求质数", "质数集", 文件序);
写改记录(被除数, "求质数", "被除数", 文件序, false);
Console.Write("{0} ", 被除数);
读质数集.Add(被除数);
开方序 = 最大开方;
前进();
}
} while (被除数.Length < int.MaxValue);
using System.IO;
using System.Threading;
static void 写改记录(string 输出内容, string 文件夹, string 文件名, string 文件序, bool 选择 = true)
{
String 保存路径 = @System.Environment.CurrentDirectory + "\\" + 文件夹;
string 创建文件 = @保存路径 + "\\" + 文件序 + ".txt ";
try
{
Thread 写入文本 = new Thread(delegate()
{
if (!Directory.Exists(保存路径))
Directory.CreateDirectory(保存路径);
String 保存文件名 = @保存路径 + "\\" + 文件名 + ".txt ";
FileInfo 文件 = new FileInfo(保存文件名);
if (!文件.Exists)
{
FileStream 创建或覆盖 = File.Create(保存文件名);
创建或覆盖.Flush();
创建或覆盖.Close();
}
else
{//FileStream 创建只写文件 = 文件.OpenWrite(); 创建只写文件.Close(); 文件.Refresh(); StreamWriter 写入 = File.AppendText(保存文件名);//以可以追加文本的方式打开文件流
StreamWriter 写入 = new StreamWriter(保存文件名, 选择, Encoding.GetEncoding("GB2312"));
if (选择) 写入.Write(输出内容); else 写入.WriteLine(输出内容);
写入.Flush();
写入.Close();
if (文件.Length > 1000 * 1024)
文件.MoveTo(创建文件);
}
});
写入文本.Start();
写入文本.Join();
}
catch (Exception Ts)
{ Console.WriteLine("!" + Ts.ToString()); }
finally { }
}
static List<string> 读文本记录(string 文件路径)
{
List<string> 内容 = new List<string>();
if (Directory.Exists(文件路径.Remove(文件路径.LastIndexOf("\\"))))
{
FileInfo 文件 = new FileInfo(文件路径);
if (!文件.Exists)
{
FileStream 创建或覆盖 = File.Create(文件路径);
创建或覆盖.Close();
}
if (文件.Exists)
using (FileStream 打开 = new FileStream(文件路径, FileMode.Open))
{
using (StreamReader 读取 = new StreamReader(打开, 获取文本编码(打开, Encoding.GetEncoding("GB2312"))))
while (!读取.EndOfStream) 内容.Add(读取.ReadLine());
}
}
return 内容;
}
static Encoding 获取文本编码(FileStream 打开文件, Encoding 编码)
{
Encoding 获取编码 = 编码;
if (打开文件 != null && 打开文件.Length >= 2)
{
byte byte1 = 0, byte2 = 0, byte3 = 0;
long Seek位置 = 打开文件.Seek(0, SeekOrigin.Begin);//保存当前Seek位置
打开文件.Seek(0, SeekOrigin.Begin);
byte1 = Convert.ToByte(打开文件.ReadByte());
byte2 = Convert.ToByte(打开文件.ReadByte());
if (打开文件.Length >= 3)
byte3 = Convert.ToByte(打开文件.ReadByte());
if (byte1 == 0xFE && byte2 == 0xFF)
获取编码 = Encoding.BigEndianUnicode;
if (byte1 == 0xFF && byte2 == 0xFE && byte3 != 0xFF)
获取编码 = Encoding.Unicode;
if (byte1 == 0xEF && byte2 == 0xBB && byte3 == 0xBF)
获取编码 = Encoding.UTF8;
打开文件.Seek(Seek位置, SeekOrigin.Begin);//恢复Seek位置
}
return 获取编码;
}
以前回答小范围内的运算不会去考虑优化算法提高效率问题,如今,求素数为int.MaxValue位字符串范围进行运算,这么大的范围就不得不考虑了,优化就很必要了,从4点去优化:
1.被除数不按1步进,改为2步进,质数2之后的都是奇数,如3开始按步进2进行就都是奇数了,从而跳过偶数来减少循环计算量;
2.除数不按1步进,而从已找到的质数集合中取,这样就可以避开2 3 5 7 11等的倍数,其一所有偶数都是2的倍数,其二能被前面奇数排除的自然也不在质数内,所以用质数作除数是最合理的减少循环计算量;
3.一个数的最大倍数是它的平方根,在质数集中找到该平方根的序,再往后推一位序,就是取下一位质数来做除数,把除数的取数范围安排在最合理的区间内来减少循环计算量;
4.采用双向运算来提高剔除非质数的速度来减少循环计算量;
通过以上4处优化,显著提高了运算速度,以前2天的计算量,现在轻松做到.
是否进行优化是要有分析依据的.
当然了,已经公布的这四则运算都未经细致优化,自然还可以继续优化,那就留待给后生了,这个除法就没写舍入的功能哦.
添加舍入功能的修改大致思路:static string 无限位数相除(string 被除数, string 除数, string 精度 = "9", int 舍入 = 0)
计数 = 舍入 == 0 ? 0 : -1;
if (位商 > 0)
{
int 精控 = 0;
if (商.Contains("."))
{
if (舍入 > 0)
{
if (精度 == (计数 - 1).ToString())
精控 = 位商;
if (精度 == 计数.ToString())
if (位商 >= 舍入) ++精控;
}
++计数;
}
商 += (精控 > 0 && 精度 == 计数.ToString() ? 精控 : 位商).ToString();
位商 = 0;
}
未经测试