簡單算法實(shí)現(xiàn)之《中國剩余定理》

中國剩余定理
這個定理之前沒見過,是在RSA算法原理(一)中提到的,想深入了解的可以去讀下。

故事背景
  • 韓信點(diǎn)兵

秦朝末年,楚漢相爭。一次,韓信將1500名將士與楚王大將李鋒交戰(zhàn)。苦戰(zhàn)一場,楚軍不敵,敗退回營,漢軍也死傷四五百人,于是韓信整頓兵馬也返回大本營。當(dāng)行至一山坡,忽有后軍來報,說有楚軍騎兵追來。只見遠(yuǎn)方塵土飛揚(yáng),殺聲震天。漢軍本來已十分疲憊,這時隊伍大嘩。韓信兵馬到坡頂,見來敵不足五百騎,便急速點(diǎn)兵迎敵。他命令士兵3人一排,結(jié)果多出2名;接著命令士兵5人一排,結(jié)果多出3名;他又命令士兵7人一排,結(jié)果又多出2名。韓信馬上向?qū)⑹總冃迹何臆娪?073名勇士,敵人不足五百,我們居高臨下,以眾擊寡,一定能打敗敵人。漢軍本來就信服自己的統(tǒng)帥,這一來更相信韓信是“神仙下凡”、“神機(jī)妙算”。于是士氣大振。一時間旌旗搖動,鼓聲喧天,漢軍步步進(jìn)逼,楚軍亂作一團(tuán)。交戰(zhàn)不久,楚軍大敗而逃。

題目就是:將軍點(diǎn)兵,三三數(shù)余2,五五數(shù)余3,七七數(shù)余2。問兵幾何?

  • 孫子算經(jīng)

今有物,不知其數(shù),三三數(shù)之,剩二,五五數(shù)之,剩三,七七數(shù)之,剩二,問物幾何?
答曰:二十三  
術(shù)曰:三三數(shù)之剩二,置一百四十,五五數(shù)之剩三,置六十三,七七數(shù)之剩二,置三十,并之,得二百三十三,以二百一十減之,即得。凡三三數(shù)之剩一,則置七十,五五數(shù)之剩一,則置二十一,七七數(shù)之剩一,則置十五,即得。

《孫子算經(jīng)》里給出了解決辦法

步驟一
除3余2 -> 取140
除5余3 -> 取63
除7余2 -> 取30
步驟二:
對上面取到的數(shù)求和
140 + 63 + 30 = 233
步驟三
用上面求出的結(jié)果減去210,就是結(jié)果
233 - 210 = 23
結(jié)果也就是23了

不過那140,63,30,還有210是怎么來的呢?這就涉及到中國剩余定理的算法原理了。

中國剩余定理原理

#題目:
設(shè)要求的數(shù)為x,每個除法的結(jié)果分別為k1,k2,k3,那么就有:
[1]  x / 3 = k1 + 2
[2]  x / 5 = k2 + 3
[3]  x / 7 = k3 + 2
1.對步驟一中的式子1求解過程為:取式子2和式子3中除數(shù)(5和7)的最小公倍數(shù)LCM(35),
然后把這個LCM作為x帶入式子1。如果LCM不能適應(yīng)式子1,就對LCM再加上一個LCM,
然后帶入式子1中......直到滿足式子1為止。

按照這個求解過程,分別對3個式子求解,取到的結(jié)果為:35,63,30
2.把上面求到的結(jié)果求和

35 + 63 + 30 = 128
3.這里對3個式子的除數(shù)取最小公倍數(shù)

3,5,7的最小公倍數(shù)為105
然后用上面的結(jié)果減去這個最小公倍數(shù),就是結(jié)果了
128 - 105 = 23

這里估計有人要問了:這個35也不是140,而且105也不等于210???! 這個不用糾結(jié),這個跟古代數(shù)學(xué)的發(fā)展史有關(guān)嘛! 當(dāng)然,這是我瞎說的,想要深挖的可以去研究下相關(guān)資料。

下面具體說代碼實(shí)現(xiàn)

首先創(chuàng)建個model類,類里面有除數(shù)和余數(shù)2個屬性,然后提供一個便利構(gòu)造器,方便使用

@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個函數(shù),最大公約數(shù)GCD和最小公倍數(shù)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);
}

//最小公倍數(shù)
-(NSInteger)lcmOf:(NSInteger)a and:(NSInteger)b
{
    return a * b / [self gcdOf:a and:b]; //最小公倍數(shù)等于兩數(shù)之積除以最大公約數(shù)
}

然后開始實(shí)現(xiàn)算法

0.先創(chuàng)建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的結(jié)果:

-(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.求全部除數(shù)的最小公倍數(shù),然后求結(jié)果

//使用"更相減損術(shù)"
NSInteger lcmOfAll = 
    [self lcmOf:[self lcmOf:model1.divider and:model2.divider] and:model3.divider];
    
//求結(jié)果
NSInteger result = sum - lcmOfAll;
NSLog(@"計算結(jié)果為:%ld + %ld * k (k為自然數(shù))",result,lcmOfAll);    

算法代碼已經(jīng)上傳GitHub:
https://github.com/Bluelich/MyBlogCode/tree/master/CRTAlgorithm

就這么多了。這是我昨晚回顧RSA加密時候看到的,剛好就學(xué)習(xí)下了,其實(shí)想再復(fù)習(xí)下歐拉函數(shù)什么的,都忘光了! 有時間就搞?。?/p>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容