標籤: 暫無標籤

模運算即求余運算。「模」是「Mod」的音譯,模運算多應用於程序編寫中。 Mod的含義為求余。模運算在數論和程序設計中都有著廣泛的應用,從奇偶數的判別到素數的判別,從模冪運算到最大公約數的求法,從孫子問題到凱撒密碼問題,無不充斥著模運算的身影。

  

  例如11 Mod 2,值為1

  上述模運算多用於程序編寫,舉一例來說明模運算的原理:

  Turbo Pascal對mod的解釋是這樣的:

  A Mod B=A-(A div B) * B (div含義為整除)

1 模運算 -模運算及其性質

  本文以c++語言為載體,對基本的模運算應用進行了分析和程序設計,以理論和實際相結合的方法向大家介紹模運算的基本應用。。

基本理論

  基本概念:

  給定一個正整數p,任意一個整數n,一定存在等式 n = kp + r ;

  其中k、r是整數,且 0 ≤ r < p,稱呼k為n除以p的商,r為n除以p的餘數。

  對於正整數p和整數a,b,定義如下運算:

  取模運算:a % p(或a mod p),表示a除以p的餘數。

  模p加法:(a + b) % p ,其結果是a+b算術和除以p的餘數,也就是說,(a+b) = kp +r,則(a + b) % p = r。

  模p減法:(a-b) % p ,其結果是a-b算術差除以p的餘數。

  模p乘法:(a * b) % p,其結果是 a * b算術乘法除以p的餘數。

  說明:

  1. 同餘式:正整數a,b對p取模,它們的餘數相同,記做 a ≡ b % p或者a ≡ b (mod p)。

  2. n % p得到結果的正負由被除數n決定,與p無關。例如:7%4 = 3, -7%4 = -3, 7%-4 = 3, -7%-4 = -3。

基本性質

  (1)若p|(a-b),則a≡b (% p)。例如 11 ≡ 4 (% 7), 18 ≡ 4(% 7)

  (2)(a % p)=(b % p)意味a≡b (% p)

  (3)對稱性:a≡b (% p)等價於b≡a (% p)

  (4)傳遞性:若a≡b (% p)且b≡c (% p) ,則a≡c (% p)

運算規則

  模運算與基本四則運算有些相似,但是除法例外。其規則如下:

  (a + b) % p = (a % p + b % p) % p (1)

  (a - b) % p = (a % p - b % p) % p (2)

  (a * b) % p = (a % p * b % p) % p (3)

  (a^b) % p = ((a % p)^b) % p (4)

  結合率: ((a+b) % p + c) % p = (a + (b+c) % p) % p (5)

  ((a*b) % p * c)% p = (a * (b*c) % p) % p (6)

  交換率: (a + b) % p = (b+a) % p (7)

  (a * b) % p = (b * a) % p (8)

  分配率: ((a +b)% p * c) % p = ((a * c) % p + (b * c) % p) % p (9)

  重要定理:若a≡b (% p),則對於任意的c,都有(a + c) ≡ (b + c) (%p);(10)

  若a≡b (% p),則對於任意的c,都有(a * c) ≡ (b * c) (%p);(11)

  若a≡b (% p),c≡d (% p),則 (a + c) ≡ (b + d) (%p),(a - c) ≡ (b - d) (%p),

  (a * c) ≡ (b * d) (%p),(a / c) ≡ (b / d) (%p); (12)

  若a≡b (% p),則對於任意的c,都有ac≡ bc (%p); (13)

