1. 程式人生 > >負數取模運算

負數取模運算

最近在學習運算子時,遇到了負數取模(求餘數)的問題。對於正數取模很簡單,但複數取模時,不同的計算器卻有不同的答案。在網上看了一篇文件感覺總結的很詳盡和大家共享

源地址:https://ceeji.net/blog/mod-in-real/

背景

最近在一道 Java 習題中,看到這樣的一道題:

What is the output when this statement executed:System.out.printf(-7 % 3);

正整數的取餘運算大家都很熟悉,但是對於負數、實數的取餘運算,確實給人很新鮮的感覺。於是我對此進行了一些探索。我發現,這裡面還是頗有一點可以探索的東西的。

探究

首先,看看自然數的取模運算(定義1

):

如果ad是兩個自然數,d非零,可以證明存在兩個唯一的整數 q 和 r,滿足 aqdr 且0 ≤ rd。其中,q 被稱為商,r 被稱為餘數。

那麼對於負數,是否可以沿用這樣的定義呢?我們發現,假如我們按照正數求餘的規則求 (-7) mod 3 的結果,就可以表示 -7 為 (-3)* 3 +2。其中,2是餘數,-3是商。

那麼,各種程式語言和計算器是否是按照這樣理解的呢?下面是幾種軟體中對此的理解。

語言 語句 輸出
C++(G++ 編譯) cout << (-7) % 3; -1
Java(1.6) System.out.println((-7) % 3); -1
Python 2.6 (-7) % 3 2
百度計算器 (-7) mod 3 2

可以看到,結果特別有意思。這個問題是百家爭鳴的。看來我們不能直接把正數的法則加在負數上。實際上,在整數範圍內,自然數的求餘法則並不被很多人所接受,大家大多認可的是下面的這個定義2

如果ad整數d 非零,那麼餘數r 滿足這樣的關係:

a = qd + r , q 為整數,且0 ≤ |r| < |d|。

可以看到,這個定義導致了有負數的求餘並不是我們想象的那麼簡單,比如,-1 和 2 都是 (-7) mod 3 正確的結果,因為這兩個數都符合定義。這種情況下,對於取模運算,可能有兩個數都可以符合要求。我們把 -1 和 2 分別叫做正餘數

負餘數。通常,當除以d 時,如果正餘數為r1,負餘數為r2,那麼有

r1 = r2 + d

對負數餘數不明確的定義可能導致嚴重的計算問題,對於處理關鍵任務的系統,錯誤的選擇會導致嚴重的後果。

看完了 (-7) mod 3,下面我們來看一看 7 mod (-3) 的情況(看清楚,前面是 7 帶負號,現在是 3 帶負號)。根據定義2,7 = (-3) * (-2) + 1 或7 = (-3) * (-3) -2,所以餘數為 1 或 -2。

語言 語句 輸出
C++(G++ 編譯) cout << 7 % (-3); 1
Java(1.6) System.out.println(7 % (-3)); 1
Python 2.6 7 % (-3) -2
百度計算器 7 mod (-3) -2

從中我們看到幾個很有意思的現象:

  • Java 緊隨 C++ 的步伐,而 Python、Google、百度步調一致。難道真是物以類聚?聯想一下,Google 一直支援 Python,Python 也頗有 Web 特色的感覺,而且 Google Application Engine 也用的 Python,國內的搜尋引擎也不約而同地按照 Google 的定義進行運算。
  • 可以推斷,C++ 和 Java 通常會儘量讓商更大一些。比如在 (-7) mod 3中,他們以 -2 為商,餘數為 -1。在 Python 和 Google 計算器中,儘量讓商更小,所以以 -3 為商。在 7 mod (-3) 中效果相同:C++ 選擇了 3 作為商,Python 選擇了 2 作為商。但是在正整數運算中,所有語言和計算器都遵循了儘量讓商小的原則,因此 7 mod 3 結果為 1 不存在爭議,不會有人說它的餘數是-2。
  • 如果按照第二點的推斷,我們測試一下 (-7) mod (-3),結果應該是前一組語言(C++,Java)返回 2,後一組返回 -1。(請注意這只是假設)

於是我做了實際測試:

語言 語句 輸出
C++(G++ 編譯) cout << -7 % (-3); -1
Java(1.6) System.out.println(-7 % (-3)); -1
Python 2.6 -7 % (-3) -1
百度計算器 -7 mod (-3) -1

結果讓人大跌眼鏡,所有語言和計算機返回結果完全一致。

總結

我們由此可以總結出下面兩個結論:

  1. 對於任何同號的兩個整數,其取餘結果沒有爭議,所有語言的運算原則都是使商儘可能小
  2. 對於異號的兩個整數,C++/Java語言的原則是使商儘可能大,很多新型語言和網頁計算器的原則是使商儘可能小。

拓展

最後是拓展時間。對於實數,我們也可以定義取模運算(定義3)。

當 a 和 d 是實數,且d 非零, a 除以 d 會得到另一個實數(商),沒有所謂的剩餘的數。但如果要求商為一個整數,則餘數的概念還是有必要的。可以證明:存在唯一的整數商 q 和唯一的實數 r 使得: a = qd + r, 0 ≤ r < |d|. (轉自維基百科)

如上在實數範圍內擴充套件餘數的定義在數學理論中並不重要,儘管如此,很多程式語言都實現了這個定義。至於哪些程式語言實現了這個定義,就留給大家自己探究吧!


相關推薦

詳解負數運算

有人如果在python上使用%運算,肯定會遇到這樣的問題,就是它在負數上的結果和我們之前在C或JAVA上的結果不一樣。比如: -6 % 5這個運算,在python中的結果是4,但是在C/JAVA上的結果是-1 這是為什麼呢?wiki百科的解釋很好,英文好的可以

