用.NET写“算命”程序
前言
“算命”,是一种迷信,我父亲那一辈却执迷不悟,有时深陷其中,有时为求一“上上签”,甚至不惜重金,向“天神”保佑。我曾看到过有些算命网站,可以根据人的生辰八字,来求得这个人一生的财运、桃花运,如果第一卦算得不好,还可以向“天神”“请愿”(充钱),再算一卦,直到达到好运为止。
作为一个深信唯物辩证法的人来说,这些东西当然是不信。
但仔细琢磨,发现这些东西其中需要有些科学道理。我可以将算命总结为以下“三要素”:
一致性
“命中注定”,因此“算”出来的东西,不管早算还是晚算,什么时候算,结果应该都一样。
无规律性
“天机不可泄露”,因此输入相近的姓名等参数,输出应该相差较远。“每个人的命运各不相同”,比如狗二和狗三,相差只有一个字,但他们的命运并不一定会几乎一样。算法应该也考虑这一点。
个性化
输入参数应该尽量个性化,不要像十二生肖/十二星座那样,和性别做排列组合,只有12x2=24种结果。否则撞车的人太多,容易露馅?。因此输入参数必须个性化,最好是姓名、性别再加上生辰八字(出生时间)。
可操作性
孜孜不倦的求卦者,可能会“诚心诚意”想求个“上上签”,因此在一致性的基础上,必须要加一点点“可操作性”。这个可以当作一个单独的输入参数来表示。
如果将算命当作一个函数,那它的输入无疑是姓名、其它个人信息和诚心,输出就是一个分数(0-100),可以用下图的代码表示:
int destinyScore = f(name, otherPersonalInformation, faith);
下面,我将用.NET实现这个功能。
最简单的“算命”程序
最初想法
如果只以姓名作为输入,那么这个函数可以简化为:
int destinyScore = f(name);
这可能就好办多了,如.NET中的.GetHashCode(),即可快速获取一个字符串的哈希值,这个哈希值应该是固定的(吗?),该值的取值范围是int.MinValue-int.MaxValue。因此最简单的办法,可以先可以通过对100求模,此时的取值范围是-99~99;然后再取绝对值+1即可,代码如下:
int GetForturn(string name)
{ return Math.Abs(name.GetHashCode() % 100) + 1;
}
在.NET Framework 4.8中运行,可以算出我(周杰)的得分固定为15分。
最简单算法的缺点-.NET Core的不一致
在.NET Core中,这个算法每次重新运行,算出的结果都不同,因为.NET Core为了确保安全性,在应用程序启动时,会随机生成一个字符串哈希值种子,因此每次exe运行,哈希值都会变,文档是这么说的:
哈希代码本身不一定是稳定的。 对于单个版本的 .NET, 相同字符串的哈希代码可能跨 .net 实现、跨 .NET 版本和跨 .NET 平台 (如32位和64位) 不同。 在某些情况下, 它们甚至不同于应用程序域。 这意味着, 同一程序的两次后续运行可能返回不同的哈希代码。(源自:https://docs.microsoft.com/zh-cn/dotnet/api/system.string.gethashcode?view=netframework-4.8 )
很显然,这不符合“一致性”,看来想简单地通过GetHashCode()快速“算命”的想法落空了,只能使用标准的哈希算法。
当然,使用如此简单的算法,客户知道了,可能也不太情愿消费更多的“诚意金”了。
哈希算法
哈希算法可以给任意长度的字符转换为一串二进制数组,也就是哈希值。.NET内置了许多不同的哈希算法可供选择:
1. 有单纯的哈希,如MD5、SHA1之类;
2. 有“加盐”的哈希,如HMACSHA、HMACSHA256等;
3. 有可指定生成长度、可多次迭代、综合性“加盐”的哈希,如Rfc2898DeriveBytes。
我们要指定一点点“天机”(加盐),但“天机不可泄露”,因此简单地MD5等单纯哈希算法排除;
我们要转化为一个整数,最大的整数类型,long/Int64,为64位,而最小的内置哈希算法,MD5,就已达128位。因此也要排除HMACSHA等“加盐”哈希。当然这些哈希值也可以手动截取部分长度,但安全性是个问号(也受强迫症影响)。
搞过ASP.NET Identity登录的都知道里面用到了Rfc2898DeriveBytes,它默认为ASP.NET Core做了10000次迭代,用多次迭代的方式(而不是引入一个新哈希算法的方式),确保了安全性。搞对称加密的时候,有时也用这个类将客户的密码转换为加密算法的密钥(key),非常有用。
所以最终我们选择了Rfc2898DeriveBytes,该算法可以生成任意指定长度的哈希值。这个类的构造函数要求输入一个盐值和迭代次数,在这个示例中我们取一个别人不知道的值(代码中写死了,你们假装不知道,你们想用这个代码时可以改改?)。可以写出如下代码:
int GetForturn(string name)
{ using (var h = new Rfc2898DeriveBytes(name, salt: new byte[8] { 44, 2, 3, 4, 5, 6, 7, 8}, iterations: 10086)) { return (int)(BitConverter.ToUInt64(h.GetBytes(8), 0) % 100) + 1; };
}
我从网上自动生成了888个姓名,然后调用该函数,发现得分超过90分“上上签”标准的,只有83个,相同于十分之一,符合分布特点(详情见Github上的代码)。可见算出一卦80分以上的“上签”,已经非常不容易了。
通过以下代码,可以算出“狗二”是48分,“狗三”是96分,可见一字之差相差甚远:
GetForturn("狗二").Dump(); // 48
GetForturn("狗三").Dump(); // 96
完整算法
最后,依葫芦画瓢,加上个人信息参数(生日)和“诚意金次数”,完成最后的算法:
int GetForturn(string name, DateTime birthDay, int faithCount)
{ using (var h = new Rfc2898DeriveBytes(name + birthDay + faithCount, salt: new byte[8] { 44, 2, 3, 4, 5, 6, 7, 8 }, iterations: 10086)) { return (int)(BitConverter.ToUInt64(h.GetBytes(8), 0) % 100) + 1; };
}
然后又是“狗二”和“狗三”,加上他们的生日参数后,默认他们的得分是95分和3分:
GetForturn("狗二", new DateTime(1994, 5, 17), 0).Dump(); // 95
GetForturn("狗三", new DateTime(1996, 11, 3), 0).Dump(); // 3
但狗三经过1次“诚意金”后,也求得了高达99分以上的“上上签”:
GetForturn("狗二", new DateTime(1994, 5, 17), 0).Dump(); // 98
GetForturn("狗三", new DateTime(1996, 11, 3), ).Dump(); // 99
最后的话
Rfc2898DeriveBytes非常有用,本文说了Rfc2898DeriveBytes的一种使用场景,相信各位在工作当时也经常会有机会去接触它。
我将上述功能做了一个页面,愿博君一笑:https://destiny.starworks.cc/
出处:微信公众号【DotNet骚操作】微信可能无法留言,可点击“阅读原文”转到博客园留言。原文链接:https://www.cnblogs.com/sdflysha/p/20190905-fortune-with-dotnet.html
觉得好看,请点这里↓↓↓
用.NET写“算命”程序相关推荐
- 用python语言写了一套在线算命程序 ,精准度怎么样?可以来试试
用python语言写了一套在线付费算命程序,分网页版和小程序版 包括八字精批 合婚 八字解名 婚姻 终身运势五大项.算法精准,回头客较多.有相关网站流量资源的可以坐等收益.python anli网zh ...
- 在线算命程序 源码 用python语言写的 算命的网络化发展趋势 你怎么理解的?
欢迎使用Markdown编辑器写博客 用python语言写了一套在线付费算命程序,分网页版和小程序版 包括八字精批 合婚 八字解名 婚姻 终身运势五大项.另外有完善的分销系统,有相关流量资源的适合做. ...
- 新版付费测算源码 星座运势 塔罗牌 牛年运程 宝宝起名 月老姻缘 起名算命程序第三版支持易支付
新版付费测算源码 星座运势 塔罗牌 牛年运程 宝宝起名 月老姻缘 起名算命程序第三版支持对接易支付,这是一款很不错的测算源码小白已经测试过了是能正常运营的,首页有三套模板原版的有点BUG我已经修复好了 ...
- 算命程序的网络化发展 实现占卜程序网络化 在线算命程序python编写 根据周易五行算法编写
用python语言编写开发出一套在线算命程序,属于占卜程序类,根据周易五行,实现出一套算法,这一套算命程序分为网页版和微信小程序版两种,每个版本包括5大版块,分别是八字精批.八字合婚.婚姻测算.起名解 ...
- 算命程序源码 实现一对多的网络算命发展趋势 python编写的,测算的精准度 页面的布局 欢迎业内人士指导改进
欢迎使用Markdown编辑器写博客 在线算命程序源码,python语言编写.有网页版本和小程序版本,包括八字精批 婚姻 八字解名 合婚 终身运势 .可以实现在线付款后 自动跳转测算页面.程序运行稳定 ...
- 自己写的程序密码功能 ------数字功能
自己写的程序密码功能 ------数字功能 class LockedViewController: UIViewController { var dataBase:FMDatabase? var i ...
- 【python编程导论】我想和你探讨下如何去写好程序
1. 题目来源 麻省理工的编程导论: Introduction to Computer Science and Programming Using Python 第一周课程的第三个编程题 Week 1 ...
- ACMNO.30 C语言-宏交换 定义一个带参的宏,使两个参数的值互换,并写出程序,输入两个数作为使用宏时的实参。输出已交换后的两个值。
题目描述 定义一个带参的宏,使两个参数的值互换,并写出程序,输入两个数作为使用宏时的实参.输出已交换后的两个值. 输入 两个数,空格隔开 输出 交换后的两个数,空格隔开 样例输入 1 2 样例输出 2 ...
- /* * 编程题第五题(20分): 本题要求你写个程序把给定的符号打印成沙漏的形状。例如给定17个“*”,要求按下列格式打印 ***** *** * *** ***** 所谓“
题目: 编程题第五题(20分): 本题要求你写个程序把给定的符号打印成沙漏的形状.例如给定17个"*",要求按下列格式打印 ***************** 所谓" ...
最新文章
- 全凭“脚感”,这个不看路的机器人就能稳稳爬楼梯,一脚踩空也不怕 | RSS 2021...
- CSS公共清除浏览器默认样式
- Python为什么这么厉害? 不想成为专业码农? 来学习Python吧!
- 二分法求方程的根_快速求解方程的根——二分法与牛顿迭代法
- matlab ode45 二阶微分,matlab关于ode45解二阶微分方程的困惑
- ORA-01841: (完整) 年份值必须介于 -4713 和 +9999 之间, 且不为 0情况解决
- mysql服务器失败1396_MYSQL ERROR 1396 (HY000) 错误的解决办法
- spring boot: 组合注解与元注解
- 《Web应用基础》课程结业报告
- 成功解决TypeError: Encoders require their input to be uniformly strings or numbers.
- Hybrid接口、MSTP协议
- 网络安全——攻防对抗
- No enclosing instance of type FormDetailBean is accessible. Must qualify the allocation with an encl
- 十只老鼠在1000瓶药水中找一瓶有毒的
- 戴尔服务器显示屏报警PDR1101 fault detected on drive 3. Check drive
- 教你修改网卡物理地址(MAC)
- Flutter之Toast
- 5.0 DataView使用说明
- 字符集、标识符、常量与变量
- 大型会议的礼仪(2)