2 模運算 -基本應用

  1.判別奇偶數

  奇偶數的判別是模運算最基本的應用,也非常簡單。易知一個整數n對2取模,如果餘數為0,則表示n為偶數,否則n為奇數。

  C++實現功能函數:

  "

  bool IsEven(int n)

  {

  return (n % 2 == 0);

  }

  2.判別素數

  一個數,如果只有1和它本身兩個因數,這樣的數叫做質數(或素數)。例如 2,3,5,7 是質數,而 4,6,8,9 則不是,後者稱為合成數或合數。

  判斷某個自然數是否是素數最常用的方法就是試除法:用比該自然數的平方根小的正整數去除這個自然數,若該自然數能被整除,則說明其非素數。

  C++實現功能函數:

  "

  bool IsPrime(unsigned int n)

  {

  unsigned maxFactor = sqrt(n); //n的最大因子

  for (unsigned int i=2; i<=maxFactor; i++)

  {

  if (n % i == 0) //n能被i整除,則說明n非素數

  {

  return false;

  }

  }

  return true;

  }

  3. 最大公約數

  求最大公約數最常見的方法是歐幾里德演算法(又稱輾轉相除法),其計算原理依賴於定理:gcd(a,b) = gcd(b,a mod b)

  證明:a可以表示成a = kb + r,則r = a mod b

  假設d是a,b的一個公約數,則有d|a, d|b,而r = a - kb,因此d|r

  因此d是(b,a mod b)的公約數

  假設d 是(b,a mod b)的公約數,則d | b , d |r ,但是a = kb +r

  因此d也是(a,b)的公約數

  因此(a,b)和(b,a mod b)的公約數是一樣的,其最大公約數也必然相等,得證。

  C++實現功能函數:

  "

  unsigned int Gcd(unsigned int a, unsigned int b)

  {

  if (b == 0)

  return a;

  return Gcd(b, a % b);

  }

  "

  unsigned int Gcd(unsigned int a, unsigned int b)

  {

  unsigned int temp;

  while (b != 0)

  {

  temp = a % b;

  a = b;

  b = temp;

  }

  return a;

  }

  4.模冪運算

  利用模運算的運算規則,我們可以使某些計算得到簡化。例如,我們想知道3333^5555的末位是什麼。很明顯不可能直接把3333^5555的結果計算出來,那樣太大了。但我們想要確定的是3333^5555(%10),所以問題就簡化了。

  根據運算規則(4)ab % p = ((a % p)b) % p ,我們知道3333^5555(%10)= 3^5555(%10)。由於3^4 = 81,所以3^4(%10)= 1。

  根據運算規則(3) (a * b) % p = (a % p * b % p) % p ,由於5555 = 4 * 1388 + 3,我們得到3^5555(%10)=(3^(4*1388) * 3^3)(%10)=((3^(4*1388)(%10)* 3^3(%10))(%10)

  =(1 * 7)(%10)= 7。

  計算完畢。

  利用這些規則我們可以有效地計算X^N(% P)。簡單的演算法是將result初始化為1,然後重複將result乘以X,每次乘法之後應用%運算符(這樣使得result的值變小,以免溢出),執行N次相乘后,result就是我們要找的答案。

  這樣對於較小的N值來說,實現是合理的,但是當N的值很大時,需要計算很長時間,是不切實際的。下面的結論可以得到一種更好的演算法。

  如果N是偶數,那麼X^N =(X*X)^&#91;N/2&#93;;

  如果N是奇數,那麼X^N = X*X^(N-1) = X *(X*X)^&#91;N/2&#93;;

  其中&#91;N&#93;是指小於或等於N的最大整數。

  C++實現功能函數:

  "

  unsigned int PowerMod(unsigned int x, unsigned int n, unsigned int p)

  {

  if (n == 0)

  {

  return 1;

  }

  unsigned int temp = PowerMod((x * x)%p, n/2, p); //遞歸計算(X*X)^&#91;N/2&#93;

  if ((n & 1) != 0) //判斷n的奇偶性

  {

  temp = (temp * x) % p;

  }

  return temp;

  }

  5.《孫子問題(中國剩餘定理)》

  在中國古代算書《孫子算經》中有這樣一個問題:

  「今有物不知其數,三三數之剩二,五五數之剩三,七七數之剩二,問物幾何?」意思是,「一個數除以3餘2,除以5餘3,除以7餘2.求適合這個條件的最小數。」

  這個問題稱為「孫子問題」.關於孫子問題的一般解法,國際上稱為「中國剩餘定理」.

  中國古代學者早就研究過這個問題。例如中國明朝數學家程大位在他著的《演算法統宗》(1593年)中就用四句很通俗的口訣暗示了此題的解法:

  三人同行七十稀,五樹梅花甘一枝,七子團圓正半月,除百零五便得知。

  "正半月"暗指15。"除百零五"的原意是,當所得的數比105大時,就105、105地往下減,使之小於105;這相當於用105去除,求出餘數。

  這四句口訣暗示的意思是:當除數分別是3、5、7時,用70乘以用3除的餘數,用21乘以用5除的餘數,用15乘以用7除的餘數,然後把這三個乘積相加。加得的結果如果比105大,就除以105,所得的餘數就是滿足題目要求的最小正整數解。

  根據剩餘定理,我把此種解法推廣到有n(n為自然數)個除數對應n個餘數,求最小被除數的情況。輸入n個除數(除數不能互相整除)和對應的餘數,計算機將輸出最小被除數。

  C++實現功能函數:

  "

  unsigned int ResidueTheorem(const unsigned int devisor&#91;&#93;, const unsigned int remainder&#91;&#93;, int length)

  {

  unsigned int product = 1; //所有除數之乘積

  for (int i=0; i<length; i++)//計算所有除數之乘積

  {

  product *= devisor&#91;i&#93;;

  }

  //公倍數數組,表示除該元素(除數)之外其他除數的公倍數

  unsigned int *commonMultiple = new unsigned int(length);

  for (int i=0; i<length; i++)//計算除該元素(除數)之外其他除數的公倍數

  {

  commonMultiple&#91;i&#93; = product / devisor&#91;i&#93;;

  }

  unsigned int dividend = 0; //被除數,就是函數要返回的值

  for (int i=0; i<length; i++)//計算被除數,但此時得到的不是最小被除數

  {

  unsigned int tempMul = commonMultiple&#91;i&#93;;

  //按照剩餘理論計算合適的公倍數,使得tempMul % devisor&#91;i&#93; == 1

  while (tempMul % devisor&#91;i&#93; != 1)

  {

  tempMul += commonMultiple&#91;i&#93;;

  }

  dividend += tempMul * remainder&#91;i&#93;; //用本除數得到的餘數乘以其他除數的公倍數

  }

  delete &#91;&#93;commonMultiple;

  return (dividend % product); //返回最小被除數

  }

  6. 凱撒密碼

  凱撒密碼(caeser)是羅馬擴張時期朱利斯o凱撒(Julius Caesar)創造的,用於加密通過信使傳遞的作戰命令。

  它將字母表中的字母移動一定位置而實現加密。注意26個字母循環使用,z的後面可以看成是a。

  例如,當密匙為k = 3,即向後移動3位時,若明文為」How are you!」,則密文為」Krz duh btx!」。

  凱撒密碼的加密演算法極其簡單。其加密過程如下:

  在這裡,我們做此約定:明文記為m,密文記為c,加密變換記為E(key1,m)(其中key1為密鑰),

  解密變換記為D(key2,m)(key2為解密密鑰)(在這裡key1=key2,不妨記為key)。

  凱撒密碼的加密過程可記為如下一個變換:c≡m+key (mod n) (其中n為基本字元個數)

  同樣,解密過程可表示為:m≡c+key (mod n) (其中n為基本字元個數)

  C++實現功能函數:

  "

  void Encrypt(const char proclaimedInWriting&#91;&#93;, char cryptograph&#91;&#93;, int key)

  {

  const int NUM = 26; //字母個數

  int len = strlen(proclaimedInWriting);

  for (int i=0; i<len; i++)

  {

  if (proclaimedInWriting&#91;i&#93; >= 'a' && proclaimedInWriting&#91;i&#93; <= 'z')

  {//明碼是大寫字母,則密碼也為大寫字母

  cryptograph&#91;i&#93; = (proclaimedInWriting&#91;i&#93; - 'a' + key) % NUM + 'a';

  }

  else if (proclaimedInWriting&#91;i&#93; >= 'A' && proclaimedInWriting&#91;i&#93; <= 'Z')

  {//明碼是小寫字母,則密碼也為小寫字母

  cryptograph&#91;i&#93; = (proclaimedInWriting&#91;i&#93; - 'A' + key) % NUM + 'A';

  }

  else

  {//明碼不是字母,則密碼與明碼相同

  cryptograph&#91;i&#93; = proclaimedInWriting&#91;i&#93;;

  }

  }

  cryptograph&#91;len&#93; = '\0';

  }

  "

  void Decode(const char cryptograph&#91;&#93;, char proclaimedInWriting&#91;&#93;, int key)

  {

  const int NUM = 26; //字母個數

  int len = strlen(cryptograph);

  for (int i=0; i<len; i++)

  {

  if (cryptograph&#91;i&#93; >= 'a' && cryptograph&#91;i&#93; <= 'z')

  {//密碼是大寫字母,則明碼也為大寫字母,為防止出現負數,轉換時要加個NUM

  proclaimedInWriting&#91;i&#93; = (cryptograph&#91;i&#93; - 'a' - key + NUM) % NUM + 'a';

  }

  else if (cryptograph&#91;i&#93; >= 'A' && cryptograph&#91;i&#93; <= 'Z')

  {//密碼是小寫字母,則明碼也為小寫字母

  proclaimedInWriting&#91;i&#93; = (cryptograph&#91;i&#93; - 'A' - key + NUM) % NUM + 'A';

  }

  else

  {//密碼不是字母,則明碼與明密相同

  proclaimedInWriting&#91;i&#93; = cryptograph&#91;i&#93;;

  }

  }

  proclaimedInWriting&#91;len&#93; = '\0';

  }

  模運算及其簡單應用就先講到這了,其實模運算在數學及計算機領域的應用非常廣泛,我這這裡搜集整理了一些最最基本的情形,希望能夠起到一個拋磚引玉的作用,讓更多的人關注模運算,並及其應用到更廣闊的領域中。

上一篇[格利雅]    下一篇 [氫化鎂]

相關評論

同義詞:暫無同義詞