負數運算

最近在學習運算子時,遇到了負數取模(求餘數)的問題。對於正數取模很簡單,但複數取模時,不同的計算器卻有不同的答案。在網上看了一篇文件感覺總結的很詳盡和大家共享 源地址:https://ceeji.net/blog/mod-in-real/ 背景 最近在一道 Java 習題中

The mod(%) operation 負數運算詳解

Please indicate the source if you want to reprint: http://blog.csdn.net/gaoxiangnumber1 If a and d a

java中對負數運算

例: System.out.println(-2%5);//-2 System.out.println(2%-5);//2 System.out.println(-2%-5);//-2 Syst

負數運算問題

今天做一個筆試題,遇到了負數取模運算,做一個總結。 我們平常遇到的除法取餘,很少涉及負數。 那麼對於負數取餘,一共只有三種情況。 12%(-5)=2  因為商是-2,所以餘數是 2 (-12)%(-5)=-2 因為商是2,所以餘數是 -2 (-12)%5 = -2 因為商

分數的乘法逆元和負數運算

好的 分數 多少 研究 法則 表達 求余 推導 模運算 1.乘法逆元 A.定義 如果ax≡1 (mod p),且gcd(a,p)=1(a與p互質),則稱a關於模p的乘法逆元為x。 既然有ax≡1 (mod p),那麽有ax - py = 1,x是a關於模p的乘法逆元

負數運算

我們知道,在不同的語言中,對負數執行取模運算,結果有可能會是不同的。例如,(-11)%5在python中計算的結果是4,而在C(C99)中計算的結果則是-1。 truncate除法 && floor除法  在大多數程式語言中,如果整數a不是整數b的整數倍

運算

add 結合 重要 nbsp left 但是 list padding 四則運算 腦子不好使,老是記不住(?_?),備忘一下。 模運算與基本四則運算有些相似,但是除法例外。其規則如下: (a + b) % p = (a % p + b % p) % p (a -

Hdu 1395 2^x mod n = 1 運算

Problem Description Give a number n, find the minimum x(x>0) that satisfies 2^x mod n = 1.     Input One positive integer on ea

運算運算

對於整型數a,b來說,取模運算或者求餘運算的方法都是: 1.求整數商: c = a/b; 2.計算模或者餘數: r = a - c*b.   求模運算和求餘運算在第一步不同: 取餘運算在取c的值時,向0方向舍入(fix()函式);而取模運算在計算c的值時,向-∞方向舍入(f

給定A, B兩個整數,不使用除法和運算,求A/B的商和餘數

第一種辦法: 從小到大遍歷 for(i = 2 to A - 1) if(i * B > A) 商 = i- 1, 餘 = A - (i -1) * B 第二種辦法 二分法,在[2, A]中查詢滿足的解 第三種辦法 以除數為初始測試值,以2的指數

關於運算(mod)和求餘(rem)運算

通常情況下取模運算(mod)和求餘(rem)運算被混為一談,因為在大多數的程式語言裡,都用’%’符號表示取模或者求餘運算。在這裡要提醒大家要十分注意當前環境下’%’運算子的具體意義,因為在有負數存在的情況下,兩者的結果是不一樣的。 對於整型數a,b來說,取模運算或者求餘運算的方法都是: 

java 中負數

正數取模運算相信很多人都很熟練,但是有時候會涉及到負數的取模,比如說面試。今天就來看看負數參與的取模應該怎麼整。 2%(-5)結果應該是什麼呢?  5%(-2)結果又應該是什麼呢?  結論一:正數取模負數的結果和正數取模這個負數的絕對值的結果完全一樣。

【效能優化】運算:x%n,當n是偶數時,可以用x&(n-1)替代

#include <assert.h> void modulo_operation_opt() { int m = 100000; int n = 100000; double a

大數運算,快速冪運算

1.快速冪取模 快速冪取模就是在O(logn)內求出a^n mod b的值。演算法的原理是ab mod c=(a mod c)(b mod c)mod c  long exp_mod(long a,long n,long b) { long t; if

Leetcode 29. Divide Two Integers--兩個32位整數相除,小數位截斷,不能使用乘法、除法、運算

Given two integers dividend and divisor, divide two integers without using multiplication, division and mod operator. Return the qu

遞迴,斐波那契數及其運算

一、遞迴 1、遞迴:即函式自己呼叫自己,函式在呼叫時會進行引數例項化,開闢棧空間。 2、遞迴可簡化程式碼的編寫。易讀。 3、遞迴必須設定遞迴出口,否則會出現死迴圈 4、遞迴過程需一直開闢棧空間,執行速度慢,效率低。且存在棧溢位問題 5、相比較,迭代(非

運算總結

引入 程式設計競賽有相當一部分題目的結果過於龐大,整數型別無法儲存,往往只要求輸出取模的結果。 例如(a+b)%p,若a+b的結果我們儲存不了,再去取模,結果顯然不對,我們為了防止溢位,可以先分別對

對於負數(mod)問題

以前沒有太在意,以為取模(mod)運算只是在正整數範圍內進行的運算,現在才知道,取模運算可以在int、float、double範圍內進行運算。對於負數的取模問題,當然以前也是沒有注意過的,現在在我的本地用workshop

Java % ()運算

取模怎麼算     取模運算實際上是計算兩數相除以後的餘數。假設 q 是 a、b 相除產生的商(quotient),r 是相應的餘數(remainder),那麼在幾乎所有的計算系統中,都滿足: a = b x q + r,其中 |r|<|a|。