简单算法实现之《中国剩余定理》

中国剩余定理
这个定理之前没见过,是在RSA算法原理(一)中提到的,想深入了解的可以去读下。

故事背景
  • 韩信点兵

秦朝末年,楚汉相争。一次,韩信将1500名将士与楚王大将李锋交战。苦战一场,楚军不敌,败退回营,汉军也死伤四五百人,于是韩信整顿兵马也返回大本营。当行至一山坡,忽有后军来报,说有楚军骑兵追来。只见远方尘土飞扬,杀声震天。汉军本来已十分疲惫,这时队伍大哗。韩信兵马到坡顶,见来敌不足五百骑,便急速点兵迎敌。他命令士兵3人一排,结果多出2名;接着命令士兵5人一排,结果多出3名;他又命令士兵7人一排,结果又多出2名。韩信马上向将士们宣布:我军有1073名勇士,敌人不足五百,我们居高临下,以众击寡,一定能打败敌人。汉军本来就信服自己的统帅,这一来更相信韩信是“神仙下凡”、“神机妙算”。于是士气大振。一时间旌旗摇动,鼓声喧天,汉军步步进逼,楚军乱作一团。交战不久,楚军大败而逃。

题目就是:将军点兵,三三数余2,五五数余3,七七数余2。问兵几何?

  • 孙子算经

今有物,不知其数,三三数之,剩二,五五数之,剩三,七七数之,剩二,问物几何?
答曰:二十三  
术曰:三三数之剩二,置一百四十,五五数之剩三,置六十三,七七数之剩二,置三十,并之,得二百三十三,以二百一十减之,即得。凡三三数之剩一,则置七十,五五数之剩一,则置二十一,七七数之剩一,则置十五,即得。

《孙子算经》里给出了解决办法

步骤一
除3余2 -> 取140
除5余3 -> 取63
除7余2 -> 取30
步骤二:
对上面取到的数求和
140 + 63 + 30 = 233
步骤三
用上面求出的结果减去210,就是结果
233 - 210 = 23
结果也就是23了

不过那140,63,30,还有210是怎么来的呢?这就涉及到中国剩余定理的算法原理了。

中国剩余定理原理

#题目:
设要求的数为x,每个除法的结果分别为k1,k2,k3,那么就有:
[1]  x / 3 = k1 + 2
[2]  x / 5 = k2 + 3
[3]  x / 7 = k3 + 2
1.对步骤一中的式子1求解过程为:取式子2和式子3中除数(5和7)的最小公倍数LCM(35),
然后把这个LCM作为x带入式子1。如果LCM不能适应式子1,就对LCM再加上一个LCM,
然后带入式子1中......直到满足式子1为止。

按照这个求解过程,分别对3个式子求解,取到的结果为:35,63,30
2.把上面求到的结果求和

35 + 63 + 30 = 128
3.这里对3个式子的除数取最小公倍数

3,5,7的最小公倍数为105
然后用上面的结果减去这个最小公倍数,就是结果了
128 - 105 = 23

这里估计有人要问了:这个35也不是140,而且105也不等于210啊?! 这个不用纠结,这个跟古代数学的发展史有关嘛! 当然,这是我瞎说的,想要深挖的可以去研究下相关资料。

下面具体说代码实现

首先创建个model类,类里面有除数和余数2个属性,然后提供一个便利构造器,方便使用

@interface CRTModel : NSObject

@property (assign, nonatomic) NSInteger divider;

@property (assign, nonatomic) NSInteger remain;

+(instancetype)modelWithDivider:(NSInteger)divider
                         remain:(NSInteger)remain;

@end

@implementation CRTModel

+(instancetype)modelWithDivider:(NSInteger)divider remain:(NSInteger)remain
{
    CRTModel *model = [[self alloc] init];
    model.divider = divider;
    model.remain = remain;
    return model;
}

@end

还需要用到2个函数,最大公约数GCD和最小公倍数LCM

-(NSInteger)gcdOf:(NSInteger)a and:(NSInteger)b
{
    //这里使用欧几里德算法
    static const NSInteger(^GCDRecursionBlock)(NSInteger,NSInteger) 
    = ^(NSInteger ra, NSInteger rb){
        if (!ra || !rb) return MAX(ra, rb);
        return GCDRecursionBlock(rb,ra%rb);
    };
    return GCDRecursionBlock(a,b);
}

//最小公倍数
-(NSInteger)lcmOf:(NSInteger)a and:(NSInteger)b
{
    return a * b / [self gcdOf:a and:b]; //最小公倍数等于两数之积除以最大公约数
}

然后开始实现算法

0.先创建3个Model,把题目"抄"一遍

CRTModel *model1 = [CRTModel modelWithDivider:3 remain:2];
CRTModel *model2 = [CRTModel modelWithDivider:5 remain:3];
CRTModel *model3 = [CRTModel modelWithDivider:7 remain:2];
1.取到算法中步骤1的结果:

-(NSInteger)getSubMinNumberOfDivider1:(NSInteger)divider1
                              divider2:(NSInteger)divider2
                           andDivider3:(NSInteger)divider3
                             remainOf3:(NSInteger)remainOf3
{
    NSInteger lcm = [self lcmOf:divider1 and:divider2];
    NSInteger result = lcm;
    while ((result % divider3) != remainOf3) {
        result += lcm;
    }
    return result;
}

NSInteger a = [self getSubMinNumberOfDivider1:model1.divider divider2:model2.divider andDivider3:model3.divider remainOf3:model3.remain];
NSInteger b = [self getSubMinNumberOfDivider1:model2.divider divider2:model3.divider andDivider3:model1.divider remainOf3:model1.remain];
NSInteger c = [self getSubMinNumberOfDivider1:model3.divider divider2:model1.divider andDivider3:model2.divider remainOf3:model2.remain];
2.求和

NSInteger sum = a + b + c;
3.求全部除数的最小公倍数,然后求结果

//使用"更相减损术"
NSInteger lcmOfAll = 
    [self lcmOf:[self lcmOf:model1.divider and:model2.divider] and:model3.divider];
    
//求结果
NSInteger result = sum - lcmOfAll;
NSLog(@"计算结果为:%ld + %ld * k (k为自然数)",result,lcmOfAll);    

算法代码已经上传GitHub:
https://github.com/Bluelich/MyBlogCode/tree/master/CRTAlgorithm

就这么多了。这是我昨晚回顾RSA加密时候看到的,刚好就学习下了,其实想再复习下欧拉函数什么的,都忘光了! 有时间就搞!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容